<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -1,348 +1,411 @@
-if (!window.Event) var Event = { };
-
-Object.extend(Event, {
-  KEY_BACKSPACE: 8,
-  KEY_TAB:       9,
-  KEY_RETURN:   13,
-  KEY_ESC:      27,
-  KEY_LEFT:     37,
-  KEY_UP:       38,
-  KEY_RIGHT:    39,
-  KEY_DOWN:     40,
-  KEY_DELETE:   46,
-  KEY_HOME:     36,
-  KEY_END:      35,
-  KEY_PAGEUP:   33,
-  KEY_PAGEDOWN: 34,
-  KEY_INSERT:   45,
-  
-  cache: { },
-
-  relatedTarget: function(event) {
-    var element;
-    switch(event.type) {
-      case 'mouseover': element = event.fromElement; break;
-      case 'mouseout':  element = event.toElement;   break;
-      default: return null;
+(function() {
+  var IE_EVENT_SYSTEM = (window.attachEvent &amp;&amp; !window.opera);
+    
+  var Event = {};
+  
+  // Define keycode constants.
+  Object.extend(Event, {
+    KEY_BACKSPACE: 8,
+    KEY_TAB:       9,
+    KEY_RETURN:   13,
+    KEY_ESC:      27,
+    KEY_LEFT:     37,
+    KEY_UP:       38,
+    KEY_RIGHT:    39,
+    KEY_DOWN:     40,
+    KEY_DELETE:   46,
+    KEY_HOME:     36,
+    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 &amp;&amp; !event.metaKey;
+      case 1:  return event.which == 1 &amp;&amp;  event.metakey;
+      default: return false;
     }
-    return Element.extend(element);
   }
-});
-
-Event.Methods = (function() {
-  var isButton;
-
-  if (Prototype.Browser.IE) {
+  
+  if (window.attachEvent &amp;&amp; !window.opera) {
+    // IE's event system 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];
+    _isButton = function(event, code) {
+      return event.button === buttonMap[code];
     };
-    
-  } else if (Prototype.Browser.WebKit) {
-    isButton = function(event, code) {
+  } else if (Prototype.Browser.Safari) {
+    // In Safari we have to account for when the user holds down
+    // the &quot;meta&quot; key.
+    _isButton = function(event, code) {
       switch (code) {
-        case 0: return event.which == 1 &amp;&amp; !event.metaKey;
-        case 1: return event.which == 1 &amp;&amp; event.metaKey;
+        case 0:  return event.which == 1 &amp;&amp; !event.metaKey;
+        case 1:  return event.which == 1 &amp;&amp;  event.metakey;
         default: return false;
       }
     };
+  }  
+
+  function _isLeftClick(event)   { return _isButton(event, 0); }
+  function _isRightClick(event)  { return _isButton(event, 1); }
+  function _isMiddleClick(event) { return _isButton(event, 2); }
+  
+  function _element(event) {
+    event = Event.extend(event);
     
-  } else {
-    isButton = function(event, code) {
-      return event.which ? (event.which === code + 1) : (event.button === code);
-    };
-  }
+    var node = event.target, type = event.type, 
+     currentTarget = event.currentTarget;
+     
+    if (currentTarget &amp;&amp; currentTarget.tagName) {
+      // Firefox screws up the &quot;click&quot; event when moving between radio buttons
+      // via arrow keys. It also screws up the &quot;load&quot; and &quot;error&quot; events on images,
+      // reporting the document as the target instead of the original image.
+      if (type === 'click' &amp;&amp; currentTarget.tagName.toLowerCase() === 'input'
+       &amp;&amp; currentTarget.type === 'radio')
+        node = currentTarget;
 
-  return {
-    isLeftClick:   function(event) { return isButton(event, 0) },
-    isMiddleClick: function(event) { return isButton(event, 1) },
-    isRightClick:  function(event) { return isButton(event, 2) },
+      if (type === 'load' || type === 'error')
+        node = currentTarget;
+    }
     
-    element: function(event) {
-      event = Event.extend(event);
+    // 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;
       
-      var node          = event.target,
-          type          = event.type,
-          currentTarget = event.currentTarget;
-      
-      if (currentTarget &amp;&amp; currentTarget.tagName) {
-        // Firefox screws up the &quot;click&quot; event when moving between radio buttons
-        // via arrow keys. It also screws up the &quot;load&quot; and &quot;error&quot; events on images,
-        // reporting the document as the target instead of the original image.
-        if (type === 'load' || type === 'error' ||
-          (type === 'click' &amp;&amp; currentTarget.tagName.toLowerCase() === 'input'
-            &amp;&amp; currentTarget.type === 'radio'))
-              node = currentTarget;
-      }
-      if (node.nodeType == Node.TEXT_NODE) node = node.parentNode;
-      return Element.extend(node);
-    },
-
-    findElement: function(event, expression) {
-      var element = Event.element(event);
-      if (!expression) return element;
-      var elements = [element].concat(element.ancestors());
-      return Selector.findElement(elements, expression, 0);
-    },
-
-    pointer: function(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))
-      };
-    },
-
-    pointerX: function(event) { return Event.pointer(event).x },
-    pointerY: function(event) { return Event.pointer(event).y },
-
-    stop: function(event) {
-      Event.extend(event);
-      event.preventDefault();
-      event.stopPropagation();
-      event.stopped = true;
+    return Element.extend(node);
+  }
+  
+  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) {
+    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))
+     };
+  }
+  
+  function _pointerX(event) { return _pointer(event).x; }
+  function _pointerY(event) { return _pointer(event).y; }
+  
+  function _stop(event) {
+    Event.extend(event);
+    event.preventDefault();
+    event.stopPropagation();
+    
+    // Set a &quot;stopped&quot; 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
   };
-})();
-
-Event.extend = (function() {
-  var methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
+  
+  
+  // Compile the list of methods that get extended onto Events.
+  var _methods = Object.keys(Event.Methods).inject({ }, function(m, name) {
     m[name] = Event.Methods[name].methodize();
     return m;
   });
   
-  if (Prototype.Browser.IE) {
-    Object.extend(methods, {
-      stopPropagation: function() { this.cancelBubble = true },
-      preventDefault:  function() { this.returnValue = false },
-      inspect: function() { return &quot;[object Event]&quot; }
+  if (IE_EVENT_SYSTEM) {
+    Object.extend(_methods, {
+      stopPropagation: function() { this.cancelBubble = true;  },
+      preventDefault:  function() { this.returnValue  = false; },
+      inspect: function() { return &quot;[object Event]&quot;; }
+    });
+  }
+  
+  // 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 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: Event.relatedTarget(event),
-        pageX:  pointer.x,
-        pageY:  pointer.y
-      });
-      return Object.extend(event, methods);
-    };
     
+    return Object.extend(event, _methods);
+  }
+  
+  if (IE_EVENT_SYSTEM) {
+    Event.extend = _extend;
   } else {
-    Event.prototype = Event.prototype || document.createEvent(&quot;HTMLEvents&quot;)['__proto__'];
-    Object.extend(Event.prototype, methods);
-    return Prototype.K;
+    Event.prototype = window.Event.prototype || document.createEvent(&quot;HTMLEvents&quot;)['__proto__'];
+    Object.extend(Event.prototype, _methods);
+    Event.extend = Prototype.K;
   }
-})();
-
-Object.extend(Event, (function() {
-  var cache = Event.cache;
-
-  function getEventID(element) {
+  
+  
+  function _getEventID(element) {
     if (element._prototypeEventID) return element._prototypeEventID[0];
-    arguments.callee.id = arguments.callee.id || 1;
     return element._prototypeEventID = [++arguments.callee.id];
   }
   
-  function getDOMEventName(eventName) {
+  _getEventID.id = 1;
+  
+  function _getDOMEventName(eventName) {
     if (eventName &amp;&amp; eventName.include(':')) return &quot;dataavailable&quot;;
     return eventName;
   }
   
-  function getCacheForID(id) {
-    return cache[id] = cache[id] || { };
+  function _getCacheForID(id) {
+    return Event.cache[id] = Event.cache[id] || { };
   }
   
-  function getWrappersForEventName(id, eventName) {
-    var c = getCacheForID(id);
+  function _getRespondersForEvent(id, eventName) {
+    var c = _getCacheForID(id);
     return c[eventName] = c[eventName] || [];
   }
   
-  function createWrapper(element, eventName, handler) {
-    var id = getEventID(element);
-    var c = getWrappersForEventName(id, eventName);
-    if (c.pluck(&quot;handler&quot;).include(handler)) return false;
+  function _createResponder(element, eventName, handler) {
+    var id = _getEventID(element), r = _getRespondersForEvent(id, eventName);
     
-    var wrapper = function(event) {
-      if (!Event || !Event.extend ||
-        (event.eventName &amp;&amp; event.eventName != eventName))
-          return false;
-      
+    // Work around the issue that permits a handler to be attached more than
+    // once to the same element &amp; event type.
+    if (r.pluck('handler').include(handler)) return false;
+    
+    var responder = function(event) {
+      if (!Event || !Event.extend) return false;
+      // If it's a custom event, but not the _correct_ custom event, ignore it.
+      if (!Object.isUndefined(event.eventName) &amp;&amp; 
+       event.eventName !== eventName)
+        return false;
+        
       Event.extend(event);
       handler.call(element, event);
     };
     
-    wrapper.handler = handler;
-    c.push(wrapper);
-    return wrapper;
+    responder.handler = handler;
+    r.push(responder);
+    return responder;
   }
   
-  function findWrapper(id, eventName, handler) {
-    var c = getWrappersForEventName(id, eventName);
-    return c.find(function(wrapper) { return wrapper.handler == handler });
+  function _findResponder(id, eventName, handler) {
+    var r = _getRespondersForEvent(id, eventName);
+    
+    return r.find( function(responder) { 
+      return responder.handler === handler;
+    });
   }
   
-  function destroyWrapper(id, eventName, handler) {
-    var c = getCacheForID(id);
-    if (!c[eventName]) return false;
-    c[eventName] = c[eventName].without(findWrapper(id, eventName, 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 cache)
+  function _destroyCache() {
+    for (var id in Event.cache) {
       for (var eventName in cache[id])
         cache[id][eventName] = null;
+    }
   }
   
   
   // Internet Explorer needs to remove event handlers on page unload
   // in order to avoid memory leaks.
-  if (window.attachEvent) {
+  if (IE_EVENT_SYSTEM) {
     window.attachEvent(&quot;onunload&quot;, destroyCache);
   }
   
-  // Safari has a dummy event handler on page unload so that it won't
+  // Safari needs a dummy event handler on page unload so that it won't
   // use its bfcache. Safari &lt;= 3.1 has an issue with restoring the &quot;document&quot;
   // object when page is returned to via the back button using its bfcache.
   if (Prototype.Browser.WebKit) {    
     window.addEventListener('unload', Prototype.emptyFunction, false);
+  }  
+  
+  function _observe(element, eventName, handler) {
+    element = $(element);
+    
+    var name = _getDOMEventName(eventName);
+    
+    var responder = _createResponder(element, eventName, handler);
+    if (!responder) return element;
+    
+    if (element.addEventListener) {
+      element.addEventListener(name, responder, false);
+    } else {
+      element.attachEvent(&quot;on&quot; + name, responder);
+    }
+    
+    return element;
   }
+  
+  function _stopObserving(element, eventName, handler) {
+    element = $(element);
+    var id = _getEventID(element), name = _getDOMEventName(eventName);
     
-  return {
-    observe: function(element, eventName, handler) {
-      element = $(element);
-      var name = getDOMEventName(eventName);
-      
-      var wrapper = createWrapper(element, eventName, handler);
-      if (!wrapper) return element;
-      
-      if (element.addEventListener) {
-        element.addEventListener(name, wrapper, false);
-      } else {
-        element.attachEvent(&quot;on&quot; + name, wrapper);
-      }
-      
+    if (eventName &amp;&amp; !handler) {
+      // If an event name is passed without a handler, we stop observing all
+      // handlers of that type.
+      _getRespondersForEvent(id, eventName).each( function(r) {
+        element.stopObserving(eventName, r.handler);
+      });
       return element;
-    },
-  
-    stopObserving: function(element, eventName, handler) {
-      element = $(element);
-      var id = getEventID(element), name = getDOMEventName(eventName);
-      
-      if (!handler &amp;&amp; eventName) {
-        getWrappersForEventName(id, eventName).each(function(wrapper) {
-          element.stopObserving(eventName, wrapper.handler);
-        });
-        return element;
-        
-      } else if (!eventName) {
-        Object.keys(getCacheForID(id)).each(function(eventName) {
-          element.stopObserving(eventName);
-        });
-        return element;
-      }
-      
-      var wrapper = findWrapper(id, eventName, handler);
-      if (!wrapper) return element;
-      
-      if (element.removeEventListener) {
-        element.removeEventListener(name, wrapper, false);
-      } else {
-        element.detachEvent(&quot;on&quot; + name, wrapper);
-      }
-      
-      destroyWrapper(id, eventName, handler);
-      
+    } 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) {
+        element.stopObserving(eventName);
+      });
       return element;
-    },
+    }
+    
+    var responder = _findResponder(id, eventName, handler);
+    if (!responder) return element;
+    
+    if (element.removeEventListener) {
+      element.removeEventListener(name, responder, false);
+    } else {
+      element.detachEvent(&quot;on&quot; + name, responder);
+    }
+    
+    _destroyResponder(id, eventName, handler);
+    
+    return element;
+  }
   
-    fire: function(element, eventName, memo) {
-      element = $(element);
-      if (element == document &amp;&amp; document.createEvent &amp;&amp; !element.dispatchEvent)
-        element = document.documentElement;
-        
-      var event;
-      if (document.createEvent) {
-        event = document.createEvent(&quot;HTMLEvents&quot;);
-        event.initEvent(&quot;dataavailable&quot;, true, true);
-      } else {
-        event = document.createEventObject();
-        event.eventType = &quot;ondataavailable&quot;;
-      }
-
-      event.eventName = eventName;
-      event.memo = memo || { };
-
-      if (document.createEvent) {
-        element.dispatchEvent(event);
-      } else {
-        element.fireEvent(event.eventType, event);
-      }
-
-      return Event.extend(event);
+  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 &amp;&amp; document.createEvent &amp;&amp; !element.dispatchEvent)
+      element = document.documentElement;
+      
+    var event;
+    
+    if (document.createEvent) {
+      event = document.createEvent(&quot;HTMLEvents&quot;);
+      event.initEvent(&quot;dataavailable&quot;, true, true);
+    } else {
+      event = document.createEventObject();
+      event.eventType = &quot;ondataavailable&quot;;
     }
-  };
-})());
-
-Object.extend(Event, Event.Methods);
-
-Element.addMethods({
-  fire:          Event.fire,
-  observe:       Event.observe,
-  stopObserving: Event.stopObserving
-});
+    
+    event.eventName = eventName;
+    event.memo = memo || { };
+    
+    if (document.createEvent) {
+      element.dispatchEvent(event);
+    } 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(document, {
-  fire:          Element.Methods.fire.methodize(),
-  observe:       Element.Methods.observe.methodize(),
-  stopObserving: Element.Methods.stopObserving.methodize(),
-  loaded:        false
-});
+  // Export to the global scope.
+  if (window.Event) {
+    Object.extend(window.Event, Event);
+  } else {
+    window.Event = Event;
+  }
+})();
 
 (function() {
   /* Support for the DOMContentLoaded event is based on work by Dan Webb, 
      Matthias Miller, Dean Edwards and John Resig. */
 
-  var timer;
+  var _timer;
   
-  function fireContentLoadedEvent() {
+  function _fireContentLoadedEvent() {
     if (document.loaded) return;
-    if (timer) window.clearInterval(timer);
+    if (_timer) window.clearInterval(_timer);
+    
     document.fire(&quot;dom:loaded&quot;);
     document.loaded = true;
   }
   
+  function _webkitContentLoadedCheck() {
+    var s = document.readyState;
+    if (s === &quot;loaded&quot; || s === &quot;complete&quot;)
+      _fireContentLoadedEvent();
+  }
+  
+  function _IEContentLoadedCheck() {
+    if (this.readyState == &quot;complete&quot;) {
+      this.onreadystatechange = null; 
+      _fireContentLoadedEvent();
+    }
+  }
+  
   if (document.addEventListener) {
     if (Prototype.Browser.WebKit) {
-      timer = window.setInterval(function() {
-        if (/loaded|complete/.test(document.readyState))
-          fireContentLoadedEvent();
-      }, 0);
-      
-      Event.observe(window, &quot;load&quot;, fireContentLoadedEvent);
-      
+      _timer = window.setInterval(_webkitContentLoadedCheck, 0);
+      Event.observe(window, &quot;load&quot;, _fireContentLoadedEvent);
     } else {
-      document.addEventListener(&quot;DOMContentLoaded&quot;, 
-        fireContentLoadedEvent, false);
+      document.addEventListener(&quot;DOMContentLoaded&quot;,
+        _fireContentLoadedEvent, false);
     }
-    
   } else {
     document.write(&quot;&lt;script id=__onDOMContentLoaded defer src=//:&gt;&lt;\/script&gt;&quot;);
-    $(&quot;__onDOMContentLoaded&quot;).onreadystatechange = function() { 
-      if (this.readyState == &quot;complete&quot;) {
-        this.onreadystatechange = null; 
-        fireContentLoadedEvent();
-      }
-    }; 
+    $(&quot;__onDOMContentLoaded&quot;).onreadystatechange = _IEContentLoadedCheck;
   }
-})();
+})();
\ No newline at end of file</diff>
      <filename>src/dom/event.js</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>d55932fc8c60847406789fa57c998c6ec6c46385</id>
    </parent>
  </parents>
  <author>
    <name>Andrew Dupont</name>
    <login>savetheclocktower</login>
    <email>prototype@andrewdupont.net</email>
  </author>
  <url>http://github.com/sstephenson/prototype/commit/b878bfe3ff5d9ad3d621987b7b05322f50fe3cf2</url>
  <id>b878bfe3ff5d9ad3d621987b7b05322f50fe3cf2</id>
  <committed-date>2008-10-01T23:00:05-07:00</committed-date>
  <authored-date>2008-10-01T23:00:05-07:00</authored-date>
  <message>Refactored event.js.</message>
  <tree>d83f46d51e284d621a55a74a612e582ae036478e</tree>
  <committer>
    <name>Andrew Dupont</name>
    <login>savetheclocktower</login>
    <email>prototype@andrewdupont.net</email>
  </committer>
</commit>
