/* eslint complexity: 0 */
|
|
/**
|
|
* @fileoverview Utility module for handling DOM events.
|
|
* @author NHN FE Development Lab <dl_javascript@nhn.com>
|
|
*/
|
|
'use strict';
|
|
|
|
var util = require('tui-code-snippet');
|
|
var eventKey = '_evt',
|
|
DRAG_START = ['touchstart', 'mousedown'];
|
|
|
|
var domevent = {
|
|
/**
|
|
* Bind dom events.
|
|
* @param {HTMLElement} obj HTMLElement to bind events.
|
|
* @param {(string|object)} types Space splitted events names or eventName:handler object.
|
|
* @param {*} fn handler function or context for handler method.
|
|
* @param {*} [context] context object for handler method.
|
|
*/
|
|
on: function(obj, types, fn, context) {
|
|
if (util.isString(types)) {
|
|
util.forEach(types.split(' '), function(type) {
|
|
domevent._on(obj, type, fn, context);
|
|
});
|
|
|
|
return;
|
|
}
|
|
|
|
util.forEachOwnProperties(types, function(handler, type) {
|
|
domevent._on(obj, type, handler, fn);
|
|
});
|
|
},
|
|
|
|
/**
|
|
* DOM event binding.
|
|
* @param {HTMLElement} obj HTMLElement to bind events.
|
|
* @param {String} type The name of events.
|
|
* @param {*} fn handler function
|
|
* @param {*} [context] context object for handler method.
|
|
* @private
|
|
*/
|
|
_on: function(obj, type, fn, context) {
|
|
var id,
|
|
handler,
|
|
originHandler;
|
|
|
|
id = type + util.stamp(fn) + (context ? '_' + util.stamp(context) : '');
|
|
|
|
if (obj[eventKey] && obj[eventKey][id]) {
|
|
return;
|
|
}
|
|
|
|
handler = function(e) {
|
|
fn.call(context || obj, e || window.event);
|
|
};
|
|
|
|
originHandler = handler;
|
|
|
|
if ('addEventListener' in obj) {
|
|
if (type === 'mouseenter' || type === 'mouseleave') {
|
|
handler = function(e) {
|
|
e = e || window.event;
|
|
if (!domevent._checkMouse(obj, e)) {
|
|
return;
|
|
}
|
|
originHandler(e);
|
|
};
|
|
obj.addEventListener((type === 'mouseenter') ?
|
|
'mouseover' : 'mouseout', handler, false);
|
|
} else {
|
|
if (type === 'mousewheel') {
|
|
obj.addEventListener('DOMMouseScroll', handler, false);
|
|
}
|
|
|
|
obj.addEventListener(type, handler, false);
|
|
}
|
|
} else if ('attachEvent' in obj) {
|
|
obj.attachEvent('on' + type, handler);
|
|
}
|
|
|
|
obj[eventKey] = obj[eventKey] || {};
|
|
obj[eventKey][id] = handler;
|
|
},
|
|
|
|
/**
|
|
* Unbind DOM Event handler.
|
|
* @param {HTMLElement} obj HTMLElement to unbind.
|
|
* @param {(string|object)} types Space splitted events names or eventName:handler object.
|
|
* @param {*} fn handler function or context for handler method.
|
|
* @param {*} [context] context object for handler method.
|
|
*/
|
|
off: function(obj, types, fn, context) {
|
|
if (util.isString(types)) {
|
|
util.forEach(types.split(' '), function(type) {
|
|
domevent._off(obj, type, fn, context);
|
|
});
|
|
|
|
return;
|
|
}
|
|
|
|
util.forEachOwnProperties(types, function(handler, type) {
|
|
domevent._off(obj, type, handler, fn);
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Unbind DOM event handler.
|
|
* @param {HTMLElement} obj HTMLElement to unbind.
|
|
* @param {String} type The name of event to unbind.
|
|
* @param {function()} fn Event handler that supplied when binding.
|
|
* @param {*} context context object that supplied when binding.
|
|
* @private
|
|
*/
|
|
_off: function(obj, type, fn, context) {
|
|
var id = type + util.stamp(fn) + (context ? '_' + util.stamp(context) : ''),
|
|
handler = obj[eventKey] && obj[eventKey][id];
|
|
|
|
if (!handler) {
|
|
return;
|
|
}
|
|
|
|
if ('removeEventListener' in obj) {
|
|
if (type === 'mouseenter' || type === 'mouseleave') {
|
|
obj.removeEventListener((type === 'mouseenter') ?
|
|
'mouseover' : 'mouseout', handler, false);
|
|
} else {
|
|
if (type === 'mousewheel') {
|
|
obj.removeEventListener('DOMMouseScroll', handler, false);
|
|
}
|
|
|
|
obj.removeEventListener(type, handler, false);
|
|
}
|
|
} else if ('detachEvent' in obj) {
|
|
try {
|
|
obj.detachEvent('on' + type, handler);
|
|
} catch (e) {} //eslint-disable-line
|
|
}
|
|
|
|
delete obj[eventKey][id];
|
|
|
|
if (util.keys(obj[eventKey]).length) {
|
|
return;
|
|
}
|
|
|
|
delete obj[eventKey];
|
|
},
|
|
|
|
/**
|
|
* Bind DOM event. this event will unbind after invokes.
|
|
* @param {HTMLElement} obj HTMLElement to bind events.
|
|
* @param {(string|object)} types Space splitted events names or eventName:handler object.
|
|
* @param {*} fn handler function or context for handler method.
|
|
* @param {*} [context] context object for handler method.
|
|
*/
|
|
once: function(obj, types, fn, context) {
|
|
var self = this;
|
|
|
|
if (util.isObject(types)) {
|
|
util.forEachOwnProperties(types, function(handler, type) {
|
|
domevent.once(obj, type, handler, fn);
|
|
});
|
|
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* Handler for temporary usage for once implementation
|
|
*/
|
|
function onceHandler() {
|
|
fn.apply(context || obj, arguments);
|
|
self._off(obj, types, onceHandler, context);
|
|
}
|
|
|
|
domevent.on(obj, types, onceHandler, context);
|
|
},
|
|
|
|
/**
|
|
* Cancel event bubbling.
|
|
* @param {Event} e Event object.
|
|
*/
|
|
stopPropagation: function(e) {
|
|
if (e.stopPropagation) {
|
|
e.stopPropagation();
|
|
} else {
|
|
e.cancelBubble = true;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Cancel browser default actions.
|
|
* @param {Event} e Event object.
|
|
*/
|
|
preventDefault: function(e) {
|
|
if (e.preventDefault) {
|
|
e.preventDefault();
|
|
} else {
|
|
e.returnValue = false;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Syntatic sugar of stopPropagation and preventDefault
|
|
* @param {Event} e Event object.
|
|
*/
|
|
stop: function(e) {
|
|
domevent.preventDefault(e);
|
|
domevent.stopPropagation(e);
|
|
},
|
|
|
|
/**
|
|
* Stop scroll events.
|
|
* @param {HTMLElement} el HTML element to prevent scroll.
|
|
*/
|
|
disableScrollPropagation: function(el) {
|
|
domevent.on(el, 'mousewheel MozMousePixelScroll', domevent.stopPropagation);
|
|
},
|
|
|
|
/**
|
|
* Stop all events related with click.
|
|
* @param {HTMLElement} el HTML element to prevent all event related with click.
|
|
*/
|
|
disableClickPropagation: function(el) {
|
|
domevent.on(el, DRAG_START.join(' ') + ' click dblclick', domevent.stopPropagation);
|
|
},
|
|
|
|
/**
|
|
* Get mouse position from mouse event.
|
|
*
|
|
* If supplied relatveElement parameter then return relative position based on element.
|
|
* @param {Event} mouseEvent Mouse event object
|
|
* @param {HTMLElement} relativeElement HTML element that calculate relative position.
|
|
* @returns {number[]} mouse position.
|
|
*/
|
|
getMousePosition: function(mouseEvent, relativeElement) {
|
|
var rect;
|
|
|
|
if (!relativeElement) {
|
|
return [mouseEvent.clientX, mouseEvent.clientY];
|
|
}
|
|
|
|
rect = relativeElement.getBoundingClientRect();
|
|
|
|
return [
|
|
mouseEvent.clientX - rect.left - relativeElement.clientLeft,
|
|
mouseEvent.clientY - rect.top - relativeElement.clientTop
|
|
];
|
|
},
|
|
|
|
/**
|
|
* Normalize mouse wheel event that different each browsers.
|
|
* @param {MouseEvent} e Mouse wheel event.
|
|
* @returns {Number} delta
|
|
*/
|
|
getWheelDelta: function(e) {
|
|
var delta = 0;
|
|
|
|
if (e.wheelDelta) {
|
|
delta = e.wheelDelta / 120;
|
|
}
|
|
|
|
if (e.detail) {
|
|
delta = -e.detail / 3;
|
|
}
|
|
|
|
return delta;
|
|
},
|
|
|
|
/**
|
|
* prevent firing mouseleave event when mouse entered child elements.
|
|
* @param {HTMLElement} el HTML element
|
|
* @param {MouseEvent} e Mouse event
|
|
* @returns {Boolean} leave?
|
|
* @private
|
|
*/
|
|
_checkMouse: function(el, e) {
|
|
var related = e.relatedTarget;
|
|
|
|
if (!related) {
|
|
return true;
|
|
}
|
|
|
|
try {
|
|
while (related && (related !== el)) {
|
|
related = related.parentNode;
|
|
}
|
|
} catch (err) {
|
|
return false;
|
|
}
|
|
|
|
return (related !== el);
|
|
},
|
|
|
|
/**
|
|
* Trigger specific events to html element.
|
|
* @param {HTMLElement} obj HTMLElement
|
|
* @param {string} type Event type name
|
|
* @param {object} [eventData] Event data
|
|
*/
|
|
trigger: function(obj, type, eventData) {
|
|
var rMouseEvent = /(mouse|click)/;
|
|
if (util.isUndefined(eventData) && rMouseEvent.exec(type)) {
|
|
eventData = domevent.mouseEvent(type);
|
|
}
|
|
|
|
if (obj.dispatchEvent) {
|
|
obj.dispatchEvent(eventData);
|
|
} else if (obj.fireEvent) {
|
|
obj.fireEvent('on' + type, eventData);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Create virtual mouse event.
|
|
*
|
|
* Tested at
|
|
*
|
|
* - IE7 ~ IE11
|
|
* - Chrome
|
|
* - Firefox
|
|
* - Safari
|
|
* @param {string} type Event type
|
|
* @param {object} [eventObj] Event data
|
|
* @returns {MouseEvent} Virtual mouse event.
|
|
*/
|
|
mouseEvent: function(type, eventObj) {
|
|
var evt,
|
|
e;
|
|
|
|
e = util.extend({
|
|
bubbles: true,
|
|
cancelable: (type !== 'mousemove'),
|
|
view: window,
|
|
wheelDelta: 0,
|
|
detail: 0,
|
|
screenX: 0,
|
|
screenY: 0,
|
|
clientX: 0,
|
|
clientY: 0,
|
|
ctrlKey: false,
|
|
altKey: false,
|
|
shiftKey: false,
|
|
metaKey: false,
|
|
button: 0,
|
|
relatedTarget: undefined // eslint-disable-line
|
|
}, eventObj);
|
|
|
|
if (typeof document.createEvent === 'function') {
|
|
evt = document.createEvent('MouseEvents');
|
|
evt.initMouseEvent(type,
|
|
e.bubbles, e.cancelable, e.view, e.detail,
|
|
e.screenX, e.screenY, e.clientX, e.clientY,
|
|
e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
|
|
e.button, document.body.parentNode
|
|
);
|
|
} else if (document.createEventObject) {
|
|
evt = document.createEventObject();
|
|
|
|
util.forEach(e, function(value, propName) {
|
|
evt[propName] = value;
|
|
}, this);
|
|
evt.button = {0: 1,
|
|
1: 4,
|
|
2: 2}[evt.button] || evt.button;
|
|
}
|
|
|
|
return evt;
|
|
},
|
|
|
|
/**
|
|
* Normalize mouse event's button attributes.
|
|
*
|
|
* Can detect which button is clicked by this method.
|
|
*
|
|
* Meaning of return numbers
|
|
*
|
|
* - 0: primary mouse button
|
|
* - 1: wheel button or center button
|
|
* - 2: secondary mouse button
|
|
* @param {MouseEvent} mouseEvent - The mouse event object want to know.
|
|
* @returns {number} - The value of meaning which button is clicked?
|
|
*/
|
|
getMouseButton: function(mouseEvent) {
|
|
var button,
|
|
primary = '0,1,3,5,7',
|
|
secondary = '2,6',
|
|
wheel = '4';
|
|
|
|
/* istanbul ignore else */
|
|
if (document.implementation.hasFeature('MouseEvents', '2.0')) {
|
|
return mouseEvent.button;
|
|
}
|
|
|
|
button = String(mouseEvent.button);
|
|
if (primary.indexOf(button) > -1) {
|
|
return 0;
|
|
}
|
|
if (secondary.indexOf(button) > -1) {
|
|
return 2;
|
|
}
|
|
if (~wheel.indexOf(button)) {
|
|
return 1;
|
|
}
|
|
|
|
return -1;
|
|
},
|
|
|
|
/**
|
|
* Get target from event object
|
|
*
|
|
* @param {Event} event - The event object
|
|
* @returns {object} - The event target object
|
|
*/
|
|
getEventTarget: function(event) {
|
|
return event.target || event.srcElement;
|
|
}
|
|
};
|
|
|
|
module.exports = domevent;
|