import { document } from "./browser";
import { isFn, noop, options } from "./util";
var globalEvents = {};
export var eventPropHooks = {}; //用于在事件回调里对事件对象进行
export var eventHooks = {}; //用于在元素上绑定特定的事件
//根据onXXX得到其全小写的事件名, onClick --> click, onClickCapture --> click,
// onMouseMove --> mousemove
export var eventLowerCache = {
onClick: "click",
onChange: "change",
onWheel: "wheel"
};
/**
* 判定否为与事件相关
*
* @param {any} name
* @returns
*/
export function isEventName(name) {
return /^on[A-Z]/.test(name);
}
export var isTouch = "ontouchstart" in document;
export function dispatchEvent(e, type, end) {
//__type__ 在injectTapEventPlugin里用到
e = new SyntheticEvent(e);
if (type) {
e.type = type;
}
var bubble = e.type;
var hook = eventPropHooks[bubble];
if (hook && false === hook(e)) {
return;
}
var paths = collectPaths(e.target, end || document);
var captured = bubble + "capture";
options.async = true;
triggerEventFlow(paths, captured, e);
if (!e._stopPropagation) {
triggerEventFlow(paths.reverse(), bubble, e);
}
options.async = false;
options.flushBatchedUpdates();
}
function collectPaths(from, end) {
var paths = [];
do {
if (from === end) {
break;
}
var events = from.__events;
if (events) {
paths.push({ dom: from, events: events });
}
} while ((from = from.parentNode) && from.nodeType === 1);
// target --> parentNode --> body --> html
return paths;
}
function triggerEventFlow(paths, prop, e) {
for (var i = paths.length; i--;) {
var path = paths[i];
var fn = path.events[prop];
if (isFn(fn)) {
e.currentTarget = path.dom;
fn.call(path.dom, e);
if (e._stopPropagation) {
break;
}
}
}
}
export function addGlobalEvent(name) {
if (!globalEvents[name]) {
globalEvents[name] = true;
addEvent(document, name, dispatchEvent);
}
}
export function addEvent(el, type, fn, bool) {
if (el.addEventListener) {
// Unable to preventDefault inside passive event listener due to target being
// treated as passive
el.addEventListener(
type,
fn,
bool || false
);
} else if (el.attachEvent) {
el.attachEvent("on" + type, fn);
}
}
var rcapture = /Capture$/;
export function getBrowserName(onStr) {
var lower = eventLowerCache[onStr];
if (lower) {
return lower;
}
var camel = onStr.slice(2).replace(rcapture, "");
lower = camel.toLowerCase();
eventLowerCache[onStr] = lower;
return lower;
}
eventPropHooks.click = function (e) {
return !e.target.disabled;
};
/* IE6-11 chrome mousewheel wheelDetla 下 -120 上 120
firefox DOMMouseScroll detail 下3 上-3
firefox wheel detlaY 下3 上-3
IE9-11 wheel deltaY 下40 上-40
chrome wheel deltaY 下100 上-100 */
/* istanbul ignore next */
const fixWheelType =
"onmousewheel" in document
? "mousewheel"
: document.onwheel !== void 666 ? "wheel" : "DOMMouseScroll";
const fixWheelDelta =
fixWheelType === "mousewheel"
? "wheelDetla"
: fixWheelType === "wheel" ? "deltaY" : "detail";
eventHooks.wheel = function (dom) {
addEvent(dom, fixWheelType, function (e) {
var delta = e[fixWheelDelta] > 0 ? -120 : 120;
var deltaY = ~~dom.__wheel + delta;
dom.__wheel = deltaY;
e = new SyntheticEvent(e);
e.type = "wheel";
e.deltaY = deltaY;
dispatchEvent(e);
});
};
var fixFocus = {};
"blur,focus".replace(/\w+/g, function (type) {
eventHooks[type] = function () {
if (!fixFocus[type]) {
fixFocus[type] = true;
addEvent(
document,
type,
dispatchEvent,
true
);
}
};
});
/**
*
DOM通过event对象的relatedTarget属性提供了相关元素的信息。这个属性只对于mouseover和mouseout事件才包含值;
对于其他事件,这个属性的值是null。IE不支持realtedTarget属性,但提供了保存着同样信息的不同属性。
在mouseover事件触发时,IE的fromElement属性中保存了相关元素;
在mouseout事件出发时,IE的toElement属性中保存着相关元素。
但fromElement与toElement可能同时都有值
*/
function getRelatedTarget(e) {
if (!e.timeStamp) {
e.relatedTarget = e.type === "mouseover" ? e.fromElement : e.toElement;
}
return e.relatedTarget;
}
function contains(a, b) {
if (b) {
while ((b = b.parentNode)) {
if (b === a) {
return true;
}
}
}
return false;
}
String("mouseenter,mouseleave").replace(/\w+/g, function (type) {
eventHooks[type] = function (dom, name) {
var mark = "__" + name;
if (!dom[mark]) {
dom[mark] = true;
var mask = name === "mouseenter" ? "mouseover" : "mouseout";
addEvent(dom, mask, function (e) {
let t = getRelatedTarget(e);
if (!t || (t !== dom && !contains(dom, t))) {
var common = getLowestCommonAncestor(dom, t);
//由于不冒泡,因此paths长度为1
dispatchEvent(e, name, common);
}
});
}
};
});
function getLowestCommonAncestor(instA, instB) {
var depthA = 0;
for (var tempA = instA; tempA; tempA = tempA.parentNode) {
depthA++;
}
var depthB = 0;
for (var tempB = instB; tempB; tempB = tempB.parentNode) {
depthB++;
}
// If A is deeper, crawl up.
while (depthA - depthB > 0) {
instA = instA.parentNode;
depthA--;
}
// If B is deeper, crawl up.
while (depthB - depthA > 0) {
instB = instB.parentNode;
depthB--;
}
// Walk in lockstep until we find a match.
var depth = depthA;
while (depth--) {
if (instA === instB) {
return instA;
}
instA = instA.parentNode;
instB = instB.parentNode;
}
return null;
}
if (isTouch) {
eventHooks.click = noop;
eventHooks.clickcapture = noop;
}
export function createHandle(name, fn) {
return function (e) {
if (fn && fn(e) === false) {
return;
}
dispatchEvent(e, name);
};
}
var changeHandle = createHandle("change");
var doubleClickHandle = createHandle("doubleclick");
//react将text,textarea,password元素中的onChange事件当成onInput事件
eventHooks.changecapture = eventHooks.change = function (dom) {
var mask = /text|password/.test(dom.type) ? "input" : "change";
addEvent(document, mask, changeHandle);
};
eventHooks.doubleclick = eventHooks.doubleclickcapture = function () {
addEvent(document, "dblclick", doubleClickHandle);
};
export function SyntheticEvent(event) {
if (event.nativeEvent) {
return event;
}
for (var i in event) {
if (!eventProto[i]) {
this[i] = event[i];
}
}
if (!this.target) {
this.target = event.srcElement;
}
this.fixEvent();
this.timeStamp = new Date() - 0;
this.nativeEvent = event;
}
var eventProto = (SyntheticEvent.prototype = {
fixEvent: function () { }, //留给以后扩展用
preventDefault: function () {
var e = this.nativeEvent || {};
e.returnValue = this.returnValue = false;
if (e.preventDefault) {
e.preventDefault();
}
},
fixHooks: function () { },
stopPropagation: function () {
var e = this.nativeEvent || {};
e.cancelBubble = this._stopPropagation = true;
if (e.stopPropagation) {
e.stopPropagation();
}
},
persist: noop,
stopImmediatePropagation: function () {
this.stopPropagation();
this.stopImmediate = true;
},
toString: function () {
return "[object Event]";
}
});
/* istanbul ignore next */
//freeze_start
Object.freeze ||
(Object.freeze = function (a) {
return a;
});
//freeze_end