Comparing version 4.1.2 to 4.2.0
@@ -141,7 +141,11 @@ /** | ||
// <http://caniuse.com/#search=pointer%20event>. | ||
pointerEventsSupported: 'onpointerdown' in window // Firefox supports pointer but not by default, only MS browsers are reliable on pointer | ||
pointerEventsSupported: // (1) Firefox supports pointer but not by default, only MS browsers are reliable on pointer | ||
// events currently. So we dont use that on other browsers unless tested sufficiently. | ||
// Although IE 10 supports pointer event, it use old style and is different from the | ||
// For example, in iOS 13 Mobile Chromium 78, if the touching behavior starts page | ||
// scroll, the `pointermove` event can not be fired any more. That will break some | ||
// features like "pan horizontally to move something and pan vertically to page scroll". | ||
// The horizontal pan probably be interrupted by the casually triggered page scroll. | ||
// (2) Although IE 10 supports pointer event, it use old style and is different from the | ||
// standard. So we exclude that. (IE 10 is hardly used on touch device) | ||
&& (browser.edge || browser.ie && browser.version >= 11), | ||
'onpointerdown' in window && (browser.edge || browser.ie && browser.version >= 11), | ||
// passiveSupported: detectPassiveSupport() | ||
@@ -148,0 +152,0 @@ domSupported: typeof document !== 'undefined' |
@@ -153,2 +153,15 @@ var Eventful = require("../mixin/Eventful"); | ||
/** | ||
* Find native event compat for legency IE. | ||
* Should be called at the begining of a native event listener. | ||
* | ||
* @param {Event} [e] Mouse event or touch event or pointer event. | ||
* For lagency IE, we use `window.event` is used. | ||
* @return {Event} The native event. | ||
*/ | ||
function getNativeEvent(e) { | ||
return e || window.event; | ||
} | ||
/** | ||
* Normalize the coordinates of the input event. | ||
@@ -167,6 +180,6 @@ * | ||
* @param {HTMLElement} el DOM element. | ||
* @param {Event} [e] Mouse event or touch event. For lagency IE, | ||
* do not need to input it and `window.event` is used. | ||
* @param {Event} [e] See `getNativeEvent`. | ||
* @param {boolean} [calculate=false] Whether to force calculate | ||
* the coordinates but not use ones provided by browser. | ||
* @return {UIEvent} The normalized native UIEvent. | ||
*/ | ||
@@ -176,3 +189,3 @@ | ||
function normalizeEvent(el, e, calculate) { | ||
e = e || window.event; | ||
e = getNativeEvent(e); | ||
@@ -214,6 +227,9 @@ if (e.zrX != null) { | ||
* @param {Function} handler | ||
* @param {Object|boolean} opt If boolean, means `opt.capture` | ||
* @param {boolean} [opt.capture=false] | ||
* @param {boolean} [opt.passive=false] | ||
*/ | ||
function addEventListener(el, name, handler) { | ||
function addEventListener(el, name, handler, opt) { | ||
if (isDomLevel2) { | ||
@@ -240,11 +256,20 @@ // Reproduct the console warning: | ||
// el.addEventListener(name, handler /* , opts */); | ||
el.addEventListener(name, handler); | ||
el.addEventListener(name, handler, opt); | ||
} else { | ||
// For simplicity, do not implement `setCapture` for IE9-. | ||
el.attachEvent('on' + name, handler); | ||
} | ||
} | ||
/** | ||
* Parameter are the same as `addEventListener`. | ||
* | ||
* Notice that if a listener is registered twice, one with capture and one without, | ||
* remove each one separately. Removal of a capturing listener does not affect a | ||
* non-capturing version of the same listener, and vice versa. | ||
*/ | ||
function removeEventListener(el, name, handler) { | ||
function removeEventListener(el, name, handler, opt) { | ||
if (isDomLevel2) { | ||
el.removeEventListener(name, handler); | ||
el.removeEventListener(name, handler, opt); | ||
} else { | ||
@@ -295,2 +320,3 @@ el.detachEvent('on' + name, handler); | ||
exports.clientToLocal = clientToLocal; | ||
exports.getNativeEvent = getNativeEvent; | ||
exports.normalizeEvent = normalizeEvent; | ||
@@ -297,0 +323,0 @@ exports.addEventListener = addEventListener; |
@@ -6,2 +6,3 @@ var _event = require("../core/event"); | ||
var normalizeEvent = _event.normalizeEvent; | ||
var getNativeEvent = _event.getNativeEvent; | ||
@@ -14,19 +15,42 @@ var zrUtil = require("../core/util"); | ||
/* global document */ | ||
var TOUCH_CLICK_DELAY = 300; | ||
var mouseHandlerNames = ['click', 'dblclick', 'mousewheel', 'mouseout', 'mouseup', 'mousedown', 'mousemove', 'contextmenu']; | ||
var touchHandlerNames = ['touchstart', 'touchend', 'touchmove']; | ||
var pointerEventNames = { | ||
pointerdown: 1, | ||
pointerup: 1, | ||
pointermove: 1, | ||
pointerout: 1 | ||
var globalEventSupported = env.domSupported; | ||
var localNativeListenerNames = function () { | ||
var mouseHandlerNames = ['click', 'dblclick', 'mousewheel', 'mouseout', 'mouseup', 'mousedown', 'mousemove', 'contextmenu']; | ||
var touchHandlerNames = ['touchstart', 'touchend', 'touchmove']; | ||
var pointerEventNameMap = { | ||
pointerdown: 1, | ||
pointerup: 1, | ||
pointermove: 1, | ||
pointerout: 1 | ||
}; | ||
var pointerHandlerNames = zrUtil.map(mouseHandlerNames, function (name) { | ||
var nm = name.replace('mouse', 'pointer'); | ||
return pointerEventNameMap.hasOwnProperty(nm) ? nm : name; | ||
}); | ||
return { | ||
mouse: mouseHandlerNames, | ||
touch: touchHandlerNames, | ||
pointer: pointerHandlerNames | ||
}; | ||
}(); | ||
var globalNativeListenerNames = { | ||
mouse: ['mousemove', 'mouseup'], | ||
pointer: ['pointermove', 'pointerup'] | ||
}; | ||
var pointerHandlerNames = zrUtil.map(mouseHandlerNames, function (name) { | ||
var nm = name.replace('mouse', 'pointer'); | ||
return pointerEventNames[nm] ? nm : name; | ||
}); | ||
function eventNameFix(name) { | ||
return name === 'mousewheel' && env.browser.firefox ? 'DOMMouseScroll' : name; | ||
} // function onMSGestureChange(proxy, event) { | ||
} | ||
function isPointerFromTouch(event) { | ||
var pointerType = event.pointerType; | ||
return pointerType === 'pen' || pointerType === 'touch'; | ||
} // function useMSGuesture(handlerProxy, event) { | ||
// return isPointerFromTouch(event) && !!handlerProxy._msGesture; | ||
// } | ||
// function onMSGestureChange(proxy, event) { | ||
// if (event.translationX || event.translationY) { | ||
@@ -50,78 +74,135 @@ // // mousemove is carried by MSGesture to reduce the sensitivity. | ||
* Result: Blocking Mouse Events for 700ms. | ||
* | ||
* @param {DOMHandlerScope} scope | ||
*/ | ||
function setTouchTimer(instance) { | ||
instance._touching = true; | ||
clearTimeout(instance._touchTimer); | ||
instance._touchTimer = setTimeout(function () { | ||
instance._touching = false; | ||
function setTouchTimer(scope) { | ||
scope.touching = true; | ||
if (scope.touchTimer != null) { | ||
clearTimeout(scope.touchTimer); | ||
scope.touchTimer = null; | ||
} | ||
scope.touchTimer = setTimeout(function () { | ||
scope.touching = false; | ||
scope.touchTimer = null; | ||
}, 700); | ||
} // Mark touch, which is useful in distinguish touch and | ||
// mouse event in upper applicatoin. | ||
function markTouch(event) { | ||
event && (event.zrByTouch = true); | ||
} // function markTriggeredFromLocal(event) { | ||
// event && (event.__zrIsFromLocal = true); | ||
// } | ||
// function isTriggeredFromLocal(instance, event) { | ||
// return !!(event && event.__zrIsFromLocal); | ||
// } | ||
function normalizeGlobalEvent(instance, event) { | ||
// offsetX, offsetY still need to be calculated. They are necessary in the event | ||
// handlers of the upper applications. Set `true` to force calculate them. | ||
return normalizeEvent(instance.dom, new FakeGlobalEvent(instance, event), true); | ||
} | ||
/** | ||
* Detect whether the given el is in `painterRoot`. | ||
*/ | ||
var domHandlers = { | ||
/** | ||
* Mouse move handler | ||
* @inner | ||
* @param {Event} event | ||
*/ | ||
function isLocalEl(instance, el) { | ||
var isLocal = false; | ||
do { | ||
el = el && el.parentNode; | ||
} while (el && el.nodeType !== 9 && !(isLocal = el === instance.painterRoot)); | ||
return isLocal; | ||
} | ||
/** | ||
* Make a fake event but not change the original event, | ||
* becuase the global event probably be used by other | ||
* listeners not belonging to zrender. | ||
* @class | ||
*/ | ||
function FakeGlobalEvent(instance, event) { | ||
this.type = event.type; | ||
this.target = this.currentTarget = instance.dom; | ||
this.pointerType = event.pointerType; // Necessray for the force calculation of zrX, zrY | ||
this.clientX = event.clientX; | ||
this.clientY = event.clientY; // Because we do not mount global listeners to touch events, | ||
// we do not copy `targetTouches` and `changedTouches` here. | ||
} | ||
var fakeGlobalEventProto = FakeGlobalEvent.prototype; // we make the default methods on the event do nothing, | ||
// otherwise it is dangerous. See more details in | ||
// [Drag outside] in `Handler.js`. | ||
fakeGlobalEventProto.stopPropagation = fakeGlobalEventProto.stopImmediatePropagation = fakeGlobalEventProto.preventDefault = zrUtil.noop; | ||
/** | ||
* Local DOM Handlers | ||
* @this {HandlerProxy} | ||
*/ | ||
var localDOMHandlers = { | ||
mousedown: function (event) { | ||
event = normalizeEvent(this.dom, event); | ||
this._mayPointerCapture = [event.zrX, event.zrY]; | ||
this.trigger('mousedown', event); | ||
}, | ||
mousemove: function (event) { | ||
event = normalizeEvent(this.dom, event); | ||
var downPoint = this._mayPointerCapture; | ||
if (downPoint && (event.zrX !== downPoint[0] || event.zrY !== downPoint[1])) { | ||
togglePointerCapture(this, true); | ||
} | ||
this.trigger('mousemove', event); | ||
}, | ||
/** | ||
* Mouse out handler | ||
* @inner | ||
* @param {Event} event | ||
*/ | ||
mouseup: function (event) { | ||
event = normalizeEvent(this.dom, event); | ||
togglePointerCapture(this, false); | ||
this.trigger('mouseup', event); | ||
}, | ||
mouseout: function (event) { | ||
event = normalizeEvent(this.dom, event); | ||
var element = event.toElement || event.relatedTarget; | ||
event = normalizeEvent(this.dom, event); // Similarly to the browser did on `document` and touch event, | ||
// `globalout` will be delayed to final pointer cature release. | ||
if (element !== this.dom) { | ||
while (element && element.nodeType !== 9) { | ||
// 忽略包含在root中的dom引起的mouseOut | ||
if (element === this.dom) { | ||
return; | ||
} | ||
if (this._pointerCapturing) { | ||
event.zrEventControl = 'no_globalout'; | ||
} // There might be some doms created by upper layer application | ||
// at the same level of painter.getViewportRoot() (e.g., tooltip | ||
// dom created by echarts), where 'globalout' event should not | ||
// be triggered when mouse enters these doms. (But 'mouseout' | ||
// should be triggered at the original hovered element as usual). | ||
element = element.parentNode; | ||
} | ||
} | ||
var element = event.toElement || event.relatedTarget; | ||
event.zrIsToLocalDOM = isLocalEl(this, element); | ||
this.trigger('mouseout', event); | ||
}, | ||
/** | ||
* Touch开始响应函数 | ||
* @inner | ||
* @param {Event} event | ||
*/ | ||
touchstart: function (event) { | ||
// Default mouse behaviour should not be disabled here. | ||
// For example, page may needs to be slided. | ||
event = normalizeEvent(this.dom, event); // Mark touch, which is useful in distinguish touch and | ||
// mouse event in upper applicatoin. | ||
event.zrByTouch = true; | ||
event = normalizeEvent(this.dom, event); | ||
markTouch(event); | ||
this._lastTouchMoment = new Date(); | ||
this.handler.processGesture(event, 'start'); // In touch device, trigger `mousemove`(`mouseover`) should | ||
// be triggered, and must before `mousedown` triggered. | ||
this.handler.processGesture(event, 'start'); // For consistent event listener for both touch device and mouse device, | ||
// we simulate "mouseover-->mousedown" in touch device. So we trigger | ||
// `mousemove` here (to trigger `mouseover` inside), and then trigger | ||
// `mousedown`. | ||
domHandlers.mousemove.call(this, event); | ||
domHandlers.mousedown.call(this, event); | ||
setTouchTimer(this); | ||
localDOMHandlers.mousemove.call(this, event); | ||
localDOMHandlers.mousedown.call(this, event); | ||
}, | ||
/** | ||
* Touch移动响应函数 | ||
* @inner | ||
* @param {Event} event | ||
*/ | ||
touchmove: function (event) { | ||
event = normalizeEvent(this.dom, event); // Mark touch, which is useful in distinguish touch and | ||
// mouse event in upper applicatoin. | ||
event.zrByTouch = true; | ||
event = normalizeEvent(this.dom, event); | ||
markTouch(event); | ||
this.handler.processGesture(event, 'change'); // Mouse move should always be triggered no matter whether | ||
@@ -131,18 +212,9 @@ // there is gestrue event, because mouse move and pinch may | ||
domHandlers.mousemove.call(this, event); | ||
setTouchTimer(this); | ||
localDOMHandlers.mousemove.call(this, event); | ||
}, | ||
/** | ||
* Touch结束响应函数 | ||
* @inner | ||
* @param {Event} event | ||
*/ | ||
touchend: function (event) { | ||
event = normalizeEvent(this.dom, event); // Mark touch, which is useful in distinguish touch and | ||
// mouse event in upper applicatoin. | ||
event.zrByTouch = true; | ||
event = normalizeEvent(this.dom, event); | ||
markTouch(event); | ||
this.handler.processGesture(event, 'end'); | ||
domHandlers.mouseup.call(this, event); // Do not trigger `mouseout` here, in spite of `mousemove`(`mouseover`) is | ||
localDOMHandlers.mouseup.call(this, event); // Do not trigger `mouseout` here, in spite of `mousemove`(`mouseover`) is | ||
// triggered in `touchstart`. This seems to be illogical, but by this mechanism, | ||
@@ -158,9 +230,7 @@ // we can conveniently implement "hover style" in both PC and touch device just | ||
if (+new Date() - this._lastTouchMoment < TOUCH_CLICK_DELAY) { | ||
domHandlers.click.call(this, event); | ||
localDOMHandlers.click.call(this, event); | ||
} | ||
setTouchTimer(this); | ||
}, | ||
pointerdown: function (event) { | ||
domHandlers.mousedown.call(this, event); // if (useMSGuesture(this, event)) { | ||
localDOMHandlers.mousedown.call(this, event); // if (useMSGuesture(this, event)) { | ||
// this._msGesture.addPointer(event.pointerId); | ||
@@ -176,7 +246,7 @@ // } | ||
if (!isPointerFromTouch(event)) { | ||
domHandlers.mousemove.call(this, event); | ||
localDOMHandlers.mousemove.call(this, event); | ||
} | ||
}, | ||
pointerup: function (event) { | ||
domHandlers.mouseup.call(this, event); | ||
localDOMHandlers.mouseup.call(this, event); | ||
}, | ||
@@ -188,20 +258,15 @@ pointerout: function (event) { | ||
// in touchend. So we unify them. | ||
// (check domHandlers.touchend for detailed explanation) | ||
// (check localDOMHandlers.touchend for detailed explanation) | ||
if (!isPointerFromTouch(event)) { | ||
domHandlers.mouseout.call(this, event); | ||
localDOMHandlers.mouseout.call(this, event); | ||
} | ||
} | ||
}; | ||
/** | ||
* Othere DOM UI Event handlers for zr dom. | ||
* @this {HandlerProxy} | ||
*/ | ||
function isPointerFromTouch(event) { | ||
var pointerType = event.pointerType; | ||
return pointerType === 'pen' || pointerType === 'touch'; | ||
} // function useMSGuesture(handlerProxy, event) { | ||
// return isPointerFromTouch(event) && !!handlerProxy._msGesture; | ||
// } | ||
// Common handlers | ||
zrUtil.each(['click', 'mousedown', 'mouseup', 'mousewheel', 'dblclick', 'contextmenu'], function (name) { | ||
domHandlers[name] = function (event) { | ||
zrUtil.each(['click', 'mousewheel', 'dblclick', 'contextmenu'], function (name) { | ||
localDOMHandlers[name] = function (event) { | ||
event = normalizeEvent(this.dom, event); | ||
@@ -212,48 +277,46 @@ this.trigger(name, event); | ||
/** | ||
* 为控制类实例初始化dom 事件处理函数 | ||
* DOM UI Event handlers for global page. | ||
* | ||
* @inner | ||
* @param {module:zrender/Handler} instance 控制类实例 | ||
* [Caution]: | ||
* those handlers should both support in capture phase and bubble phase! | ||
* | ||
* @this {HandlerProxy} | ||
*/ | ||
function initDomHandler(instance) { | ||
zrUtil.each(touchHandlerNames, function (name) { | ||
instance._handlers[name] = zrUtil.bind(domHandlers[name], instance); | ||
}); | ||
zrUtil.each(pointerHandlerNames, function (name) { | ||
instance._handlers[name] = zrUtil.bind(domHandlers[name], instance); | ||
}); | ||
zrUtil.each(mouseHandlerNames, function (name) { | ||
instance._handlers[name] = makeMouseHandler(domHandlers[name], instance); | ||
}); | ||
var globalDOMHandlers = { | ||
pointermove: function (event) { | ||
// FIXME | ||
// pointermove is so sensitive that it always triggered when | ||
// tap(click) on touch screen, which affect some judgement in | ||
// upper application. So, we dont support mousemove on MS touch | ||
// device yet. | ||
if (!isPointerFromTouch(event)) { | ||
globalDOMHandlers.mousemove.call(this, event); | ||
} | ||
}, | ||
pointerup: function (event) { | ||
globalDOMHandlers.mouseup.call(this, event); | ||
}, | ||
mousemove: function (event) { | ||
this.trigger('mousemove', event); | ||
}, | ||
mouseup: function (event) { | ||
var pointerCaptureReleasing = this._pointerCapturing; | ||
togglePointerCapture(this, false); | ||
this.trigger('mouseup', event); | ||
function makeMouseHandler(fn, instance) { | ||
return function () { | ||
if (instance._touching) { | ||
return; | ||
} | ||
return fn.apply(instance, arguments); | ||
}; | ||
if (pointerCaptureReleasing) { | ||
event.zrEventControl = 'only_globalout'; | ||
this.trigger('mouseout', event); | ||
} | ||
} | ||
} | ||
}; | ||
/** | ||
* @param {HandlerProxy} instance | ||
* @param {DOMHandlerScope} scope | ||
*/ | ||
function HandlerDomProxy(dom) { | ||
Eventful.call(this); | ||
this.dom = dom; | ||
/** | ||
* @private | ||
* @type {boolean} | ||
*/ | ||
function mountLocalDOMEventListeners(instance, scope) { | ||
var domHandlers = scope.domHandlers; | ||
this._touching = false; | ||
/** | ||
* @private | ||
* @type {number} | ||
*/ | ||
this._touchTimer; | ||
this._handlers = {}; | ||
initDomHandler(this); | ||
if (env.pointerEventsSupported) { | ||
@@ -267,3 +330,8 @@ // Only IE11+/Edge | ||
// So we use pointer event to both detect touch gesture and mouse behavior. | ||
mountHandlers(pointerHandlerNames, this); // FIXME | ||
zrUtil.each(localNativeListenerNames.pointer, function (nativeEventName) { | ||
mountSingleDOMEventListener(scope, nativeEventName, function (event) { | ||
// markTriggeredFromLocal(event); | ||
domHandlers[nativeEventName].call(instance, event); | ||
}); | ||
}); // FIXME | ||
// Note: MS Gesture require CSS touch-action set. But touch-action is not reliable, | ||
@@ -283,3 +351,9 @@ // which does not prevent defuault behavior occasionally (which may cause view port | ||
if (env.touchEventsSupported) { | ||
mountHandlers(touchHandlerNames, this); // Handler of 'mouseout' event is needed in touch mode, which will be mounted below. | ||
zrUtil.each(localNativeListenerNames.touch, function (nativeEventName) { | ||
mountSingleDOMEventListener(scope, nativeEventName, function (event) { | ||
// markTriggeredFromLocal(event); | ||
domHandlers[nativeEventName].call(instance, event); | ||
setTouchTimer(scope); | ||
}); | ||
}); // Handler of 'mouseout' event is needed in touch mode, which will be mounted below. | ||
// addEventListener(root, 'mouseout', this._mouseoutHandler); | ||
@@ -293,20 +367,140 @@ } // 1. Considering some devices that both enable touch and mouse event (like on MS Surface | ||
mountHandlers(mouseHandlerNames, this); | ||
zrUtil.each(localNativeListenerNames.mouse, function (nativeEventName) { | ||
mountSingleDOMEventListener(scope, nativeEventName, function (event) { | ||
event = getNativeEvent(event); | ||
if (!scope.touching) { | ||
// markTriggeredFromLocal(event); | ||
domHandlers[nativeEventName].call(instance, event); | ||
} | ||
}); | ||
}); | ||
} | ||
} | ||
/** | ||
* @param {HandlerProxy} instance | ||
* @param {DOMHandlerScope} scope | ||
*/ | ||
function mountHandlers(handlerNames, instance) { | ||
zrUtil.each(handlerNames, function (name) { | ||
addEventListener(dom, eventNameFix(name), instance._handlers[name]); | ||
}, instance); | ||
function mountGlobalDOMEventListeners(instance, scope) { | ||
// Only IE11+/Edge. See the comment in `mountLocalDOMEventListeners`. | ||
if (env.pointerEventsSupported) { | ||
zrUtil.each(globalNativeListenerNames.pointer, mount); | ||
} // Touch event has implemented "drag outside" so we do not mount global listener for touch event. | ||
// (see https://www.w3.org/TR/touch-events/#the-touchmove-event) | ||
// We do not consider "both-support-touch-and-mouse device" for this feature (see the comment of | ||
// `mountLocalDOMEventListeners`) to avoid bugs util some requirements come. | ||
else if (!env.touchEventsSupported) { | ||
zrUtil.each(globalNativeListenerNames.mouse, mount); | ||
} | ||
function mount(nativeEventName) { | ||
function nativeEventListener(event) { | ||
event = getNativeEvent(event); // See the reason in [Drag outside] in `Handler.js` | ||
// This checking supports both `useCapture` or not. | ||
// PENDING: if there is performance issue in some devices, | ||
// we probably can not use `useCapture` and change a easier | ||
// to judes whether local (mark). | ||
if (!isLocalEl(instance, event.target)) { | ||
event = normalizeGlobalEvent(instance, event); | ||
scope.domHandlers[nativeEventName].call(instance, event); | ||
} | ||
} | ||
mountSingleDOMEventListener(scope, nativeEventName, nativeEventListener, { | ||
capture: true | ||
} // See [Drag Outside] in `Handler.js` | ||
); | ||
} | ||
} | ||
function mountSingleDOMEventListener(scope, nativeEventName, listener, opt) { | ||
scope.mounted[nativeEventName] = listener; | ||
scope.listenerOpts[nativeEventName] = opt; | ||
addEventListener(scope.domTarget, eventNameFix(nativeEventName), listener, opt); | ||
} | ||
function unmountDOMEventListeners(scope) { | ||
var mounted = scope.mounted; | ||
for (var nativeEventName in mounted) { | ||
if (mounted.hasOwnProperty(nativeEventName)) { | ||
removeEventListener(scope.domTarget, eventNameFix(nativeEventName), mounted[nativeEventName], scope.listenerOpts[nativeEventName]); | ||
} | ||
} | ||
scope.mounted = {}; | ||
} | ||
/** | ||
* See [Drag Outside] in `Handler.js`. | ||
* @implement | ||
* @param {boolean} isPointerCapturing Should never be `null`/`undefined`. | ||
* `true`: start to capture pointer if it is not capturing. | ||
* `false`: end the capture if it is capturing. | ||
*/ | ||
function togglePointerCapture(instance, isPointerCapturing) { | ||
instance._mayPointerCapture = null; | ||
if (globalEventSupported && instance._pointerCapturing ^ isPointerCapturing) { | ||
instance._pointerCapturing = isPointerCapturing; | ||
var globalHandlerScope = instance._globalHandlerScope; | ||
isPointerCapturing ? mountGlobalDOMEventListeners(instance, globalHandlerScope) : unmountDOMEventListeners(globalHandlerScope); | ||
} | ||
} | ||
/** | ||
* @inner | ||
* @class | ||
*/ | ||
function DOMHandlerScope(domTarget, domHandlers) { | ||
this.domTarget = domTarget; | ||
this.domHandlers = domHandlers; // Key: eventName, value: mounted handler funcitons. | ||
// Used for unmount. | ||
this.mounted = {}; | ||
this.listenerOpts = {}; | ||
this.touchTimer = null; | ||
this.touching = false; | ||
} | ||
/** | ||
* @public | ||
* @class | ||
*/ | ||
function HandlerDomProxy(dom, painterRoot) { | ||
Eventful.call(this); | ||
this.dom = dom; | ||
this.painterRoot = painterRoot; | ||
this._localHandlerScope = new DOMHandlerScope(dom, localDOMHandlers); | ||
if (globalEventSupported) { | ||
this._globalHandlerScope = new DOMHandlerScope(document, globalDOMHandlers); | ||
} | ||
/** | ||
* @type {boolean} | ||
*/ | ||
this._pointerCapturing = false; | ||
/** | ||
* @type {Array.<number>} [x, y] or null. | ||
*/ | ||
this._mayPointerCapture = null; | ||
mountLocalDOMEventListeners(this, this._localHandlerScope); | ||
} | ||
var handlerDomProxyProto = HandlerDomProxy.prototype; | ||
handlerDomProxyProto.dispose = function () { | ||
var handlerNames = mouseHandlerNames.concat(touchHandlerNames); | ||
unmountDOMEventListeners(this._localHandlerScope); | ||
for (var i = 0; i < handlerNames.length; i++) { | ||
var name = handlerNames[i]; | ||
removeEventListener(this.dom, eventNameFix(name), this._handlers[name]); | ||
if (globalEventSupported) { | ||
unmountDOMEventListeners(this._globalHandlerScope); | ||
} | ||
@@ -313,0 +507,0 @@ }; |
@@ -13,2 +13,59 @@ var util = require("./core/util"); | ||
/** | ||
* [The interface between `Handler` and `HandlerProxy`]: | ||
* | ||
* The default `HandlerProxy` only support the common standard web environment | ||
* (e.g., standalone browser, headless browser, embed browser in mobild APP, ...). | ||
* But `HandlerProxy` can be replaced to support more non-standard environment | ||
* (e.g., mini app), or to support more feature that the default `HandlerProxy` | ||
* not provided (like echarts-gl did). | ||
* So the interface between `Handler` and `HandlerProxy` should be stable. Do not | ||
* make break changes util inevitable. The interface include the public methods | ||
* of `Handler` and the events listed in `handlerNames` below, by which `HandlerProxy` | ||
* drives `Handler`. | ||
*/ | ||
/** | ||
* [Drag outside]: | ||
* | ||
* That is, triggering `mousemove` and `mouseup` event when the pointer is out of the | ||
* zrender area when dragging. That is important for the improvement of the user experience | ||
* when dragging something near the boundary without being terminated unexpectedly. | ||
* | ||
* We originally consider to introduce new events like `pagemovemove` and `pagemouseup` | ||
* to resolve this issue. But some drawbacks of it is described in | ||
* https://github.com/ecomfe/zrender/pull/536#issuecomment-560286899 | ||
* | ||
* Instead, we referenced the specifications: | ||
* https://www.w3.org/TR/touch-events/#the-touchmove-event | ||
* https://www.w3.org/TR/2014/WD-DOM-Level-3-Events-20140925/#event-type-mousemove | ||
* where the the mousemove/touchmove can be continue to fire if the user began a drag | ||
* operation and the pointer has left the boundary. (for the mouse event, browsers | ||
* only do it on `document` and when the pointer has left the boundary of the browser.) | ||
* | ||
* So the default `HandlerProxy` supports this feature similarly: if it is in the dragging | ||
* state (see `pointerCapture` in `HandlerProxy`), the `mousemove` and `mouseup` continue | ||
* to fire until release the pointer. That is implemented by listen to those event on | ||
* `document`. | ||
* If we implement some other `HandlerProxy` only for touch device, that would be easier. | ||
* The touch event support this feature by default. | ||
* | ||
* Note: | ||
* There might be some cases that the mouse event can not be | ||
* received on `document`. For example, | ||
* (A) `useCapture` is not supported and some user defined event listeners on the ancestor | ||
* of zr dom throw Error . | ||
* (B) `useCapture` is not supported Some user defined event listeners on the ancestor of | ||
* zr dom call `stopPropagation`. | ||
* In these cases, the `mousemove` event might be keep triggered event | ||
* if the mouse is released. We try to reduce the side-effect in those cases. | ||
* That is, do nothing (especially, `findHover`) in those cases. See `isOutsideBoundary`. | ||
* | ||
* Note: | ||
* If `HandlerProxy` listens to `document` with `useCapture`, `HandlerProxy` needs to | ||
* make sure `stopPropagation` and `preventDefault` doing nothing if and only if the event | ||
* target is not zrender dom. Becuase it is dangerous to enable users to call them in | ||
* `document` capture phase to prevent the propagation to any listener of the webpage. | ||
* But they are needed to work when the pointer inside the zrender dom. | ||
*/ | ||
var SILENT = 'silent'; | ||
@@ -38,3 +95,3 @@ | ||
function stopEvent(event) { | ||
function stopEvent() { | ||
eventTool.stop(this.event); | ||
@@ -124,2 +181,3 @@ } | ||
var y = event.zrY; | ||
var isOutside = isOutsideBoundary(this, x, y); | ||
var lastHovered = this._hovered; | ||
@@ -136,3 +194,6 @@ var lastHoveredTarget = lastHovered.target; // If lastHoveredTarget is removed from zr (detected by '__zr') by some API call | ||
var hovered = this._hovered = this.findHover(x, y); | ||
var hovered = this._hovered = isOutside ? { | ||
x: x, | ||
y: y | ||
} : this.findHover(x, y); | ||
var hoveredTarget = hovered.target; | ||
@@ -154,18 +215,17 @@ var proxy = this.proxy; | ||
mouseout: function (event) { | ||
this.dispatchToElement(this._hovered, 'mouseout', event); // There might be some doms created by upper layer application | ||
// at the same level of painter.getViewportRoot() (e.g., tooltip | ||
// dom created by echarts), where 'globalout' event should not | ||
// be triggered when mouse enters these doms. (But 'mouseout' | ||
// should be triggered at the original hovered element as usual). | ||
var eventControl = event.zrEventControl; | ||
var zrIsToLocalDOM = event.zrIsToLocalDOM; | ||
var element = event.toElement || event.relatedTarget; | ||
var innerDom; | ||
if (eventControl !== 'only_globalout') { | ||
this.dispatchToElement(this._hovered, 'mouseout', event); | ||
} | ||
do { | ||
element = element && element.parentNode; | ||
} while (element && element.nodeType !== 9 && !(innerDom = element === this.painterRoot)); | ||
!innerDom && this.trigger('globalout', { | ||
event: event | ||
}); | ||
if (eventControl !== 'no_globalout') { | ||
// FIXME: if the pointer moving from the extra doms to realy "outside", | ||
// the `globalout` should have been triggered. But currently not. | ||
!zrIsToLocalDOM && this.trigger('globalout', { | ||
type: 'globalout', | ||
event: event | ||
}); | ||
} | ||
}, | ||
@@ -306,6 +366,14 @@ | ||
Handler.prototype[name] = function (event) { | ||
// Find hover again to avoid click event is dispatched manually. Or click is triggered without mouseover | ||
var hovered = this.findHover(event.zrX, event.zrY); | ||
var hoveredTarget = hovered.target; | ||
var x = event.zrX; | ||
var y = event.zrY; | ||
var isOutside = isOutsideBoundary(this, x, y); | ||
var hovered; | ||
var hoveredTarget; | ||
if (name !== 'mouseup' || !isOutside) { | ||
// Find hover again to avoid click event is dispatched manually. Or click is triggered without mouseover | ||
hovered = this.findHover(x, y); | ||
hoveredTarget = hovered.target; | ||
} | ||
if (name === 'mousedown') { | ||
@@ -360,3 +428,12 @@ this._downEl = hoveredTarget; | ||
} | ||
/** | ||
* See [Drag outside]. | ||
*/ | ||
function isOutsideBoundary(handlerInstance, x, y) { | ||
var painter = handlerInstance.painter; | ||
return x < 0 || x > painter.getWidth() || y < 0 || y > painter.getHeight(); | ||
} | ||
util.mixin(Handler, Eventful); | ||
@@ -363,0 +440,0 @@ util.mixin(Handler, Draggable); |
@@ -6,4 +6,7 @@ // TODO Draggable for group | ||
this.on('mousemove', this._drag, this); | ||
this.on('mouseup', this._dragEnd, this); | ||
this.on('globalout', this._dragEnd, this); // this._dropTarget = null; | ||
this.on('mouseup', this._dragEnd, this); // `mosuemove` and `mouseup` can be continue to fire when dragging. | ||
// See [Drag outside] in `Handler.js`. So we do not need to trigger | ||
// `_dragEnd` when globalout. That would brings better user experience. | ||
// this.on('globalout', this._dragEnd, this); | ||
// this._dropTarget = null; | ||
// this._draggingTarget = null; | ||
@@ -10,0 +13,0 @@ // this._x = 0; |
@@ -23,3 +23,3 @@ /** | ||
* return: {boolean} | ||
* @param {Function} [eventProcessor.afterTrigger] Call after all handlers called. | ||
* @param {Function} [eventProcessor.afterTrigger] Called after all handlers called. | ||
* param: {string} eventType | ||
@@ -74,4 +74,6 @@ */ | ||
* | ||
* @param {string} event The event name. | ||
* @param {string} [event] The event name. | ||
* If no `event` input, "off" all listeners. | ||
* @param {Function} [handler] The event handler. | ||
* If no `handler` input, "off" all listeners of the `event`. | ||
*/ | ||
@@ -78,0 +80,0 @@ off: function (event, handler) { |
@@ -252,2 +252,4 @@ var _core = require("./core"); | ||
svgTextDrawRectText(el, el.getBoundingRect()); | ||
} else { | ||
removeOldTextNode(el); | ||
} | ||
@@ -300,2 +302,4 @@ }; | ||
svgTextDrawRectText(el, el.getBoundingRect()); | ||
} else { | ||
removeOldTextNode(el); | ||
} | ||
@@ -494,2 +498,15 @@ }; | ||
function removeOldTextNode(el) { | ||
if (el && el.__textSvgEl) { | ||
// textSvgEl may has no parentNode if el has been removed temporary. | ||
if (el.__textSvgEl.parentNode) { | ||
el.__textSvgEl.parentNode.removeChild(el.__textSvgEl); | ||
} | ||
el.__textSvgEl = null; | ||
el.__tspanList = []; | ||
el.__text = null; | ||
} | ||
} | ||
svgText.drawRectText = svgTextDrawRectText; | ||
@@ -502,2 +519,4 @@ | ||
svgTextDrawRectText(el, false); | ||
} else { | ||
removeOldTextNode(el); | ||
} | ||
@@ -504,0 +523,0 @@ }; |
@@ -224,5 +224,6 @@ var _core = require("./core"); | ||
var displayable = newVisibleList[item.indices[k]]; | ||
prevSvgElement = getTextSvgElement(displayable) || getSvgElement(displayable) || prevSvgElement; | ||
var svgElement = getSvgElement(displayable); | ||
var textSvgElement = getTextSvgElement(displayable); | ||
var svgElement = getSvgElement(displayable); | ||
var textSvgElement = getTextSvgElement(displayable); | ||
this.gradientManager.markUsed(displayable); | ||
@@ -233,2 +234,9 @@ this.gradientManager.addWithoutUpdate(svgElement || textSvgElement, displayable); | ||
this.clipPathManager.markUsed(displayable); | ||
if (textSvgElement) { | ||
// Insert text. | ||
insertAfter(svgRoot, textSvgElement, svgElement); | ||
} | ||
prevSvgElement = svgElement || textSvgElement || prevSvgElement; | ||
} | ||
@@ -235,0 +243,0 @@ } |
@@ -36,3 +36,3 @@ var guid = require("./core/guid"); | ||
var version = '4.1.2'; | ||
var version = '4.2.0'; | ||
/** | ||
@@ -139,3 +139,3 @@ * Initializing a zrender instance | ||
this.painter = painter; | ||
var handerProxy = !env.node && !env.worker ? new HandlerProxy(painter.getViewportRoot()) : null; | ||
var handerProxy = !env.node && !env.worker ? new HandlerProxy(painter.getViewportRoot(), painter.root) : null; | ||
this.handler = new Handler(storage, painter, handerProxy, painter.root); | ||
@@ -142,0 +142,0 @@ /** |
{ | ||
"name": "zrender", | ||
"version": "4.1.2", | ||
"version": "4.2.0", | ||
"description": "A lightweight canvas library.", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
@@ -151,8 +151,13 @@ /** | ||
// <http://caniuse.com/#search=pointer%20event>. | ||
pointerEventsSupported: 'onpointerdown' in window | ||
// Firefox supports pointer but not by default, only MS browsers are reliable on pointer | ||
pointerEventsSupported: | ||
// (1) Firefox supports pointer but not by default, only MS browsers are reliable on pointer | ||
// events currently. So we dont use that on other browsers unless tested sufficiently. | ||
// Although IE 10 supports pointer event, it use old style and is different from the | ||
// For example, in iOS 13 Mobile Chromium 78, if the touching behavior starts page | ||
// scroll, the `pointermove` event can not be fired any more. That will break some | ||
// features like "pan horizontally to move something and pan vertically to page scroll". | ||
// The horizontal pan probably be interrupted by the casually triggered page scroll. | ||
// (2) Although IE 10 supports pointer event, it use old style and is different from the | ||
// standard. So we exclude that. (IE 10 is hardly used on touch device) | ||
&& (browser.edge || (browser.ie && browser.version >= 11)), | ||
'onpointerdown' in window | ||
&& (browser.edge || (browser.ie && browser.version >= 11)), | ||
// passiveSupported: detectPassiveSupport() | ||
@@ -159,0 +164,0 @@ domSupported: typeof document !== 'undefined' |
@@ -171,2 +171,14 @@ /** | ||
/** | ||
* Find native event compat for legency IE. | ||
* Should be called at the begining of a native event listener. | ||
* | ||
* @param {Event} [e] Mouse event or touch event or pointer event. | ||
* For lagency IE, we use `window.event` is used. | ||
* @return {Event} The native event. | ||
*/ | ||
export function getNativeEvent(e) { | ||
return e || window.event; | ||
} | ||
/** | ||
* Normalize the coordinates of the input event. | ||
@@ -185,10 +197,10 @@ * | ||
* @param {HTMLElement} el DOM element. | ||
* @param {Event} [e] Mouse event or touch event. For lagency IE, | ||
* do not need to input it and `window.event` is used. | ||
* @param {Event} [e] See `getNativeEvent`. | ||
* @param {boolean} [calculate=false] Whether to force calculate | ||
* the coordinates but not use ones provided by browser. | ||
* @return {UIEvent} The normalized native UIEvent. | ||
*/ | ||
export function normalizeEvent(el, e, calculate) { | ||
e = e || window.event; | ||
e = getNativeEvent(e); | ||
@@ -233,4 +245,7 @@ if (e.zrX != null) { | ||
* @param {Function} handler | ||
* @param {Object|boolean} opt If boolean, means `opt.capture` | ||
* @param {boolean} [opt.capture=false] | ||
* @param {boolean} [opt.passive=false] | ||
*/ | ||
export function addEventListener(el, name, handler) { | ||
export function addEventListener(el, name, handler, opt) { | ||
if (isDomLevel2) { | ||
@@ -258,5 +273,6 @@ // Reproduct the console warning: | ||
// el.addEventListener(name, handler /* , opts */); | ||
el.addEventListener(name, handler); | ||
el.addEventListener(name, handler, opt); | ||
} | ||
else { | ||
// For simplicity, do not implement `setCapture` for IE9-. | ||
el.attachEvent('on' + name, handler); | ||
@@ -266,5 +282,12 @@ } | ||
export function removeEventListener(el, name, handler) { | ||
/** | ||
* Parameter are the same as `addEventListener`. | ||
* | ||
* Notice that if a listener is registered twice, one with capture and one without, | ||
* remove each one separately. Removal of a capturing listener does not affect a | ||
* non-capturing version of the same listener, and vice versa. | ||
*/ | ||
export function removeEventListener(el, name, handler, opt) { | ||
if (isDomLevel2) { | ||
el.removeEventListener(name, handler); | ||
el.removeEventListener(name, handler, opt); | ||
} | ||
@@ -271,0 +294,0 @@ else { |
/* global document */ | ||
import { | ||
addEventListener, | ||
removeEventListener, | ||
normalizeEvent | ||
normalizeEvent, | ||
getNativeEvent | ||
} from '../core/event'; | ||
@@ -13,19 +16,33 @@ import * as zrUtil from '../core/util'; | ||
var mouseHandlerNames = [ | ||
'click', 'dblclick', 'mousewheel', 'mouseout', | ||
'mouseup', 'mousedown', 'mousemove', 'contextmenu' | ||
]; | ||
var globalEventSupported = env.domSupported; | ||
var touchHandlerNames = [ | ||
'touchstart', 'touchend', 'touchmove' | ||
]; | ||
var pointerEventNames = { | ||
pointerdown: 1, pointerup: 1, pointermove: 1, pointerout: 1 | ||
var localNativeListenerNames = (function () { | ||
var mouseHandlerNames = [ | ||
'click', 'dblclick', 'mousewheel', 'mouseout', | ||
'mouseup', 'mousedown', 'mousemove', 'contextmenu' | ||
]; | ||
var touchHandlerNames = [ | ||
'touchstart', 'touchend', 'touchmove' | ||
]; | ||
var pointerEventNameMap = { | ||
pointerdown: 1, pointerup: 1, pointermove: 1, pointerout: 1 | ||
}; | ||
var pointerHandlerNames = zrUtil.map(mouseHandlerNames, function (name) { | ||
var nm = name.replace('mouse', 'pointer'); | ||
return pointerEventNameMap.hasOwnProperty(nm) ? nm : name; | ||
}); | ||
return { | ||
mouse: mouseHandlerNames, | ||
touch: touchHandlerNames, | ||
pointer: pointerHandlerNames | ||
}; | ||
})(); | ||
var globalNativeListenerNames = { | ||
mouse: ['mousemove', 'mouseup'], | ||
pointer: ['pointermove', 'pointerup'] | ||
}; | ||
var pointerHandlerNames = zrUtil.map(mouseHandlerNames, function (name) { | ||
var nm = name.replace('mouse', 'pointer'); | ||
return pointerEventNames[nm] ? nm : name; | ||
}); | ||
@@ -36,2 +53,11 @@ function eventNameFix(name) { | ||
function isPointerFromTouch(event) { | ||
var pointerType = event.pointerType; | ||
return pointerType === 'pen' || pointerType === 'touch'; | ||
} | ||
// function useMSGuesture(handlerProxy, event) { | ||
// return isPointerFromTouch(event) && !!handlerProxy._msGesture; | ||
// } | ||
// function onMSGestureChange(proxy, event) { | ||
@@ -56,52 +82,130 @@ // if (event.translationX || event.translationY) { | ||
* Result: Blocking Mouse Events for 700ms. | ||
* | ||
* @param {DOMHandlerScope} scope | ||
*/ | ||
function setTouchTimer(instance) { | ||
instance._touching = true; | ||
clearTimeout(instance._touchTimer); | ||
instance._touchTimer = setTimeout(function () { | ||
instance._touching = false; | ||
function setTouchTimer(scope) { | ||
scope.touching = true; | ||
if (scope.touchTimer != null) { | ||
clearTimeout(scope.touchTimer); | ||
scope.touchTimer = null; | ||
} | ||
scope.touchTimer = setTimeout(function () { | ||
scope.touching = false; | ||
scope.touchTimer = null; | ||
}, 700); | ||
} | ||
// Mark touch, which is useful in distinguish touch and | ||
// mouse event in upper applicatoin. | ||
function markTouch(event) { | ||
event && (event.zrByTouch = true); | ||
} | ||
var domHandlers = { | ||
/** | ||
* Mouse move handler | ||
* @inner | ||
* @param {Event} event | ||
*/ | ||
// function markTriggeredFromLocal(event) { | ||
// event && (event.__zrIsFromLocal = true); | ||
// } | ||
// function isTriggeredFromLocal(instance, event) { | ||
// return !!(event && event.__zrIsFromLocal); | ||
// } | ||
function normalizeGlobalEvent(instance, event) { | ||
// offsetX, offsetY still need to be calculated. They are necessary in the event | ||
// handlers of the upper applications. Set `true` to force calculate them. | ||
return normalizeEvent(instance.dom, new FakeGlobalEvent(instance, event), true); | ||
} | ||
/** | ||
* Detect whether the given el is in `painterRoot`. | ||
*/ | ||
function isLocalEl(instance, el) { | ||
var isLocal = false; | ||
do { | ||
el = el && el.parentNode; | ||
} | ||
while (el && el.nodeType !== 9 && !( | ||
isLocal = el === instance.painterRoot | ||
)); | ||
return isLocal; | ||
} | ||
/** | ||
* Make a fake event but not change the original event, | ||
* becuase the global event probably be used by other | ||
* listeners not belonging to zrender. | ||
* @class | ||
*/ | ||
function FakeGlobalEvent(instance, event) { | ||
this.type = event.type; | ||
this.target = this.currentTarget = instance.dom; | ||
this.pointerType = event.pointerType; | ||
// Necessray for the force calculation of zrX, zrY | ||
this.clientX = event.clientX; | ||
this.clientY = event.clientY; | ||
// Because we do not mount global listeners to touch events, | ||
// we do not copy `targetTouches` and `changedTouches` here. | ||
} | ||
var fakeGlobalEventProto = FakeGlobalEvent.prototype; | ||
// we make the default methods on the event do nothing, | ||
// otherwise it is dangerous. See more details in | ||
// [Drag outside] in `Handler.js`. | ||
fakeGlobalEventProto.stopPropagation = | ||
fakeGlobalEventProto.stopImmediatePropagation = | ||
fakeGlobalEventProto.preventDefault = zrUtil.noop; | ||
/** | ||
* Local DOM Handlers | ||
* @this {HandlerProxy} | ||
*/ | ||
var localDOMHandlers = { | ||
mousedown: function (event) { | ||
event = normalizeEvent(this.dom, event); | ||
this._mayPointerCapture = [event.zrX, event.zrY]; | ||
this.trigger('mousedown', event); | ||
}, | ||
mousemove: function (event) { | ||
event = normalizeEvent(this.dom, event); | ||
var downPoint = this._mayPointerCapture; | ||
if (downPoint && (event.zrX !== downPoint[0] || event.zrY !== downPoint[1])) { | ||
togglePointerCapture(this, true); | ||
} | ||
this.trigger('mousemove', event); | ||
}, | ||
/** | ||
* Mouse out handler | ||
* @inner | ||
* @param {Event} event | ||
*/ | ||
mouseup: function (event) { | ||
event = normalizeEvent(this.dom, event); | ||
togglePointerCapture(this, false); | ||
this.trigger('mouseup', event); | ||
}, | ||
mouseout: function (event) { | ||
event = normalizeEvent(this.dom, event); | ||
// Similarly to the browser did on `document` and touch event, | ||
// `globalout` will be delayed to final pointer cature release. | ||
if (this._pointerCapturing) { | ||
event.zrEventControl = 'no_globalout'; | ||
} | ||
// There might be some doms created by upper layer application | ||
// at the same level of painter.getViewportRoot() (e.g., tooltip | ||
// dom created by echarts), where 'globalout' event should not | ||
// be triggered when mouse enters these doms. (But 'mouseout' | ||
// should be triggered at the original hovered element as usual). | ||
var element = event.toElement || event.relatedTarget; | ||
if (element !== this.dom) { | ||
while (element && element.nodeType !== 9) { | ||
// 忽略包含在root中的dom引起的mouseOut | ||
if (element === this.dom) { | ||
return; | ||
} | ||
event.zrIsToLocalDOM = isLocalEl(this, element); | ||
element = element.parentNode; | ||
} | ||
} | ||
this.trigger('mouseout', event); | ||
}, | ||
/** | ||
* Touch开始响应函数 | ||
* @inner | ||
* @param {Event} event | ||
*/ | ||
touchstart: function (event) { | ||
@@ -112,5 +216,3 @@ // Default mouse behaviour should not be disabled here. | ||
// Mark touch, which is useful in distinguish touch and | ||
// mouse event in upper applicatoin. | ||
event.zrByTouch = true; | ||
markTouch(event); | ||
@@ -121,23 +223,14 @@ this._lastTouchMoment = new Date(); | ||
// In touch device, trigger `mousemove`(`mouseover`) should | ||
// be triggered, and must before `mousedown` triggered. | ||
domHandlers.mousemove.call(this, event); | ||
domHandlers.mousedown.call(this, event); | ||
setTouchTimer(this); | ||
// For consistent event listener for both touch device and mouse device, | ||
// we simulate "mouseover-->mousedown" in touch device. So we trigger | ||
// `mousemove` here (to trigger `mouseover` inside), and then trigger | ||
// `mousedown`. | ||
localDOMHandlers.mousemove.call(this, event); | ||
localDOMHandlers.mousedown.call(this, event); | ||
}, | ||
/** | ||
* Touch移动响应函数 | ||
* @inner | ||
* @param {Event} event | ||
*/ | ||
touchmove: function (event) { | ||
event = normalizeEvent(this.dom, event); | ||
// Mark touch, which is useful in distinguish touch and | ||
// mouse event in upper applicatoin. | ||
event.zrByTouch = true; | ||
markTouch(event); | ||
@@ -149,23 +242,13 @@ this.handler.processGesture(event, 'change'); | ||
// be used at the same time. | ||
domHandlers.mousemove.call(this, event); | ||
setTouchTimer(this); | ||
localDOMHandlers.mousemove.call(this, event); | ||
}, | ||
/** | ||
* Touch结束响应函数 | ||
* @inner | ||
* @param {Event} event | ||
*/ | ||
touchend: function (event) { | ||
event = normalizeEvent(this.dom, event); | ||
// Mark touch, which is useful in distinguish touch and | ||
// mouse event in upper applicatoin. | ||
event.zrByTouch = true; | ||
markTouch(event); | ||
this.handler.processGesture(event, 'end'); | ||
domHandlers.mouseup.call(this, event); | ||
localDOMHandlers.mouseup.call(this, event); | ||
@@ -183,10 +266,8 @@ // Do not trigger `mouseout` here, in spite of `mousemove`(`mouseover`) is | ||
if (+new Date() - this._lastTouchMoment < TOUCH_CLICK_DELAY) { | ||
domHandlers.click.call(this, event); | ||
localDOMHandlers.click.call(this, event); | ||
} | ||
setTouchTimer(this); | ||
}, | ||
pointerdown: function (event) { | ||
domHandlers.mousedown.call(this, event); | ||
localDOMHandlers.mousedown.call(this, event); | ||
@@ -205,3 +286,3 @@ // if (useMSGuesture(this, event)) { | ||
if (!isPointerFromTouch(event)) { | ||
domHandlers.mousemove.call(this, event); | ||
localDOMHandlers.mousemove.call(this, event); | ||
} | ||
@@ -211,3 +292,3 @@ }, | ||
pointerup: function (event) { | ||
domHandlers.mouseup.call(this, event); | ||
localDOMHandlers.mouseup.call(this, event); | ||
}, | ||
@@ -220,21 +301,16 @@ | ||
// in touchend. So we unify them. | ||
// (check domHandlers.touchend for detailed explanation) | ||
// (check localDOMHandlers.touchend for detailed explanation) | ||
if (!isPointerFromTouch(event)) { | ||
domHandlers.mouseout.call(this, event); | ||
localDOMHandlers.mouseout.call(this, event); | ||
} | ||
} | ||
}; | ||
function isPointerFromTouch(event) { | ||
var pointerType = event.pointerType; | ||
return pointerType === 'pen' || pointerType === 'touch'; | ||
} | ||
// function useMSGuesture(handlerProxy, event) { | ||
// return isPointerFromTouch(event) && !!handlerProxy._msGesture; | ||
// } | ||
// Common handlers | ||
zrUtil.each(['click', 'mousedown', 'mouseup', 'mousewheel', 'dblclick', 'contextmenu'], function (name) { | ||
domHandlers[name] = function (event) { | ||
/** | ||
* Othere DOM UI Event handlers for zr dom. | ||
* @this {HandlerProxy} | ||
*/ | ||
zrUtil.each(['click', 'mousewheel', 'dblclick', 'contextmenu'], function (name) { | ||
localDOMHandlers[name] = function (event) { | ||
event = normalizeEvent(this.dom, event); | ||
@@ -245,52 +321,54 @@ this.trigger(name, event); | ||
/** | ||
* 为控制类实例初始化dom 事件处理函数 | ||
* DOM UI Event handlers for global page. | ||
* | ||
* @inner | ||
* @param {module:zrender/Handler} instance 控制类实例 | ||
* [Caution]: | ||
* those handlers should both support in capture phase and bubble phase! | ||
* | ||
* @this {HandlerProxy} | ||
*/ | ||
function initDomHandler(instance) { | ||
zrUtil.each(touchHandlerNames, function (name) { | ||
instance._handlers[name] = zrUtil.bind(domHandlers[name], instance); | ||
}); | ||
var globalDOMHandlers = { | ||
zrUtil.each(pointerHandlerNames, function (name) { | ||
instance._handlers[name] = zrUtil.bind(domHandlers[name], instance); | ||
}); | ||
pointermove: function (event) { | ||
// FIXME | ||
// pointermove is so sensitive that it always triggered when | ||
// tap(click) on touch screen, which affect some judgement in | ||
// upper application. So, we dont support mousemove on MS touch | ||
// device yet. | ||
if (!isPointerFromTouch(event)) { | ||
globalDOMHandlers.mousemove.call(this, event); | ||
} | ||
}, | ||
zrUtil.each(mouseHandlerNames, function (name) { | ||
instance._handlers[name] = makeMouseHandler(domHandlers[name], instance); | ||
}); | ||
pointerup: function (event) { | ||
globalDOMHandlers.mouseup.call(this, event); | ||
}, | ||
function makeMouseHandler(fn, instance) { | ||
return function () { | ||
if (instance._touching) { | ||
return; | ||
} | ||
return fn.apply(instance, arguments); | ||
}; | ||
} | ||
} | ||
mousemove: function (event) { | ||
this.trigger('mousemove', event); | ||
}, | ||
mouseup: function (event) { | ||
var pointerCaptureReleasing = this._pointerCapturing; | ||
function HandlerDomProxy(dom) { | ||
Eventful.call(this); | ||
togglePointerCapture(this, false); | ||
this.dom = dom; | ||
this.trigger('mouseup', event); | ||
/** | ||
* @private | ||
* @type {boolean} | ||
*/ | ||
this._touching = false; | ||
if (pointerCaptureReleasing) { | ||
event.zrEventControl = 'only_globalout'; | ||
this.trigger('mouseout', event); | ||
} | ||
} | ||
/** | ||
* @private | ||
* @type {number} | ||
*/ | ||
this._touchTimer; | ||
}; | ||
this._handlers = {}; | ||
initDomHandler(this); | ||
/** | ||
* @param {HandlerProxy} instance | ||
* @param {DOMHandlerScope} scope | ||
*/ | ||
function mountLocalDOMEventListeners(instance, scope) { | ||
var domHandlers = scope.domHandlers; | ||
@@ -304,3 +382,8 @@ if (env.pointerEventsSupported) { // Only IE11+/Edge | ||
// So we use pointer event to both detect touch gesture and mouse behavior. | ||
mountHandlers(pointerHandlerNames, this); | ||
zrUtil.each(localNativeListenerNames.pointer, function (nativeEventName) { | ||
mountSingleDOMEventListener(scope, nativeEventName, function (event) { | ||
// markTriggeredFromLocal(event); | ||
domHandlers[nativeEventName].call(instance, event); | ||
}); | ||
}); | ||
@@ -324,3 +407,9 @@ // FIXME | ||
if (env.touchEventsSupported) { | ||
mountHandlers(touchHandlerNames, this); | ||
zrUtil.each(localNativeListenerNames.touch, function (nativeEventName) { | ||
mountSingleDOMEventListener(scope, nativeEventName, function (event) { | ||
// markTriggeredFromLocal(event); | ||
domHandlers[nativeEventName].call(instance, event); | ||
setTouchTimer(scope); | ||
}); | ||
}); | ||
// Handler of 'mouseout' event is needed in touch mode, which will be mounted below. | ||
@@ -335,19 +424,141 @@ // addEventListener(root, 'mouseout', this._mouseoutHandler); | ||
// mouseevent after touch event triggered, see `setTouchTimer`. | ||
mountHandlers(mouseHandlerNames, this); | ||
zrUtil.each(localNativeListenerNames.mouse, function (nativeEventName) { | ||
mountSingleDOMEventListener(scope, nativeEventName, function (event) { | ||
event = getNativeEvent(event); | ||
if (!scope.touching) { | ||
// markTriggeredFromLocal(event); | ||
domHandlers[nativeEventName].call(instance, event); | ||
} | ||
}); | ||
}); | ||
} | ||
} | ||
function mountHandlers(handlerNames, instance) { | ||
zrUtil.each(handlerNames, function (name) { | ||
addEventListener(dom, eventNameFix(name), instance._handlers[name]); | ||
}, instance); | ||
/** | ||
* @param {HandlerProxy} instance | ||
* @param {DOMHandlerScope} scope | ||
*/ | ||
function mountGlobalDOMEventListeners(instance, scope) { | ||
// Only IE11+/Edge. See the comment in `mountLocalDOMEventListeners`. | ||
if (env.pointerEventsSupported) { | ||
zrUtil.each(globalNativeListenerNames.pointer, mount); | ||
} | ||
// Touch event has implemented "drag outside" so we do not mount global listener for touch event. | ||
// (see https://www.w3.org/TR/touch-events/#the-touchmove-event) | ||
// We do not consider "both-support-touch-and-mouse device" for this feature (see the comment of | ||
// `mountLocalDOMEventListeners`) to avoid bugs util some requirements come. | ||
else if (!env.touchEventsSupported) { | ||
zrUtil.each(globalNativeListenerNames.mouse, mount); | ||
} | ||
function mount(nativeEventName) { | ||
function nativeEventListener(event) { | ||
event = getNativeEvent(event); | ||
// See the reason in [Drag outside] in `Handler.js` | ||
// This checking supports both `useCapture` or not. | ||
// PENDING: if there is performance issue in some devices, | ||
// we probably can not use `useCapture` and change a easier | ||
// to judes whether local (mark). | ||
if (!isLocalEl(instance, event.target)) { | ||
event = normalizeGlobalEvent(instance, event); | ||
scope.domHandlers[nativeEventName].call(instance, event); | ||
} | ||
} | ||
mountSingleDOMEventListener( | ||
scope, nativeEventName, nativeEventListener, | ||
{capture: true} // See [Drag Outside] in `Handler.js` | ||
); | ||
} | ||
} | ||
function mountSingleDOMEventListener(scope, nativeEventName, listener, opt) { | ||
scope.mounted[nativeEventName] = listener; | ||
scope.listenerOpts[nativeEventName] = opt; | ||
addEventListener(scope.domTarget, eventNameFix(nativeEventName), listener, opt); | ||
} | ||
function unmountDOMEventListeners(scope) { | ||
var mounted = scope.mounted; | ||
for (var nativeEventName in mounted) { | ||
if (mounted.hasOwnProperty(nativeEventName)) { | ||
removeEventListener( | ||
scope.domTarget, eventNameFix(nativeEventName), mounted[nativeEventName], | ||
scope.listenerOpts[nativeEventName] | ||
); | ||
} | ||
} | ||
scope.mounted = {}; | ||
} | ||
/** | ||
* See [Drag Outside] in `Handler.js`. | ||
* @implement | ||
* @param {boolean} isPointerCapturing Should never be `null`/`undefined`. | ||
* `true`: start to capture pointer if it is not capturing. | ||
* `false`: end the capture if it is capturing. | ||
*/ | ||
function togglePointerCapture(instance, isPointerCapturing) { | ||
instance._mayPointerCapture = null; | ||
if (globalEventSupported && (instance._pointerCapturing ^ isPointerCapturing)) { | ||
instance._pointerCapturing = isPointerCapturing; | ||
var globalHandlerScope = instance._globalHandlerScope; | ||
isPointerCapturing | ||
? mountGlobalDOMEventListeners(instance, globalHandlerScope) | ||
: unmountDOMEventListeners(globalHandlerScope); | ||
} | ||
} | ||
/** | ||
* @inner | ||
* @class | ||
*/ | ||
function DOMHandlerScope(domTarget, domHandlers) { | ||
this.domTarget = domTarget; | ||
this.domHandlers = domHandlers; | ||
// Key: eventName, value: mounted handler funcitons. | ||
// Used for unmount. | ||
this.mounted = {}; | ||
this.listenerOpts = {}; | ||
this.touchTimer = null; | ||
this.touching = false; | ||
} | ||
/** | ||
* @public | ||
* @class | ||
*/ | ||
function HandlerDomProxy(dom, painterRoot) { | ||
Eventful.call(this); | ||
this.dom = dom; | ||
this.painterRoot = painterRoot; | ||
this._localHandlerScope = new DOMHandlerScope(dom, localDOMHandlers); | ||
if (globalEventSupported) { | ||
this._globalHandlerScope = new DOMHandlerScope(document, globalDOMHandlers); | ||
} | ||
/** | ||
* @type {boolean} | ||
*/ | ||
this._pointerCapturing = false; | ||
/** | ||
* @type {Array.<number>} [x, y] or null. | ||
*/ | ||
this._mayPointerCapture = null; | ||
mountLocalDOMEventListeners(this, this._localHandlerScope); | ||
} | ||
var handlerDomProxyProto = HandlerDomProxy.prototype; | ||
handlerDomProxyProto.dispose = function () { | ||
var handlerNames = mouseHandlerNames.concat(touchHandlerNames); | ||
for (var i = 0; i < handlerNames.length; i++) { | ||
var name = handlerNames[i]; | ||
removeEventListener(this.dom, eventNameFix(name), this._handlers[name]); | ||
unmountDOMEventListeners(this._localHandlerScope); | ||
if (globalEventSupported) { | ||
unmountDOMEventListeners(this._globalHandlerScope); | ||
} | ||
@@ -360,4 +571,5 @@ }; | ||
zrUtil.mixin(HandlerDomProxy, Eventful); | ||
export default HandlerDomProxy; | ||
export default HandlerDomProxy; |
@@ -8,2 +8,62 @@ import * as util from './core/util'; | ||
/** | ||
* [The interface between `Handler` and `HandlerProxy`]: | ||
* | ||
* The default `HandlerProxy` only support the common standard web environment | ||
* (e.g., standalone browser, headless browser, embed browser in mobild APP, ...). | ||
* But `HandlerProxy` can be replaced to support more non-standard environment | ||
* (e.g., mini app), or to support more feature that the default `HandlerProxy` | ||
* not provided (like echarts-gl did). | ||
* So the interface between `Handler` and `HandlerProxy` should be stable. Do not | ||
* make break changes util inevitable. The interface include the public methods | ||
* of `Handler` and the events listed in `handlerNames` below, by which `HandlerProxy` | ||
* drives `Handler`. | ||
*/ | ||
/** | ||
* [Drag outside]: | ||
* | ||
* That is, triggering `mousemove` and `mouseup` event when the pointer is out of the | ||
* zrender area when dragging. That is important for the improvement of the user experience | ||
* when dragging something near the boundary without being terminated unexpectedly. | ||
* | ||
* We originally consider to introduce new events like `pagemovemove` and `pagemouseup` | ||
* to resolve this issue. But some drawbacks of it is described in | ||
* https://github.com/ecomfe/zrender/pull/536#issuecomment-560286899 | ||
* | ||
* Instead, we referenced the specifications: | ||
* https://www.w3.org/TR/touch-events/#the-touchmove-event | ||
* https://www.w3.org/TR/2014/WD-DOM-Level-3-Events-20140925/#event-type-mousemove | ||
* where the the mousemove/touchmove can be continue to fire if the user began a drag | ||
* operation and the pointer has left the boundary. (for the mouse event, browsers | ||
* only do it on `document` and when the pointer has left the boundary of the browser.) | ||
* | ||
* So the default `HandlerProxy` supports this feature similarly: if it is in the dragging | ||
* state (see `pointerCapture` in `HandlerProxy`), the `mousemove` and `mouseup` continue | ||
* to fire until release the pointer. That is implemented by listen to those event on | ||
* `document`. | ||
* If we implement some other `HandlerProxy` only for touch device, that would be easier. | ||
* The touch event support this feature by default. | ||
* | ||
* Note: | ||
* There might be some cases that the mouse event can not be | ||
* received on `document`. For example, | ||
* (A) `useCapture` is not supported and some user defined event listeners on the ancestor | ||
* of zr dom throw Error . | ||
* (B) `useCapture` is not supported Some user defined event listeners on the ancestor of | ||
* zr dom call `stopPropagation`. | ||
* In these cases, the `mousemove` event might be keep triggered event | ||
* if the mouse is released. We try to reduce the side-effect in those cases. | ||
* That is, do nothing (especially, `findHover`) in those cases. See `isOutsideBoundary`. | ||
* | ||
* Note: | ||
* If `HandlerProxy` listens to `document` with `useCapture`, `HandlerProxy` needs to | ||
* make sure `stopPropagation` and `preventDefault` doing nothing if and only if the event | ||
* target is not zrender dom. Becuase it is dangerous to enable users to call them in | ||
* `document` capture phase to prevent the propagation to any listener of the webpage. | ||
* But they are needed to work when the pointer inside the zrender dom. | ||
*/ | ||
var SILENT = 'silent'; | ||
@@ -33,3 +93,3 @@ | ||
function stopEvent(event) { | ||
function stopEvent() { | ||
eventTool.stop(this.event); | ||
@@ -41,2 +101,3 @@ } | ||
var handlerNames = [ | ||
@@ -46,2 +107,3 @@ 'click', 'dblclick', 'mousewheel', 'mouseout', | ||
]; | ||
/** | ||
@@ -103,3 +165,2 @@ * @alias module:zrender/Handler | ||
Draggable.call(this); | ||
@@ -133,2 +194,4 @@ | ||
var isOutside = isOutsideBoundary(this, x, y); | ||
var lastHovered = this._hovered; | ||
@@ -146,3 +209,3 @@ var lastHoveredTarget = lastHovered.target; | ||
var hovered = this._hovered = this.findHover(x, y); | ||
var hovered = this._hovered = isOutside ? {x: x, y: y} : this.findHover(x, y); | ||
var hoveredTarget = hovered.target; | ||
@@ -168,19 +231,14 @@ | ||
mouseout: function (event) { | ||
this.dispatchToElement(this._hovered, 'mouseout', event); | ||
var eventControl = event.zrEventControl; | ||
var zrIsToLocalDOM = event.zrIsToLocalDOM; | ||
// There might be some doms created by upper layer application | ||
// at the same level of painter.getViewportRoot() (e.g., tooltip | ||
// dom created by echarts), where 'globalout' event should not | ||
// be triggered when mouse enters these doms. (But 'mouseout' | ||
// should be triggered at the original hovered element as usual). | ||
var element = event.toElement || event.relatedTarget; | ||
var innerDom; | ||
do { | ||
element = element && element.parentNode; | ||
if (eventControl !== 'only_globalout') { | ||
this.dispatchToElement(this._hovered, 'mouseout', event); | ||
} | ||
while (element && element.nodeType !== 9 && !( | ||
innerDom = element === this.painterRoot | ||
)); | ||
!innerDom && this.trigger('globalout', {event: event}); | ||
if (eventControl !== 'no_globalout') { | ||
// FIXME: if the pointer moving from the extra doms to realy "outside", | ||
// the `globalout` should have been triggered. But currently not. | ||
!zrIsToLocalDOM && this.trigger('globalout', {type: 'globalout', event: event}); | ||
} | ||
}, | ||
@@ -331,6 +389,15 @@ | ||
Handler.prototype[name] = function (event) { | ||
// Find hover again to avoid click event is dispatched manually. Or click is triggered without mouseover | ||
var hovered = this.findHover(event.zrX, event.zrY); | ||
var hoveredTarget = hovered.target; | ||
var x = event.zrX; | ||
var y = event.zrY; | ||
var isOutside = isOutsideBoundary(this, x, y); | ||
var hovered; | ||
var hoveredTarget; | ||
if (name !== 'mouseup' || !isOutside) { | ||
// Find hover again to avoid click event is dispatched manually. Or click is triggered without mouseover | ||
hovered = this.findHover(x, y); | ||
hoveredTarget = hovered.target; | ||
} | ||
if (name === 'mousedown') { | ||
@@ -386,2 +453,10 @@ this._downEl = hoveredTarget; | ||
/** | ||
* See [Drag outside]. | ||
*/ | ||
function isOutsideBoundary(handlerInstance, x, y) { | ||
var painter = handlerInstance.painter; | ||
return x < 0 || x > painter.getWidth() || y < 0 || y > painter.getHeight(); | ||
} | ||
util.mixin(Handler, Eventful); | ||
@@ -388,0 +463,0 @@ util.mixin(Handler, Draggable); |
@@ -8,3 +8,7 @@ // TODO Draggable for group | ||
this.on('mouseup', this._dragEnd, this); | ||
this.on('globalout', this._dragEnd, this); | ||
// `mosuemove` and `mouseup` can be continue to fire when dragging. | ||
// See [Drag outside] in `Handler.js`. So we do not need to trigger | ||
// `_dragEnd` when globalout. That would brings better user experience. | ||
// this.on('globalout', this._dragEnd, this); | ||
// this._dropTarget = null; | ||
@@ -11,0 +15,0 @@ // this._draggingTarget = null; |
@@ -25,3 +25,3 @@ /** | ||
* return: {boolean} | ||
* @param {Function} [eventProcessor.afterTrigger] Call after all handlers called. | ||
* @param {Function} [eventProcessor.afterTrigger] Called after all handlers called. | ||
* param: {string} eventType | ||
@@ -76,4 +76,6 @@ */ | ||
* | ||
* @param {string} event The event name. | ||
* @param {string} [event] The event name. | ||
* If no `event` input, "off" all listeners. | ||
* @param {Function} [handler] The event handler. | ||
* If no `handler` input, "off" all listeners of the `event`. | ||
*/ | ||
@@ -236,2 +238,3 @@ off: function (event, handler) { | ||
function normalizeQuery(host, query) { | ||
@@ -238,0 +241,0 @@ var eventProcessor = host._$eventProcessor; |
@@ -257,2 +257,5 @@ // TODO | ||
} | ||
else { | ||
removeOldTextNode(el); | ||
} | ||
}; | ||
@@ -307,2 +310,5 @@ | ||
} | ||
else { | ||
removeOldTextNode(el); | ||
} | ||
}; | ||
@@ -509,2 +515,14 @@ | ||
function removeOldTextNode(el) { | ||
if (el && el.__textSvgEl) { | ||
// textSvgEl may has no parentNode if el has been removed temporary. | ||
if (el.__textSvgEl.parentNode) { | ||
el.__textSvgEl.parentNode.removeChild(el.__textSvgEl); | ||
} | ||
el.__textSvgEl = null; | ||
el.__tspanList = []; | ||
el.__text = null; | ||
} | ||
} | ||
svgText.drawRectText = svgTextDrawRectText; | ||
@@ -517,2 +535,5 @@ | ||
} | ||
else { | ||
removeOldTextNode(el); | ||
} | ||
}; |
@@ -246,4 +246,4 @@ /** | ||
var displayable = newVisibleList[item.indices[k]]; | ||
prevSvgElement = getTextSvgElement(displayable) | ||
|| getSvgElement(displayable) || prevSvgElement; | ||
var svgElement = getSvgElement(displayable); | ||
var textSvgElement = getTextSvgElement(displayable); | ||
@@ -262,2 +262,8 @@ var svgElement = getSvgElement(displayable); | ||
this.clipPathManager.markUsed(displayable); | ||
if (textSvgElement) { // Insert text. | ||
insertAfter(svgRoot, textSvgElement, svgElement); | ||
} | ||
prevSvgElement = svgElement | ||
|| textSvgElement || prevSvgElement; | ||
} | ||
@@ -264,0 +270,0 @@ } |
@@ -31,3 +31,3 @@ /*! | ||
*/ | ||
export var version = '4.1.2'; | ||
export var version = '4.2.0'; | ||
@@ -134,3 +134,3 @@ /** | ||
var handerProxy = (!env.node && !env.worker) ? new HandlerProxy(painter.getViewportRoot()) : null; | ||
var handerProxy = (!env.node && !env.worker) ? new HandlerProxy(painter.getViewportRoot(), painter.root) : null; | ||
this.handler = new Handler(storage, painter, handerProxy, painter.root); | ||
@@ -137,0 +137,0 @@ |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
3410523
66763