Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Changeset 6194

Show
Ignore:
Timestamp:
02/21/07 21:29:00 (2 years ago)
Author:
mislav
Message:

These changes are imported from Andrew's and mine work on #5786.

Features include:

  • IE scope correction;
  • IE event object normalization;
  • IE doesn't error out on Object.inspect(evt) anymore (#2706);
  • bindAsEventListener arguments fix (#6497);
  • able to call observe and stopObserving with an array of elements;
  • stopObserving(element, type) removes all observers of type,
    stopObserving(element) removes *all* observers regardless of type! :)

There are functional tests in test/functional/event.html.
Tested on FF2, Opera 8.5 and IE6

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • spinoffs/prototype/branches/event/src/base.js

    r5490 r6194  
    5454} 
    5555 
    56 Function.prototype.bindAsEventListener = function(object) { 
     56Function.prototype.bindAsEventListener = function() { 
    5757  var __method = this, args = $A(arguments), object = args.shift(); 
    5858  return function(event) { 
    59     return __method.apply(object, [( event || window.event)].concat(args).concat($A(arguments))); 
     59    return __method.apply(object, [event || window.event].concat(args)); 
    6060  } 
    6161} 
  • spinoffs/prototype/branches/event/src/event.js

    r6173 r6194  
    2121    return event.target || event.srcElement; 
    2222  }, 
     23   
     24  relatedTarget: function(event) { 
     25    switch(event.type) { 
     26      case 'mouseover': return event.fromElement; 
     27      case 'mouseout':  return event.toElement; 
     28      default: return null; 
     29    } 
     30  }, 
    2331 
    2432  isLeftClick: function(event) { 
    25     return (((event.which) && (event.which == 1)) || 
    26             ((event.button) && (event.button == 1))); 
     33    return event.which == 1 || event.button == 1; 
    2734  }, 
    2835 
     
    3845 
    3946  stop: function(event) { 
    40     if (event.preventDefault) {  
    41       event.preventDefault();  
    42       event.stopPropagation();  
    43     } else { 
    44       event.returnValue = false; 
    45       event.cancelBubble = true; 
    46     } 
     47    event.preventDefault();  
     48    event.stopPropagation();  
    4749  }, 
    4850 
     
    5052  // node the event was triggered on; traverses the DOM upwards 
    5153  findElement: function(event, tagName) { 
    52     var element = Event.element(event); 
     54    var element = this.element(event); 
    5355    while (element.parentNode && (!element.tagName || 
    5456        (element.tagName.toUpperCase() != tagName.toUpperCase()))) 
    5557      element = element.parentNode; 
    5658    return element; 
    57   }, 
    58  
    59   observers: false, 
    60    
    61   _observeAndCache: function(element, name, observer, useCapture) { 
    62     if (!this.observers) this.observers = []; 
    63     if (element.addEventListener) { 
    64       this.observers.push([element, name, observer, useCapture]); 
    65       element.addEventListener(name, observer, useCapture); 
    66     } else if (element.attachEvent) { 
    67       this.observers.push([element, name, observer, useCapture]); 
    68       element.attachEvent('on' + name, observer); 
    69     } 
    70   }, 
    71    
    72   unloadCache: function() { 
    73     if (!Event.observers) return; 
    74     for (var i = 0, length = Event.observers.length; i < length; i++) { 
    75       Event.stopObserving.apply(this, Event.observers[i]); 
    76       Event.observers[i][0] = null; 
    77     } 
    78     Event.observers = false; 
    79   }, 
    80  
    81   observe: function(element, name, observer, useCapture) { 
    82     element = $(element); 
    83     useCapture = useCapture || false; 
    84      
    85     if (name == 'keypress' && 
    86       (Prototype.Browser.WebKit || element.attachEvent)) 
    87       name = 'keydown'; 
    88      
    89     Event._observeAndCache(element, name, observer, useCapture); 
    90   }, 
    91  
    92   stopObserving: function(element, name, observer, useCapture) { 
    93     element = $(element); 
    94     useCapture = useCapture || false; 
    95      
    96     if (name == 'keypress' && 
    97         (Prototype.Browser.WebKit || element.attachEvent)) 
    98       name = 'keydown'; 
    99      
    100     if (element.removeEventListener) { 
    101       element.removeEventListener(name, observer, useCapture); 
    102     } else if (element.detachEvent) { 
    103       try {  
    104         element.detachEvent('on' + name, observer);  
    105       } catch (e) {} 
    106     } 
    10759  } 
    10860}); 
    10961 
    110 /* prevent memory leaks in IE */ 
    111 if (Prototype.Browser.IE) 
    112   Event.observe(window, 'unload', Event.unloadCache, false); 
     62Object.extend(Event, function() 
     63
     64  var B = Prototype.Browser; 
     65   
     66  Observer = Class.create(); 
     67  Observer.guid = 1; 
     68   
     69  Object.extend(Observer.prototype, { 
     70    initialize: function(element, type, observer, useCapture) { 
     71      this.element = $(element); 
     72      if (type == 'keypress' && (B.IE || B.WebKit)) type = 'keydown'; 
     73      this.type = type; 
     74      this.observer = observer; 
     75      this.useCapture = useCapture || false; 
     76    }, 
     77    add: function() { 
     78      this._add(); 
     79      this.cache(); 
     80    }, 
     81    cache: function() { 
     82      if (!this.element._observers) this.element._observers = {}; 
     83      var local = this.element._observers[this.type]; 
     84      if (!local) local = this.element._observers[this.type] = {}; 
     85      if (!this.observer.$$guid) this.observer.$$guid = this.constructor.guid++; 
     86      local[this.observer.$$guid] = this; 
     87    }, 
     88    remove: function() { 
     89      if (!this.observer) { 
     90        if (this.element._observers) { 
     91          var collection = this.element._observers; 
     92          if (this.type) { 
     93            collection = collection[this.type] 
     94            if (!collection) return; 
     95          } 
     96          for (var i in collection) { 
     97            if (i > 0) collection[i].remove(); 
     98            else for (var j in collection[i]) collection[i][j].remove(); 
     99          } 
     100        } 
     101        return; 
     102      } 
     103      if (!this.observer.$$guid) return; 
     104      this._remove(); 
     105      delete this.element._observers[this.type][this.observer.$$guid]; 
     106    } 
     107  }); 
     108 
     109  if (document.addEventListener) 
     110  { 
     111    Object.extend(Observer.prototype, { 
     112      _add: function() { 
     113        // TODO: create wrapper for Safari? 
     114        this.element.addEventListener(this.type, this.observer, this.useCapture); 
     115      }, 
     116      _remove: function() { 
     117        this.element.removeEventListener(this.type, this.observer, this.useCapture); 
     118      } 
     119    }); 
     120  } else if (B.IE) 
     121  { 
     122    Object.extend(Observer.prototype, { 
     123      _add: function() { 
     124        // create a wrapper for scope correction and event object normalization 
     125        var ob = this.observer, el = this.element, klass = this.constructor; 
     126        this.wrapper = function(e) { 
     127          return ob.call(el, klass.extendEvent(e)) 
     128        }; 
     129        this.element.attachEvent('on' + this.type, this.wrapper); 
     130        this.constructor.globalCache.push(this); 
     131      }, 
     132      _remove: function() { 
     133        if (!this.wrapper) this.wrapper = this.element._observers[this.type][this.observer.$$guid].wrapper; 
     134        this.element.detachEvent('on' + this.type, this.wrapper); 
     135      } 
     136    }); 
     137 
     138    Object.extend(Observer, { 
     139      globalCache: [], 
     140      unloadCache: function() { 
     141        for(var i=0, length = this.globalCache.length; i < length; i++) 
     142          this.globalCache[i].remove() 
     143      }, 
     144      /* adds standard DOM event model properties to IE's events */ 
     145      extendEvent: function(event) { 
     146        event || (event = window.event); 
     147        return Object.extend(Object.extend(event, { 
     148            target: event.srcElement, 
     149            which:  event.button, 
     150            pageX:  Event.pointerX(event), 
     151            pageY:  Event.pointerY(event), 
     152            relatedTarget:   Event.relatedTarget(event) 
     153          }), 
     154          this.eventMethods 
     155        ); 
     156      }, 
     157      eventMethods: { 
     158        stopPropagation: function() { this.cancelBubble = true }, 
     159        preventDefault:  function() { this.returnValue = false }, 
     160        inspect: function() { return '[object Event]' } 
     161      } 
     162    }); 
     163 
     164    /* unload to prevent memory leaks */ 
     165    window.attachEvent('onunload', Observer.unloadCache.bind(Observer)); 
     166  } 
     167 
     168  // applies the calling method to an array of elements 
     169  function applyToList(args) { 
     170    if (args[0].constructor == Array) { 
     171      var method = args.callee; 
     172      args = $A(args); 
     173      args.shift().each(function(el){ 
     174        method.apply(null, [el].concat(args)); 
     175      }); 
     176      return true; // the calling method can end 
     177    } 
     178    return false; 
     179  } 
     180   
     181  return { 
     182    observe: function(element, type, observer, useCapture) { 
     183      if (applyToList(arguments)) return; 
     184       
     185      new Observer(element, type, observer, useCapture).add(); 
     186    }, 
     187    stopObserving: function(element, type, observer, useCapture) { 
     188      if (applyToList(arguments)) return; 
     189       
     190      new Observer(element, type, observer, useCapture).remove(); 
     191    } 
     192  } 
     193}());