12da489cd246702bee5938545b18a6f710ed214bcJamie Gennis// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis// Use of this source code is governed by a BSD-style license that can be 3010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis// found in the LICENSE file. 4010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis 566a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis'use strict'; 666a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis 7010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis/** 8010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * @fileoverview This contains an implementation of the EventTarget interface 9010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * as defined by DOM Level 2 Events. 10010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis */ 112da489cd246702bee5938545b18a6f710ed214bcJamie Gennisbase.exportTo('base', function() { 12010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis 13010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis /** 14010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * Creates a new EventTarget. This class implements the DOM level 2 15010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * EventTarget interface and can be used wherever those are used. 16010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * @constructor 17010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis */ 18010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis function EventTarget() { 19010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis } 20010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis 21010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis EventTarget.prototype = { 22010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis 23010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis /** 24010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * Adds an event listener to the target. 25010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * @param {string} type The name of the event. 26010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * @param {!Function|{handleEvent:Function}} handler The handler for the 27010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * event. This is called when the event is dispatched. 28010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis */ 29010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis addEventListener: function(type, handler) { 30010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis if (!this.listeners_) 31010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis this.listeners_ = Object.create(null); 32010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis if (!(type in this.listeners_)) { 33010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis this.listeners_[type] = [handler]; 34010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis } else { 35010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis var handlers = this.listeners_[type]; 36010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis if (handlers.indexOf(handler) < 0) 37010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis handlers.push(handler); 38010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis } 39010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis }, 40010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis 41010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis /** 42010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * Removes an event listener from the target. 43010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * @param {string} type The name of the event. 44010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * @param {!Function|{handleEvent:Function}} handler The handler for the 45010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * event. 46010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis */ 47010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis removeEventListener: function(type, handler) { 48010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis if (!this.listeners_) 49010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis return; 50010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis if (type in this.listeners_) { 51010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis var handlers = this.listeners_[type]; 52010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis var index = handlers.indexOf(handler); 53010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis if (index >= 0) { 54010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis // Clean up if this was the last listener. 55010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis if (handlers.length == 1) 56010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis delete this.listeners_[type]; 57010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis else 58010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis handlers.splice(index, 1); 59010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis } 60010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis } 61010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis }, 62010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis 63010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis /** 64010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * Dispatches an event and calls all the listeners that are listening to 65010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * the type of the event. 66010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * @param {!cr.event.Event} event The event to dispatch. 67010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * @return {boolean} Whether the default action was prevented. If someone 68010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis * calls preventDefault on the event object then this returns false. 69010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis */ 70010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis dispatchEvent: function(event) { 71010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis if (!this.listeners_) 72010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis return true; 73010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis 74010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis // Since we are using DOM Event objects we need to override some of the 75010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis // properties and methods so that we can emulate this correctly. 76010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis var self = this; 77010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis event.__defineGetter__('target', function() { 78010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis return self; 79010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis }); 80010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis event.preventDefault = function() { 81010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis this.returnValue = false; 82010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis }; 83010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis 84010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis var type = event.type; 85010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis var prevented = 0; 86010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis if (type in this.listeners_) { 87010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis // Clone to prevent removal during dispatch 88010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis var handlers = this.listeners_[type].concat(); 89010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis for (var i = 0, handler; handler = handlers[i]; i++) { 90010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis if (handler.handleEvent) 91010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis prevented |= handler.handleEvent.call(handler, event) === false; 92010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis else 93010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis prevented |= handler.call(this, event) === false; 94010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis } 95010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis } 96010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis 97010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis return !prevented && event.returnValue; 9866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis }, 9966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis 10066a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis hasEventListener: function(type) { 10166a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis return this.listeners_[type] !== undefined; 10266a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis } 10366a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis }; 10466a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis 10566a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis var EventTargetHelper = { 10666a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis decorate: function(target) { 10766a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis for (var k in EventTargetHelper) { 10866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis if (k == 'decorate') 10966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis continue; 11066a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis var v = EventTargetHelper[k]; 11166a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis if (typeof v !== 'function') 11266a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis continue; 11366a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis target[k] = v; 11466a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis } 11566a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis target.listenerCounts_ = {}; 11666a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis }, 11766a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis 11866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis addEventListener: function(type, listener, useCapture) { 11966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis this.__proto__.addEventListener.call( 12066a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis this, type, listener, useCapture); 12166a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis if (this.listenerCounts_[type] === undefined) 12266a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis this.listenerCounts_[type] = 0; 12366a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis this.listenerCounts_[type]++; 12466a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis }, 12566a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis 12666a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis removeEventListener: function(type, listener, useCapture) { 12766a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis this.__proto__.removeEventListener.call( 12866a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis this, type, listener, useCapture); 12966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis this.listenerCounts_[type]--; 13066a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis }, 13166a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis 13266a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis hasEventListener: function(type) { 13366a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis return this.listenerCounts_[type] > 0; 134010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis } 135010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis }; 136010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis 137010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis // Export 138010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis return { 13966a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis EventTarget: EventTarget, 14066a37686207944273ced825e0e8b6b6375f8c3deJamie Gennis EventTargetHelper: EventTargetHelper 141010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis }; 142010583560e0e6db74fe50b840bce46ba6537de63Jamie Gennis}); 143