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

Changeset 7270

Show
Ignore:
Timestamp:
08/04/07 07:20:45 (3 years ago)
Author:
sam
Message:

prototype: Merge -r7242:HEAD from ../branches/1-6-0-events. Adds support for DOM-based custom events with Event.fire/Element.Methods.fire, cross-browser DOMContentLoaded support, extended and normalized event objects, and improvements to the observe and stopObserving APIs.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • spinoffs/prototype/trunk/CHANGELOG

    r7265 r7270  
    11*SVN* 
     2 
     3* Make the eventName and handler arguments to Event.stopObserving optional.  If no handler is specified, all handlers for the given event are unregistered.  If no event name is specified, all observed events on the element are unregistered.  [sam] 
     4 
     5* Add cross-support for the DOMContentLoaded event through a Prototype custom event on document called "contentloaded".  The DOMContentLoaded event fires before window.load, when the entire HTML document, but not necessarily its images, stylesheets or other assets, has loaded.  Based on [6596].  [sam, Mislav Marohnić]  
     6  Example: 
     7    document.observe("contentloaded", function() { 
     8      $$("a").invoke("identify"); // give all <a> tags an ID 
     9    }); 
     10 
     11* Add Event.fire and Element.Methods.fire for firing custom events.  Prototype custom events piggyback on a real DOM event ("ondataavailable"), so they bubble and cancel.  You can fire custom events from any element, or fire global events on the document object.  Observe custom events just as you'd observe a regular DOM event.  [sam, Seth Dillingham] 
     12 
     13* Extend the event object with methods from Event.Methods and normalize it in IE.  [sam, Mislav Marohnić] 
     14 
     15* Remove support for observing the capturing phase of DOM events, since we can't support it in all browsers.  [sam] 
    216 
    317* Add Ajax.Response object which supports the following methods: responseJSON, headerJSON, getHeader, getAllHeaders and handles browser discrepancies in the other response methods. Add sanitizeJSON, evalJS and evalJSON to Ajax.Request. Closes #8122, #8006, #7295. [Tobie Langel] 
  • spinoffs/prototype/trunk/src/dom.js

    r7231 r7270  
    2323/*--------------------------------------------------------------------------*/ 
    2424 
     25if (!window.Node) 
     26  var Node = { }; 
     27   
     28Object.extend(Node, { 
     29  ELEMENT_NODE: 1, 
     30  ATTRIBUTE_NODE: 2, 
     31  TEXT_NODE: 3, 
     32  CDATA_SECTION_NODE: 4, 
     33  ENTITY_REFERENCE_NODE: 5, 
     34  ENTITY_NODE: 6, 
     35  PROCESSING_INSTRUCTION_NODE: 7, 
     36  COMMENT_NODE: 8, 
     37  DOCUMENT_NODE: 9, 
     38  DOCUMENT_TYPE_NODE: 10, 
     39  DOCUMENT_FRAGMENT_NODE: 11, 
     40  NOTATION_NODE: 12 
     41}); 
     42 
    2543(function() { 
    2644  var element = this.Element; 
    2745  this.Element = function(tagName, attributes) { 
    28     attributes = attributes || {}; 
     46    attributes = attributes || { }; 
    2947    tagName = tagName.toLowerCase(); 
    3048    var cache = Element.cache; 
     
    3755    return Element.writeAttribute(cache[tagName].cloneNode(false), attributes); 
    3856  }; 
    39   Object.extend(this.Element, element || {}); 
     57  Object.extend(this.Element, element || { }); 
    4058}).call(window); 
    4159 
    42 Element.cache = {}; 
     60Element.cache = { }; 
    4361 
    4462Element.Methods = { 
     
    128146    element = $(element); 
    129147    if (Object.isElement(wrapper)) 
    130       $(wrapper).writeAttribute(attributes || {}); 
     148      $(wrapper).writeAttribute(attributes || { }); 
    131149    else if (typeof wrapper == 'string') wrapper = new Element(wrapper, attributes); 
    132150    else wrapper = new Element('div', wrapper); 
     
    260278  writeAttribute: function(element, name, value) { 
    261279    element = $(element); 
    262     var attributes = {}, t = Element._attributeTranslations.write; 
     280    var attributes = { }, t = Element._attributeTranslations.write; 
    263281     
    264282    if (typeof name == 'object') attributes = name; 
     
    314332    return element[element.hasClassName(className) ? 
    315333      'removeClassName' : 'addClassName'](className); 
    316   }, 
    317    
    318   observe: function() { 
    319     Event.observe.apply(Event, arguments); 
    320     return $A(arguments).first(); 
    321   }, 
    322    
    323   stopObserving: function() { 
    324     Event.stopObserving.apply(Event, arguments); 
    325     return $A(arguments).first(); 
    326334  }, 
    327335   
     
    586594      offsetTop:  0, 
    587595      offsetLeft: 0 
    588     }, arguments[2] || {}); 
     596    }, arguments[2] || { }); 
    589597 
    590598    // find page position of source 
     
    664672      htmlFor:   'for'       
    665673    },  
    666     values: {
     674    values: {
    667675  } 
    668676}; 
     
    869877        element.appendChild(n); 
    870878        element.removeChild(n); 
    871       } catch (e) {
     879      } catch (e) {
    872880     
    873881    return element; 
     
    10201028}; 
    10211029 
    1022 Element.Methods.ByTag = {}; 
     1030Element.Methods.ByTag = { }; 
    10231031 
    10241032Object.extend(Element, Element.Methods); 
     
    10261034if (!Prototype.BrowserFeatures.ElementExtensions &&  
    10271035    document.createElement('div').__proto__) { 
    1028   window.HTMLElement = {}; 
     1036  window.HTMLElement = { }; 
    10291037  window.HTMLElement.prototype = document.createElement('div').__proto__; 
    10301038  Prototype.BrowserFeatures.ElementExtensions = true; 
     
    10351043    return Prototype.K; 
    10361044   
    1037   var Methods = {}, ByTag = Element.Methods.ByTag; 
     1045  var Methods = { }, ByTag = Element.Methods.ByTag; 
    10381046   
    10391047  var extend = Object.extend(function(element) { 
     
    10941102  } 
    10951103   
    1096   if (!tagName) Object.extend(Element.Methods, methods || {});   
     1104  if (!tagName) Object.extend(Element.Methods, methods || { });   
    10971105  else { 
    10981106    if (Object.isArray(tagName)) tagName.each(extend); 
     
    11031111    tagName = tagName.toUpperCase(); 
    11041112    if (!Element.Methods.ByTag[tagName]) 
    1105       Element.Methods.ByTag[tagName] = {}; 
     1113      Element.Methods.ByTag[tagName] = { }; 
    11061114    Object.extend(Element.Methods.ByTag[tagName], methods); 
    11071115  } 
     
    11371145    if (window[klass]) return window[klass]; 
    11381146     
    1139     window[klass] = {}; 
     1147    window[klass] = { }; 
    11401148    window[klass].prototype = document.createElement(tagName).__proto__; 
    11411149    return window[klass]; 
     
    11591167   
    11601168  if (Element.extend.refresh) Element.extend.refresh(); 
    1161   Element.cache = {}; 
     1169  Element.cache = { }; 
    11621170}; 
  • spinoffs/prototype/trunk/src/event.js

    r6825 r7270  
    1 if (!window.Event) { 
    2   var Event = new Object(); 
    3 
     1if (!window.Event) var Event = { }; 
    42 
    53Object.extend(Event, { 
     
    1715  KEY_PAGEUP:   33, 
    1816  KEY_PAGEDOWN: 34, 
    19  
     17  KEY_INSERT:   45, 
     18   
     19  DOMEvents: ['click', 'dblclick', 'mousedown', 'mouseup', 'mouseover',  
     20              'mousemove', 'mouseout', 'keypress', 'keydown', 'keyup',  
     21              'load', 'unload', 'abort', 'error', 'resize', 'scroll',  
     22              'select', 'change', 'submit', 'reset', 'focus', 'blur',  
     23              'DOMFocusIn', 'DOMFocusOut', 'DOMActivate',  
     24              'DOMSubtreeModified', 'DOMNodeInserted',  
     25              'NodeInsertedIntoDocument', 'DOMAttrModified',  
     26              'DOMCharacterDataModified'], 
     27 
     28  relatedTarget: function(event) { 
     29    var element; 
     30    switch(event.type) { 
     31      case 'mouseover': element = event.fromElement; break; 
     32      case 'mouseout':  element = event.toElement;   break; 
     33      default: return null; 
     34    } 
     35    return Element.extend(element); 
     36  } 
     37}); 
     38 
     39Event.Methods = { 
    2040  element: function(event) { 
    21     return $(event.target || event.srcElement); 
     41    var node = event.target; 
     42    return Element.extend(node.nodeType == Node.TEXT_NODE ? node.parentNode : node); 
     43  }, 
     44 
     45  findElement: function(event, expression) { 
     46    var element = Event.element(event); 
     47    return element.match(expression) ? element : element.up(expression); 
    2248  }, 
    2349 
     
    2753  }, 
    2854 
    29   pointerX: function(event) { 
    30     return event.pageX || (event.clientX +  
    31       (document.documentElement.scrollLeft || document.body.scrollLeft)); 
    32   }, 
    33  
    34   pointerY: function(event) { 
    35     return event.pageY || (event.clientY +  
    36       (document.documentElement.scrollTop || document.body.scrollTop)); 
    37   }, 
     55  pointer: function(event) { 
     56    return { 
     57      x: event.pageX || (event.clientX +  
     58        (document.documentElement.scrollLeft || document.body.scrollLeft)), 
     59      y: event.pageY || (event.clientY +  
     60        (document.documentElement.scrollTop || document.body.scrollTop)) 
     61    }; 
     62  }, 
     63 
     64  pointerX: function(event) { return Event.pointer(event).x }, 
     65  pointerY: function(event) { return Event.pointer(event).y }, 
    3866 
    3967  stop: function(event) { 
    40     if (event.preventDefault) {  
    41       event.preventDefault();  
    42       event.stopPropagation();  
     68    event.preventDefault();  
     69    event.stopPropagation();  
     70  } 
     71}; 
     72 
     73Event.extend = (function() { 
     74  var methods = Object.keys(Event.Methods).inject({ }, function(m, name) { 
     75    m[name] = Event.Methods[name].methodize(); 
     76    return m; 
     77  }); 
     78   
     79  if (Prototype.Browser.IE) { 
     80    Object.extend(methods, { 
     81      stopPropagation: function() { this.cancelBubble = true }, 
     82      preventDefault:  function() { this.returnValue = false }, 
     83      inspect: function() { return "[object Event]" } 
     84    }); 
     85 
     86    return function(event) { 
     87      if (!event) return false; 
     88      if (event._extendedByPrototype) return event; 
     89       
     90      event._extendedByPrototype = Prototype.emptyFunction; 
     91      var pointer = Event.pointer(event); 
     92      Object.extend(event, { 
     93        target: event.srcElement, 
     94        relatedTarget: Event.relatedTarget(event), 
     95        pageX:  pointer.x, 
     96        pageY:  pointer.y 
     97      }); 
     98      return Object.extend(event, methods); 
     99    }; 
     100     
     101  } else { 
     102    Event.prototype = Event.prototype || document.createEvent("Events").__proto__; 
     103    Object.extend(Event.prototype, methods); 
     104    return Prototype.K; 
     105  } 
     106})(); 
     107 
     108Object.extend(Event, (function() { 
     109  var cache = { }; 
     110   
     111  function getEventID(element) { 
     112    if (element._eventID) return element._eventID; 
     113    arguments.callee.id = arguments.callee.id || 1; 
     114    return element._eventID = ++arguments.callee.id; 
     115  } 
     116   
     117  function getDOMEventName(eventName) { 
     118    if (!Event.DOMEvents.include(eventName)) return "dataavailable"; 
     119    return { keypress: "keydown" }[eventName] || eventName; 
     120  } 
     121   
     122  function getCacheForID(id) { 
     123    return cache[id] = cache[id] || { }; 
     124  } 
     125   
     126  function getWrappersForEventName(id, eventName) { 
     127    var c = getCacheForID(id); 
     128    return c[eventName] = c[eventName] || []; 
     129  } 
     130   
     131  function createWrapper(id, eventName, handler) { 
     132    var c = getWrappersForEventName(id, eventName); 
     133    if (c.pluck("handler").include(handler)) return false; 
     134     
     135    var wrapper = function(event) { 
     136      if (event.eventName && event.eventName != eventName) 
     137        return false; 
     138       
     139      Event.extend(event); 
     140      handler.call(event.target, event); 
     141    }; 
     142     
     143    wrapper.handler = handler; 
     144    c.push(wrapper); 
     145    return wrapper; 
     146  } 
     147   
     148  function findWrapper(id, eventName, handler) { 
     149    var c = getWrappersForEventName(id, eventName); 
     150    return c.find(function(wrapper) { return wrapper.handler == handler }); 
     151  } 
     152   
     153  function destroyWrapper(id, eventName, handler) { 
     154    var c = getCacheForID(id), name = getDOMEventName(eventName); 
     155    if (!c[name]) return false; 
     156    c[name] = c[name].without(findWrapper(id, eventName, handler)); 
     157  } 
     158   
     159  function destroyCache() { 
     160    for (var id in cache) 
     161      for (var eventName in cache[id]) 
     162        cache[id][eventName] = null; 
     163  } 
     164   
     165  if (window.attachEvent) { 
     166    window.attachEvent("onunload", destroyCache); 
     167  } 
     168   
     169  return { 
     170    observe: function(element, eventName, handler) { 
     171      element = $(element); 
     172      var id = getEventID(element), name = getDOMEventName(eventName); 
     173       
     174      var wrapper = createWrapper(id, eventName, handler); 
     175      if (!wrapper) return false; 
     176       
     177      if (element.addEventListener) { 
     178        element.addEventListener(name, wrapper, false); 
     179      } else { 
     180        element.attachEvent("on" + name, wrapper); 
     181      } 
     182    }, 
     183   
     184    stopObserving: function(element, eventName, handler) { 
     185      element = $(element); 
     186      var id = getEventID(element), name = getDOMEventName(eventName); 
     187       
     188      if (!handler && eventName) { 
     189        return getWrappersForEventName(id, eventName).each(function(wrapper) { 
     190          element.stopObserving(eventName, wrapper.handler); 
     191        }) && false; 
     192         
     193      } else if (!eventName) { 
     194        return Object.keys(getCacheForID(id)).each(function(eventName) { 
     195          element.stopObserving(eventName); 
     196        }) && false; 
     197      } 
     198       
     199      var wrapper = findWrapper(id, eventName, handler); 
     200      if (!wrapper) return false; 
     201       
     202      if (element.removeEventListener) { 
     203        element.removeEventListener(name, wrapper, false); 
     204      } else { 
     205        element.detachEvent("on" + name, wrapper); 
     206      } 
     207    }, 
     208   
     209    fire: function(element, eventName, memo) { 
     210      element = $(element); 
     211      if (element == document && document.createEvent && !element.dispatchEvent) 
     212        element = document.documentElement; 
     213         
     214      if (document.createEvent) { 
     215        var event = document.createEvent("Events"); 
     216        event.initEvent("dataavailable", true, true); 
     217      } else { 
     218        var event = document.createEventObject(); 
     219        event.eventType = "ondataavailable"; 
     220      } 
     221 
     222      event.eventName = eventName; 
     223      event.memo = memo || {}; 
     224 
     225      if (document.createEvent) { 
     226        element.dispatchEvent(event); 
     227      } else { 
     228        element.fireEvent(event.eventType, event); 
     229      } 
     230 
     231      return element; 
     232    } 
     233  }; 
     234})()); 
     235 
     236Object.extend(Event, Event.Methods); 
     237 
     238Element.addMethods({ 
     239  fire: function() { 
     240    Event.fire.apply(Event, arguments); 
     241    return $A(arguments).first(); 
     242  }, 
     243   
     244  observe: function() { 
     245    Event.observe.apply(Event, arguments); 
     246    return $A(arguments).first(); 
     247  }, 
     248   
     249  stopObserving: function() { 
     250    Event.stopObserving.apply(Event, arguments); 
     251    return $A(arguments).first(); 
     252  } 
     253}); 
     254 
     255Object.extend(document, { 
     256  fire:          Element.Methods.fire.methodize(), 
     257  observe:       Element.Methods.observe.methodize(), 
     258  stopObserving: Element.Methods.stopObserving.methodize() 
     259}); 
     260 
     261(function() { 
     262  /* Support for the DOMContentLoaded event is based on work by Dan Webb,  
     263     Matthias Miller, Dean Edwards and John Resig. */ 
     264 
     265  var timer, fired = false; 
     266   
     267  function fireContentLoadedEvent() { 
     268    if (fired) return; 
     269    if (timer) window.clearInterval(timer); 
     270    document.fire("contentloaded"); 
     271    fired = true; 
     272  } 
     273   
     274  if (document.addEventListener) { 
     275    if (Prototype.Browser.WebKit) { 
     276      timer = window.setInterval(function() { 
     277        if (/loaded|complete/.test(document.readyState)) 
     278          fireContentLoadedEvent(); 
     279      }, 0); 
     280       
     281      Event.observe(window, "load", fireContentLoadedEvent); 
     282       
    43283    } else { 
    44       event.returnValue = false; 
    45       event.cancelBubble = true; 
     284      document.addEventListener("DOMContentLoaded", fireContentLoadedEvent, false); 
    46285    } 
    47   }, 
    48  
    49   findElement: function(event, expression) { 
    50     var element = Event.element(event); 
    51     return element.match(expression) ? element : element.up(expression); 
    52   }, 
    53  
    54   observers: false, 
    55    
    56   _observeAndCache: function(element, name, observer, useCapture) { 
    57     if (!this.observers) this.observers = []; 
    58     if (element.addEventListener) { 
    59       this.observers.push([element, name, observer, useCapture]); 
    60       element.addEventListener(name, observer, useCapture); 
    61     } else if (element.attachEvent) { 
    62       this.observers.push([element, name, observer, useCapture]); 
    63       element.attachEvent('on' + name, observer); 
    64     } 
    65   }, 
    66    
    67   unloadCache: function() { 
    68     if (!Event.observers) return; 
    69     for (var i = 0, length = Event.observers.length; i < length; i++) { 
    70       Event.stopObserving.apply(this, Event.observers[i]); 
    71       Event.observers[i][0] = null; 
    72     } 
    73     Event.observers = false; 
    74   }, 
    75  
    76   observe: function(element, name, observer, useCapture) { 
    77     element = $(element); 
    78     useCapture = useCapture || false; 
    79      
    80     if (name == 'keypress' && 
    81       (Prototype.Browser.WebKit || element.attachEvent)) 
    82       name = 'keydown'; 
    83      
    84     Event._observeAndCache(element, name, observer, useCapture); 
    85   }, 
    86  
    87   stopObserving: function(element, name, observer, useCapture) { 
    88     element = $(element); 
    89     useCapture = useCapture || false; 
    90      
    91     if (name == 'keypress' && 
    92         (Prototype.Browser.WebKit || element.attachEvent)) 
    93       name = 'keydown'; 
    94      
    95     if (element.removeEventListener) { 
    96       element.removeEventListener(name, observer, useCapture); 
    97     } else if (element.detachEvent) { 
    98       try {  
    99         element.detachEvent('on' + name, observer);  
    100       } catch (e) {} 
    101     } 
    102   } 
    103 }); 
    104  
    105 /* prevent memory leaks in IE */ 
    106 if (Prototype.Browser.IE) 
    107   Event.observe(window, 'unload', Event.unloadCache, false); 
     286     
     287  } else { 
     288    var dummy = location.protocol == "https:" ? "https://javascript:void(0)" : "javascript:void(0)"; 
     289    document.write("<script id=__onDOMContentLoaded defer src='" + dummy + "'><\/script>"); 
     290    $("__onDOMContentLoaded").onreadystatechange = function() {  
     291      if (this.readyState == "complete") { 
     292        this.onreadystatechange = null;  
     293        fireContentLoadedEvent(); 
     294      } 
     295    };  
     296  } 
     297})(); 
  • spinoffs/prototype/trunk/test/lib/unittest.js

    r7265 r7270  
    166166    this.currentTest = 0; 
    167167    this.logger = new Test.Unit.Logger(this.options.testLog); 
    168     setTimeout(this.runTests.bind(this), 1000); 
     168    Event.observe(window, "load", function() {  
     169      setTimeout(this.runTests.bind(this), 100); 
     170    }.bind(this)); 
    169171  }, 
    170172  parseResultsURLQueryParameter: function() {