@qooxdoo/framework
Version:
The JS Framework for Coders
567 lines (444 loc) • 15.9 kB
JavaScript
/* ************************************************************************
qooxdoo - the new era of web development
http://qooxdoo.org
Copyright:
2004-2008 1&1 Internet AG, Germany, http://www.1und1.de
License:
MIT: https://opensource.org/licenses/MIT
See the LICENSE file in the project's top-level directory for details.
Authors:
* Sebastian Werner (wpbasti)
* Andreas Ecker (ecker)
* Fabian Jakobs (fjakobs)
* Christian Hagendorn (chris_schmidt)
************************************************************************ */
/**
* This class provides an unified mouse event handler for Internet Explorer,
* Firefox, Opera and Safari
*
* NOTE: Instances of this class must be disposed of after use
*
* @require(qx.event.handler.UserAction)
* @ignore(qx.event.handler.DragDrop)
*/
qx.Class.define("qx.event.handler.Mouse",
{
extend : qx.core.Object,
implement : [ qx.event.IEventHandler, qx.core.IDisposable ],
/*
*****************************************************************************
CONSTRUCTOR
*****************************************************************************
*/
/**
* Create a new instance
*
* @param manager {qx.event.Manager} Event manager for the window to use
*/
construct : function(manager)
{
this.base(arguments);
// Define shorthands
this.__manager = manager;
this.__window = manager.getWindow();
this.__root = this.__window.document;
// Initialize observers
this._initButtonObserver();
this._initMoveObserver();
this._initWheelObserver();
},
/*
*****************************************************************************
STATICS
*****************************************************************************
*/
statics :
{
/** @type {Integer} Priority of this handler */
PRIORITY : qx.event.Registration.PRIORITY_NORMAL,
/** @type {Map} Supported event types */
SUPPORTED_TYPES :
{
mousemove : 1,
mouseover : 1,
mouseout : 1,
mousedown : 1,
mouseup : 1,
click : 1,
auxclick : 1,
dblclick : 1,
contextmenu : 1,
mousewheel : 1
},
/** @type {Integer} Which target check to use */
TARGET_CHECK : qx.event.IEventHandler.TARGET_DOMNODE + qx.event.IEventHandler.TARGET_DOCUMENT + qx.event.IEventHandler.TARGET_WINDOW,
/** @type {Integer} Whether the method "canHandleEvent" must be called */
IGNORE_CAN_HANDLE : true
},
/*
*****************************************************************************
MEMBERS
*****************************************************************************
*/
members :
{
__onButtonEventWrapper : null,
__onMoveEventWrapper : null,
__onWheelEventWrapper : null,
__lastEventType : null,
__lastMouseDownTarget : null,
__manager : null,
__window : null,
__root : null,
__preventNextClick: null,
/*
---------------------------------------------------------------------------
EVENT HANDLER INTERFACE
---------------------------------------------------------------------------
*/
// interface implementation
canHandleEvent : function(target, type) {},
// interface implementation
// The iPhone requires for attaching mouse events natively to every element which
// should react on mouse events. As of version 3.0 it also requires to keep the
// listeners as long as the event should work. In 2.0 it was enough to attach the
// listener once.
registerEvent : qx.core.Environment.get("os.name") === "ios" ?
function(target, type, capture) {
target["on" + type] = (function() {return null;});
} : (function() {return null;}),
// interface implementation
unregisterEvent : qx.core.Environment.get("os.name") === "ios" ?
function(target, type, capture) {
target["on" + type] = undefined;
} : (function() {return null;}),
/*
---------------------------------------------------------------------------
HELPER
---------------------------------------------------------------------------
*/
/**
* Fire a mouse event with the given parameters
*
* @param domEvent {Event} DOM event
* @param type {String} type of the event
* @param target {Element} event target
*/
__fireEvent : function(domEvent, type, target)
{
if (!target) {
target = qx.bom.Event.getTarget(domEvent);
}
// we need a true node for the fireEvent
// e.g. when hovering over text of disabled textfields IE is returning
// an empty object as "srcElement"
if (target && target.nodeType)
{
qx.event.Registration.fireEvent(
target,
type||domEvent.type,
type == "mousewheel" ? qx.event.type.MouseWheel : qx.event.type.Mouse,
[domEvent, target, null, true, true]
);
}
// Fire user action event
qx.event.Registration.fireEvent(this.__window, "useraction", qx.event.type.Data, [type||domEvent.type]);
},
/**
* Helper to prevent the next click.
* @internal
*/
preventNextClick : function() {
this.__preventNextClick = true;
},
/*
---------------------------------------------------------------------------
OBSERVER INIT
---------------------------------------------------------------------------
*/
/**
* Initializes the native mouse button event listeners.
*
* @signature function()
*/
_initButtonObserver : function()
{
this.__onButtonEventWrapper = qx.lang.Function.listener(this._onButtonEvent, this);
var Event = qx.bom.Event;
Event.addNativeListener(this.__root, "mousedown", this.__onButtonEventWrapper);
Event.addNativeListener(this.__root, "mouseup", this.__onButtonEventWrapper);
Event.addNativeListener(this.__root, "click", this.__onButtonEventWrapper);
Event.addNativeListener(this.__root, "auxclick", this.__onButtonEventWrapper);
Event.addNativeListener(this.__root, "dblclick", this.__onButtonEventWrapper);
Event.addNativeListener(this.__root, "contextmenu", this.__onButtonEventWrapper);
},
/**
* Initializes the native mouse move event listeners.
*
* @signature function()
*/
_initMoveObserver : function()
{
this.__onMoveEventWrapper = qx.lang.Function.listener(this._onMoveEvent, this);
var Event = qx.bom.Event;
Event.addNativeListener(this.__root, "mousemove", this.__onMoveEventWrapper);
Event.addNativeListener(this.__root, "mouseover", this.__onMoveEventWrapper);
Event.addNativeListener(this.__root, "mouseout", this.__onMoveEventWrapper);
},
/**
* Initializes the native mouse wheel event listeners.
*
* @signature function()
*/
_initWheelObserver : function()
{
this.__onWheelEventWrapper = qx.lang.Function.listener(this._onWheelEvent, this);
var data = qx.bom.client.Event.getMouseWheel(this.__window);
qx.bom.Event.addNativeListener(
data.target, data.type, this.__onWheelEventWrapper
);
},
/*
---------------------------------------------------------------------------
OBSERVER STOP
---------------------------------------------------------------------------
*/
/**
* Disconnects the native mouse button event listeners.
*
* @signature function()
*/
_stopButtonObserver : function()
{
var Event = qx.bom.Event;
Event.removeNativeListener(this.__root, "mousedown", this.__onButtonEventWrapper);
Event.removeNativeListener(this.__root, "mouseup", this.__onButtonEventWrapper);
Event.removeNativeListener(this.__root, "click", this.__onButtonEventWrapper);
Event.removeNativeListener(this.__root, "dblclick", this.__onButtonEventWrapper);
Event.removeNativeListener(this.__root, "contextmenu", this.__onButtonEventWrapper);
},
/**
* Disconnects the native mouse move event listeners.
*
* @signature function()
*/
_stopMoveObserver : function()
{
var Event = qx.bom.Event;
Event.removeNativeListener(this.__root, "mousemove", this.__onMoveEventWrapper);
Event.removeNativeListener(this.__root, "mouseover", this.__onMoveEventWrapper);
Event.removeNativeListener(this.__root, "mouseout", this.__onMoveEventWrapper);
},
/**
* Disconnects the native mouse wheel event listeners.
*
* @signature function()
*/
_stopWheelObserver : function()
{
var data = qx.bom.client.Event.getMouseWheel(this.__window);
qx.bom.Event.removeNativeListener(
data.target, data.type, this.__onWheelEventWrapper
);
},
/*
---------------------------------------------------------------------------
NATIVE EVENT OBSERVERS
---------------------------------------------------------------------------
*/
/**
* Global handler for all mouse move related events like "mousemove",
* "mouseout" and "mouseover".
*
* @signature function(domEvent)
* @param domEvent {Event} DOM event
*/
_onMoveEvent : qx.event.GlobalError.observeMethod(function(domEvent) {
this.__fireEvent(domEvent);
}),
/**
* Global handler for all mouse button related events like "mouseup",
* "mousedown", "click", "dblclick" and "contextmenu".
*
* @signature function(domEvent)
* @param domEvent {Event} DOM event
*/
_onButtonEvent : qx.event.GlobalError.observeMethod(function(domEvent)
{
var type = domEvent.type;
var target = qx.bom.Event.getTarget(domEvent);
if (type == "click" && this.__preventNextClick) {
delete this.__preventNextClick;
return;
}
// Safari (and maybe gecko) takes text nodes as targets for events
// See: http://www.quirksmode.org/js/events_properties.html
if (
qx.core.Environment.get("engine.name") == "gecko" ||
qx.core.Environment.get("engine.name") == "webkit"
) {
if (target && target.nodeType == 3) {
target = target.parentNode;
}
}
// prevent click events on drop during Drag&Drop [BUG #6846]
var isDrag = qx.event.handler.DragDrop &&
this.__manager.getHandler(qx.event.handler.DragDrop).isSessionActive();
if (isDrag && type == "click") {
return;
}
if (this.__doubleClickFixPre) {
this.__doubleClickFixPre(domEvent, type, target);
}
this.__fireEvent(domEvent, type, target);
/*
* In order to normalize middle button click events we
* need to fire an artificial click event if the client
* fires auxclick events for non primary buttons instead.
*
* See https://github.com/qooxdoo/qooxdoo/issues/9268
*/
if (type == "auxclick" && domEvent.button == 1) {
this.__fireEvent(domEvent, "click", target);
}
if (this.__rightClickFixPost) {
this.__rightClickFixPost(domEvent, type, target);
}
if (this.__differentTargetClickFixPost && !isDrag) {
this.__differentTargetClickFixPost(domEvent, type, target);
}
this.__lastEventType = type;
}),
/**
* Global handler for the mouse wheel event.
*
* @signature function(domEvent)
* @param domEvent {Event} DOM event
*/
_onWheelEvent : qx.event.GlobalError.observeMethod(function(domEvent) {
this.__fireEvent(domEvent, "mousewheel");
}),
/*
---------------------------------------------------------------------------
CROSS BROWSER SUPPORT FIXES
---------------------------------------------------------------------------
*/
/**
* Normalizes the click sequence of right click events in Webkit and Opera.
* The normalized sequence is:
*
* 1. mousedown <- not fired by Webkit
* 2. mouseup <- not fired by Webkit
* 3. contextmenu <- not fired by Opera
*
* @param domEvent {Event} original DOM event
* @param type {String} event type
* @param target {Element} event target of the DOM event.
*
* @signature function(domEvent, type, target)
*/
__rightClickFixPost : qx.core.Environment.select("engine.name",
{
"opera" : function(domEvent, type, target)
{
if (type =="mouseup" && domEvent.button == 2) {
this.__fireEvent(domEvent, "contextmenu", target);
}
},
"default" : null
}),
/**
* Normalizes the click sequence of double click event in the Internet
* Explorer. The normalized sequence is:
*
* 1. mousedown
* 2. mouseup
* 3. click
* 4. mousedown <- not fired by IE
* 5. mouseup
* 6. click <- not fired by IE
* 7. dblclick
*
* Note: This fix is only applied, when the IE event model is used, otherwise
* the fix is ignored.
*
* @param domEvent {Event} original DOM event
* @param type {String} event type
* @param target {Element} event target of the DOM event.
*
* @signature function(domEvent, type, target)
*/
__doubleClickFixPre : qx.core.Environment.select("engine.name",
{
"mshtml" : function(domEvent, type, target)
{
// Do only apply the fix when the event is from the IE event model,
// otherwise do not apply the fix.
if (domEvent.target !== undefined) {
return;
}
if (type == "mouseup" && this.__lastEventType == "click") {
this.__fireEvent(domEvent, "mousedown", target);
} else if (type == "dblclick") {
this.__fireEvent(domEvent, "click", target);
}
},
"default" : null
}),
/**
* If the mouseup event happens on a different target than the corresponding
* mousedown event the internet explorer dispatches a click event on the
* first common ancestor of both targets. The presence of this click event
* is essential for the qooxdoo widget system. All other browsers don't fire
* the click event so it must be emulated.
*
* @param domEvent {Event} original DOM event
* @param type {String} event type
* @param target {Element} event target of the DOM event.
*
* @signature function(domEvent, type, target)
*/
__differentTargetClickFixPost : qx.core.Environment.select("engine.name",
{
"mshtml" : null,
"default" : function(domEvent, type, target)
{
switch (type)
{
case "mousedown":
this.__lastMouseDownTarget = target;
break;
case "mouseup":
if (target !== this.__lastMouseDownTarget)
{
var commonParent = qx.dom.Hierarchy.getCommonParent(target, this.__lastMouseDownTarget);
if (commonParent) {
this.__fireEvent(domEvent, "click", commonParent);
}
}
}
}
})
},
/*
*****************************************************************************
DESTRUCTOR
*****************************************************************************
*/
destruct : function()
{
this._stopButtonObserver();
this._stopMoveObserver();
this._stopWheelObserver();
this.__manager = this.__window = this.__root =
this.__lastMouseDownTarget = null;
},
/*
*****************************************************************************
DEFER
*****************************************************************************
*/
defer : function(statics) {
qx.event.Registration.addHandler(statics);
}
});