| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290 |
- /* General Utils
- ---------------------------------------------------------------------------------------------------------------------- */
- $.simulateByPoint = function(type, options) {
- var docEl = $(document)
- var point = options.point
- var clientX, clientY
- var node
- if (point) {
- clientX = point.left - docEl.scrollLeft()
- clientY = point.top - docEl.scrollTop()
- node = document.elementFromPoint(clientX, clientY)
- $(node).simulate(type, options)
- }
- }
- /* Touch
- ---------------------------------------------------------------------------------------------------------------------- */
- var origSimulateEvent = $.simulate.prototype.simulateEvent
- var touchUID = Date.now()
- $.simulate.prototype.simulateEvent = function(elem, type, options) {
- if (/^touch/.test(type)) {
- return this.simulateTouchEvent(elem, type, options)
- } else {
- return origSimulateEvent.apply(this, arguments)
- }
- }
- $.simulate.prototype.simulateTouchEvent = function(elem, type, options) {
- // http://stackoverflow.com/a/29019278/96342
- var event = document.createEvent('Event')
- event.initEvent(type, true, true) // cancelable, bubbleable
- event.touches = [{
- target: elem,
- identifier: touchUID++,
- pageX: options.clientX,
- pageY: options.clientY,
- screenX: options.clientX,
- screenY: options.clientY,
- clientX: options.clientX,
- clientY: options.clientY
- }]
- this.dispatchEvent(elem, type, event, options)
- }
- $.simulateMouseClick = function(elem) {
- var $elem = $(elem)
- var clientCoords = {
- clientX: $elem.offset().left + $elem.outerWidth() / 2,
- clientY: $elem.offset().top + $elem.outerHeight() / 2
- }
- $elem.simulate('mousemove', clientCoords)
- $elem.simulate('mousedown', clientCoords)
- $elem.simulate('mouseup', clientCoords)
- $elem.simulate('click', clientCoords)
- }
- $.simulateTouchClick = function(elem) {
- var $elem = $(elem)
- var clientCoords = {
- clientX: $elem.offset().left + $elem.outerWidth() / 2,
- clientY: $elem.offset().top + $elem.outerHeight() / 2
- }
- $elem.simulate('touchstart', clientCoords)
- $elem.simulate('touchend', clientCoords)
- $elem.simulate('mousemove', clientCoords)
- $elem.simulate('mousedown', clientCoords)
- $elem.simulate('mouseup', clientCoords)
- $elem.simulate('click', clientCoords)
- }
- /* Drag-n-drop
- ---------------------------------------------------------------------------------------------------------------------- */
- var DEBUG_DELAY = 500
- var DEBUG_MIN_DURATION = 2000
- var DEBUG_MIN_MOVES = 100
- var DRAG_DEFAULTS = {
- point: null, // the start point
- localPoint: { left: '50%', top: '50%' },
- end: null, // can be a point or an el
- localEndPoint: { left: '50%', top: '50%' },
- dx: 0,
- dy: 0,
- moves: 5,
- duration: 100 // ms
- }
- var dragStackCnt = 0
- $.simulate.prototype.simulateDrag = function() {
- var targetNode = this.target
- var targetEl = $(targetNode)
- var options = $.extend({}, DRAG_DEFAULTS, this.options)
- var dx = options.dx
- var dy = options.dy
- var duration = options.duration
- var moves = options.moves
- var startPoint
- var endEl
- var endPoint
- var localPoint
- var offset
- // compute start point
- if (options.point) {
- startPoint = options.point
- } else {
- localPoint = normalizeElPoint(options.localPoint, targetEl)
- offset = targetEl.offset()
- startPoint = {
- left: offset.left + localPoint.left,
- top: offset.top + localPoint.top
- }
- }
- // compute end point
- if (options.end) {
- if (isPoint(options.end)) {
- endPoint = options.end
- } else { // assume options.end is an element
- endEl = $(options.end)
- localPoint = normalizeElPoint(options.localEndPoint, endEl)
- offset = endEl.offset()
- endPoint = {
- left: offset.left + localPoint.left,
- top: offset.top + localPoint.top
- }
- }
- }
- if (endPoint) {
- dx = endPoint.left - startPoint.left
- dy = endPoint.top - startPoint.top
- }
- moves = Math.max(moves, options.debug ? DEBUG_MIN_MOVES : 1)
- duration = Math.max(duration, options.debug ? DEBUG_MIN_DURATION : 10)
- simulateDrag(
- this,
- targetNode,
- startPoint,
- dx,
- dy,
- moves,
- duration,
- options
- )
- }
- function simulateDrag(self, targetNode, startPoint, dx, dy, moveCnt, duration, options) {
- var debug = options.debug
- var isTouch = options.isTouch
- var docNode = targetNode.ownerDocument
- var docEl = $(docNode)
- var waitTime = duration / moveCnt
- var moveIndex = 0
- var clientCoords
- var intervalId
- var dotEl
- var dragId
- if (debug) {
- dotEl = $('<div>')
- .css({
- position: 'absolute',
- zIndex: 99999,
- border: '5px solid red',
- borderRadius: '5px',
- margin: '-5px 0 0 -5px'
- })
- .appendTo('body')
- }
- function updateCoords() {
- var progress = moveIndex / moveCnt
- var left = startPoint.left + dx * progress
- var top = startPoint.top + dy * progress
- clientCoords = {
- clientX: left - docEl.scrollLeft(),
- clientY: top - docEl.scrollTop()
- }
- if (debug) {
- dotEl.css({ left: left, top: top })
- }
- }
- function startDrag() {
- updateCoords()
- dragId = ++dragStackCnt
- // simulate a drag-start only if another drag isn't already happening
- if (dragStackCnt === 1) {
- self.simulateEvent(targetNode, isTouch ? 'touchstart' : 'mousedown', clientCoords)
- }
- var delay = options.delay || 0
- if (debug) {
- delay = Math.max(delay, DEBUG_DELAY)
- }
- if (delay) {
- setTimeout(function() {
- startMoving()
- }, delay)
- } else {
- startMoving()
- }
- }
- function startMoving() {
- intervalId = setInterval(tick, waitTime)
- }
- function tick() { // called one interval after start
- moveIndex++
- updateCoords() // update clientCoords before mousemove
- self.simulateEvent(docNode, isTouch ? 'touchmove' : 'mousemove', clientCoords)
- if (moveIndex >= moveCnt) {
- stopMoving()
- }
- }
- function stopMoving() {
- clearInterval(intervalId)
- if (debug) {
- setTimeout(function() {
- dotEl.remove() // do this before calling stopDrag/callback. don't want dot picked up by elementFromPoint
- stopDrag()
- }, DEBUG_DELAY)
- } else {
- stopDrag()
- }
- }
- function stopDrag() { // progress at 1, coords already up to date at this point
- (options.onBeforeRelease || function() {})()
- // only simulate a drop if the current drag is still the active one.
- // otherwise, this means another drag has begun via onBeforeRelease.
- if (dragId === dragStackCnt) {
- if ($.contains(docNode, targetNode)) {
- self.simulateEvent(targetNode, isTouch ? 'touchend' : 'mouseup', clientCoords)
- self.simulateEvent(targetNode, 'click', clientCoords)
- } else {
- self.simulateEvent(docNode, isTouch ? 'touchend' : 'mouseup', clientCoords)
- }
- }
- dragStackCnt--;
- (options.onRelease || options.callback || function() {})() // TODO: deprecate "callback" ?
- }
- startDrag()
- }
- function normalizeElPoint(point, el) {
- var left = point.left
- var top = point.top
- if (/%$/.test(left)) {
- left = parseInt(left) / 100 * el.outerWidth()
- }
- if (/%$/.test(top)) {
- top = parseInt(top) / 100 * el.outerHeight()
- }
- return { left: left, top: top }
- }
- function isPoint(input) {
- return typeof input === 'object' && // `in` operator only works on objects
- 'left' in input && 'top' in input
- }
|