From d12e77c56d8cbb6fa218dba222045cb86efe6ea3 Mon Sep 17 00:00:00 2001 From: Nick Stakenburg Date: Wed, 15 Oct 2008 18:12:06 +0200 Subject: [PATCH] Fixed event.js so more unit tests pass on IE. Changed the use of '_' prefix to match other parts of the rewrite branch. --- src/dom/event.js | 389 ++++++++++++++++++++++------------------------- 1 file changed, 178 insertions(+), 211 deletions(-) diff --git a/src/dom/event.js b/src/dom/event.js index 5a0a5f2..20a47df 100644 --- a/src/dom/event.js +++ b/src/dom/event.js @@ -1,10 +1,5 @@ (function() { - var IE_EVENT_SYSTEM = (window.attachEvent && !window.opera); - - var Event = {}; - - // Define keycode constants. - Object.extend(Event, { + var Event = { KEY_BACKSPACE: 8, KEY_TAB: 9, KEY_RETURN: 13, @@ -18,355 +13,327 @@ KEY_END: 35, KEY_PAGEUP: 33, KEY_PAGEDOWN: 34, - KEY_INSERT: 45 - }); - - Event.cache = {}; - - function _isButton(event, code) { - switch (code) { - case 0: return event.which == 1 && !event.metaKey; - case 1: return event.which == 1 && event.metakey; - default: return false; - } - } - - if (window.attachEvent && !window.opera) { - // IE's event system doesn't map left/right/middle the same way. + KEY_INSERT: 45, + + cache: {} + }; + + var _isButton; + if (Prototype.Browser.IE) { + // IE doesn't map left/right/middle the same way. var buttonMap = { 0: 1, 1: 4, 2: 2 }; _isButton = function(event, code) { return event.button === buttonMap[code]; }; - } else if (Prototype.Browser.Safari) { + } else if (Prototype.Browser.WebKit) { // In Safari we have to account for when the user holds down // the "meta" key. _isButton = function(event, code) { switch (code) { - case 0: return event.which == 1 && !event.metaKey; - case 1: return event.which == 1 && event.metakey; + case 0: return event.which == 1 && !event.metaKey; + case 1: return event.which == 1 && event.metaKey; default: return false; } }; - } + } else { + _isButton = function(event, code) { + return event.which ? (event.which === code + 1) : (event.button === code); + }; + } - function _isLeftClick(event) { return _isButton(event, 0); } - function _isRightClick(event) { return _isButton(event, 1); } - function _isMiddleClick(event) { return _isButton(event, 2); } - - function _element(event) { + function isLeftClick(event) { return _isButton(event, 0) } + function isMiddleClick(event) { return _isButton(event, 1) } + function isRightClick(event) { return _isButton(event, 2) } + + function element(event) { event = Event.extend(event); - - var node = event.target, type = event.type, + + var node = event.target, type = event.type, currentTarget = event.currentTarget; - + if (currentTarget && currentTarget.tagName) { // Firefox screws up the "click" event when moving between radio buttons // via arrow keys. It also screws up the "load" and "error" events on images, // reporting the document as the target instead of the original image. - if (type === 'click' && currentTarget.tagName.toLowerCase() === 'input' - && currentTarget.type === 'radio') - node = currentTarget; - - if (type === 'load' || type === 'error') - node = currentTarget; + if (type === 'load' || type === 'error' || + (type === 'click' && currentTarget.tagName.toLowerCase() === 'input' + && currentTarget.type === 'radio')) + node = currentTarget; } - + // Fix a Safari bug where a text node gets passed as the target of an // anchor click rather than the anchor itself. if (node.nodeType == Node.TEXT_NODE) node = node.parentNode; - + return Element.extend(node); } - - function _findElement(event, expression) { + + function findElement(event, expression) { var element = Event.element(event); if (!expression) return element; var elements = [element].concat(element.ancestors()); return Selector.findElement(elements, expression, 0); } - - function _pointer(event) { + + function pointer(event) { var docElement = document.documentElement, body = document.body || { scrollLeft: 0, scrollTop: 0 }; - - return { - x: event.pageX || (event.clientX + - (docElement.scrollLeft || body.scrollLeft) - - (docElement.clientLeft || 0)), - y: event.pageY || (event.clientY + - (docElement.scrollTop || body.scrollTop) - - (docElement.clientTop || 0)) - }; + return { + x: event.pageX || (event.clientX + + (docElement.scrollLeft || body.scrollLeft) - + (docElement.clientLeft || 0)), + y: event.pageY || (event.clientY + + (docElement.scrollTop || body.scrollTop) - + (docElement.clientTop || 0)) + }; } - - function _pointerX(event) { return _pointer(event).x; } - function _pointerY(event) { return _pointer(event).y; } - - function _stop(event) { + + function pointerX(event) { return Event.pointer(event).x } + function pointerY(event) { return Event.pointer(event).y } + + function stop(event) { Event.extend(event); event.preventDefault(); event.stopPropagation(); - + // Set a "stopped" property so that a custom event can be inspected // after the fact to determine whether or not it was stopped. event.stopped = true; } - - // Simulates the relatedTarget property in IE. - function _relatedTarget(event) { - var element; - switch(event.type) { - case 'mouseover': element = event.fromElement; break; - case 'mouseout': element = event.toElement; break; - default: return null; - } - return Element.extend(element); - } - - + Event.Methods = { - pointer: _pointer, - pointerX: _pointerX, - pointerY: _pointerY, - - element: _element, - findElement: _findElement, - - isLeftClick: _isLeftClick, - isRightClick: _isRightClick, - isMiddleClick: _isMiddleClick, - - stop: _stop + isLeftClick: isLeftClick, + isMiddleClick: isMiddleClick, + isRightClick: isRightClick, + + element: element, + findElement: findElement, + + pointer: pointer, + pointerX: pointerX, + pointerY: pointerY, + + stop: stop }; - - + + // Compile the list of methods that get extended onto Events. - var _methods = Object.keys(Event.Methods).inject({ }, function(m, name) { + var methods = Object.keys(Event.Methods).inject({ }, function(m, name) { m[name] = Event.Methods[name].methodize(); return m; }); - - if (IE_EVENT_SYSTEM) { - Object.extend(_methods, { - stopPropagation: function() { this.cancelBubble = true; }, - preventDefault: function() { this.returnValue = false; }, - inspect: function() { return "[object Event]"; } + + if (Prototype.Browser.IE) { + function _relatedTarget(event) { + var element; + switch (event.type) { + case 'mouseover': element = event.fromElement; break; + case 'mouseout': element = event.toElement; break; + default: return null; + } + return Element.extend(element); + } + + Object.extend(methods, { + stopPropagation: function() { this.cancelBubble = true }, + preventDefault: function() { this.returnValue = false }, + inspect: function() { return '[object Event]' } }); - } - - // IE's method for extending events. - function _extend(event) { - if (!event) return false; - if (event._extendedByPrototype) return event; - - event._extendedByPrototype = Prototype.emptyFunction; - var pointer = _pointer(event); - Object.extend(event, { - target: event.srcElement, - relatedTarget: _relatedTarget(event), - pageX: pointer.x, - pageY: pointer.y - }); - - return Object.extend(event, _methods); - } - - if (IE_EVENT_SYSTEM) { - Event.extend = _extend; + + // IE's method for extending events. + Event.extend = function(event) { + if (!event) return false; + if (event._extendedByPrototype) return event; + + event._extendedByPrototype = Prototype.emptyFunction; + var pointer = Event.pointer(event); + Object.extend(event, { + target: event.srcElement, + relatedTarget: _relatedTarget(event), + pageX: pointer.x, + pageY: pointer.y + }); + return Object.extend(event, methods); + }; } else { - Event.prototype = window.Event.prototype || document.createEvent("HTMLEvents")['__proto__']; - Object.extend(Event.prototype, _methods); + Event.prototype = window.Event.prototype || document.createEvent('HTMLEvents').__proto__; + Object.extend(Event.prototype, methods); Event.extend = Prototype.K; } - - + function _getEventID(element) { if (element._prototypeEventID) return element._prototypeEventID[0]; return element._prototypeEventID = [++arguments.callee.id]; } - _getEventID.id = 1; - + function _getDOMEventName(eventName) { - if (eventName && eventName.include(':')) return "dataavailable"; + if (eventName && eventName.include(':')) return 'dataavailable'; return eventName; } - + function _getCacheForID(id) { return Event.cache[id] = Event.cache[id] || { }; } - + function _getRespondersForEvent(id, eventName) { var c = _getCacheForID(id); return c[eventName] = c[eventName] || []; } - + function _createResponder(element, eventName, handler) { var id = _getEventID(element), r = _getRespondersForEvent(id, eventName); - + // Work around the issue that permits a handler to be attached more than // once to the same element & event type. if (r.pluck('handler').include(handler)) return false; - + var responder = function(event) { - if (!Event || !Event.extend) return false; + if (!Event || !Event.extend || // If it's a custom event, but not the _correct_ custom event, ignore it. - if (!Object.isUndefined(event.eventName) && - event.eventName !== eventName) + (!Object.isUndefined(event.eventName) && event.eventName !== eventName)) return false; - + Event.extend(event); handler.call(element, event); }; - + responder.handler = handler; r.push(responder); return responder; } - + function _findResponder(id, eventName, handler) { var r = _getRespondersForEvent(id, eventName); - - return r.find( function(responder) { + return r.find(function(responder) { return responder.handler === handler; }); } - + function _destroyResponder(id, eventName, handler) { var c = _getCacheForID(id); if (Object.isUndefined(c[eventName])) return false; c[eventName] = c[eventName].without(_findResponder(id, eventName, handler)); } - + function _destroyCache() { for (var id in Event.cache) { - for (var eventName in cache[id]) - cache[id][eventName] = null; + for (var eventName in Event.cache[id]) + Event.cache[id][eventName] = null; } } - - + // Internet Explorer needs to remove event handlers on page unload // in order to avoid memory leaks. - if (IE_EVENT_SYSTEM) { - window.attachEvent("onunload", destroyCache); - } - + if (Prototype.Browser.IE) + window.attachEvent('onunload', _destroyCache); + // Safari needs a dummy event handler on page unload so that it won't // use its bfcache. Safari <= 3.1 has an issue with restoring the "document" // object when page is returned to via the back button using its bfcache. - if (Prototype.Browser.WebKit) { + if (Prototype.Browser.WebKit) window.addEventListener('unload', Prototype.emptyFunction, false); - } - - function _observe(element, eventName, handler) { + + + function observe(element, eventName, handler) { element = $(element); - - var name = _getDOMEventName(eventName); - - var responder = _createResponder(element, eventName, handler); + var name = _getDOMEventName(eventName), + responder = _createResponder(element, eventName, handler); + if (!responder) return element; - - if (element.addEventListener) { + + if (element.addEventListener) element.addEventListener(name, responder, false); - } else { + else element.attachEvent("on" + name, responder); - } - + return element; } - - function _stopObserving(element, eventName, handler) { + + function stopObserving(element, eventName, handler) { element = $(element); var id = _getEventID(element), name = _getDOMEventName(eventName); - + if (eventName && !handler) { // If an event name is passed without a handler, we stop observing all // handlers of that type. - _getRespondersForEvent(id, eventName).each( function(r) { + _getRespondersForEvent(id, eventName).each(function(r) { element.stopObserving(eventName, r.handler); }); return element; } else if (!eventName) { // If both the event name and the handler are omitted, we stop observing // _all_ handlers on the element. - Object.keys(_getCacheForID(id)).each( function(eventName) { + Object.keys(_getCacheForID(id)).each(function(eventName) { element.stopObserving(eventName); }); return element; } - + var responder = _findResponder(id, eventName, handler); if (!responder) return element; - - if (element.removeEventListener) { + + if (element.removeEventListener) element.removeEventListener(name, responder, false); - } else { - element.detachEvent("on" + name, responder); - } - + else + element.detachEvent('on' + name, responder); + _destroyResponder(id, eventName, handler); - + return element; } - - function _fire(element, eventName, memo) { + + function fire(element, eventName, memo) { element = $(element); - // In the W3C system, all calls to document.fire should treat - // document.documentElement as the target. if (element == document && document.createEvent && !element.dispatchEvent) element = document.documentElement; - + var event; - if (document.createEvent) { - event = document.createEvent("HTMLEvents"); - event.initEvent("dataavailable", true, true); + event = document.createEvent('HTMLEvents'); + event.initEvent('dataavailable', true, true); } else { event = document.createEventObject(); - event.eventType = "ondataavailable"; + event.eventType = 'ondataavailable'; } - + event.eventName = eventName; event.memo = memo || { }; - - if (document.createEvent) { + + if (document.createEvent) element.dispatchEvent(event); - } else { + else element.fireEvent(event.eventType, event); - } - + return Event.extend(event); } - - Object.extend(Event, { - fire: _fire, - observe: _observe, - stopObserving: _stopObserving - }); - - Element.addMethods({ - fire: _fire, - observe: _observe, - stopObserving: _stopObserving - }); - - Object.extend(document, { - fire: _fire.methodize(), - observe: _observe.methodize(), - stopObserving: _stopObserving.methodize(), - loaded: false - }); - + + Object.extend(Event, Event.Methods); + Object.extend(Event, { + fire: fire, + observe: observe, + stopObserving: stopObserving + }); + + Element.addMethods({ + fire: fire, + observe: observe, + stopObserving: stopObserving + }); + + Object.extend(document, { + fire: fire.methodize(), + observe: observe.methodize(), + stopObserving: stopObserving.methodize(), + loaded: false + }); + // Export to the global scope. - if (window.Event) { - Object.extend(window.Event, Event); - } else { - window.Event = Event; - } + if (window.Event) Object.extend(window.Event, Event) + else window.Event = Event; })(); (function() {