531 lines
17 KiB
JavaScript
531 lines
17 KiB
JavaScript
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.eventTargetShim = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
|
|
/**
|
|
* @author Toru Nagashima
|
|
* @copyright 2015 Toru Nagashima. All rights reserved.
|
|
* See LICENSE file in root directory for full license.
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
/**
|
|
* Creates a unique key.
|
|
*
|
|
* @param {string} name - A name to create.
|
|
* @returns {symbol|string}
|
|
* @private
|
|
*/
|
|
var createUniqueKey = exports.createUniqueKey = (typeof Symbol !== "undefined" ?
|
|
Symbol :
|
|
function createUniqueKey(name) {
|
|
return "[[" + name + "_" + Math.random().toFixed(8).slice(2) + "]]";
|
|
});
|
|
|
|
/**
|
|
* The key of listeners.
|
|
*
|
|
* @type {symbol|string}
|
|
* @private
|
|
*/
|
|
exports.LISTENERS = createUniqueKey("listeners");
|
|
|
|
/**
|
|
* A value of kind for listeners which are registered in the capturing phase.
|
|
*
|
|
* @type {number}
|
|
* @private
|
|
*/
|
|
exports.CAPTURE = 1;
|
|
|
|
/**
|
|
* A value of kind for listeners which are registered in the bubbling phase.
|
|
*
|
|
* @type {number}
|
|
* @private
|
|
*/
|
|
exports.BUBBLE = 2;
|
|
|
|
/**
|
|
* A value of kind for listeners which are registered as an attribute.
|
|
*
|
|
* @type {number}
|
|
* @private
|
|
*/
|
|
exports.ATTRIBUTE = 3;
|
|
|
|
/**
|
|
* @typedef object ListenerNode
|
|
* @property {function} listener - A listener function.
|
|
* @property {number} kind - The kind of the listener.
|
|
* @property {ListenerNode|null} next - The next node.
|
|
* If this node is the last, this is `null`.
|
|
*/
|
|
|
|
/**
|
|
* Creates a node of singly linked list for a list of listeners.
|
|
*
|
|
* @param {function} listener - A listener function.
|
|
* @param {number} kind - The kind of the listener.
|
|
* @returns {ListenerNode} The created listener node.
|
|
*/
|
|
exports.newNode = function newNode(listener, kind) {
|
|
return {listener: listener, kind: kind, next: null};
|
|
};
|
|
|
|
},{}],2:[function(require,module,exports){
|
|
/**
|
|
* @author Toru Nagashima
|
|
* @copyright 2015 Toru Nagashima. All rights reserved.
|
|
* See LICENSE file in root directory for full license.
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Requirements
|
|
//-----------------------------------------------------------------------------
|
|
|
|
var Commons = require("./commons");
|
|
var LISTENERS = Commons.LISTENERS;
|
|
var ATTRIBUTE = Commons.ATTRIBUTE;
|
|
var newNode = Commons.newNode;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Helpers
|
|
//-----------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Gets a specified attribute listener from a given EventTarget object.
|
|
*
|
|
* @param {EventTarget} eventTarget - An EventTarget object to get.
|
|
* @param {string} type - An event type to get.
|
|
* @returns {function|null} The found attribute listener.
|
|
*/
|
|
function getAttributeListener(eventTarget, type) {
|
|
var node = eventTarget[LISTENERS][type];
|
|
while (node != null) {
|
|
if (node.kind === ATTRIBUTE) {
|
|
return node.listener;
|
|
}
|
|
node = node.next;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Sets a specified attribute listener to a given EventTarget object.
|
|
*
|
|
* @param {EventTarget} eventTarget - An EventTarget object to set.
|
|
* @param {string} type - An event type to set.
|
|
* @param {function|null} listener - A listener to be set.
|
|
* @returns {void}
|
|
*/
|
|
function setAttributeListener(eventTarget, type, listener) {
|
|
if (typeof listener !== "function" && typeof listener !== "object") {
|
|
listener = null; // eslint-disable-line no-param-reassign
|
|
}
|
|
|
|
var prev = null;
|
|
var node = eventTarget[LISTENERS][type];
|
|
while (node != null) {
|
|
if (node.kind === ATTRIBUTE) {
|
|
// Remove old value.
|
|
if (prev == null) {
|
|
eventTarget[LISTENERS][type] = node.next;
|
|
}
|
|
else {
|
|
prev.next = node.next;
|
|
}
|
|
}
|
|
else {
|
|
prev = node;
|
|
}
|
|
|
|
node = node.next;
|
|
}
|
|
|
|
// Add new value.
|
|
if (listener != null) {
|
|
if (prev == null) {
|
|
eventTarget[LISTENERS][type] = newNode(listener, ATTRIBUTE);
|
|
}
|
|
else {
|
|
prev.next = newNode(listener, ATTRIBUTE);
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Public Interface
|
|
//-----------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Defines an `EventTarget` implementation which has `onfoobar` attributes.
|
|
*
|
|
* @param {EventTarget} EventTargetBase - A base implementation of EventTarget.
|
|
* @param {string[]} types - A list of event types which are defined as attribute listeners.
|
|
* @returns {EventTarget} The defined `EventTarget` implementation which has attribute listeners.
|
|
*/
|
|
exports.defineCustomEventTarget = function(EventTargetBase, types) {
|
|
function EventTarget() {
|
|
EventTargetBase.call(this);
|
|
}
|
|
|
|
var descripter = {
|
|
constructor: {
|
|
value: EventTarget,
|
|
configurable: true,
|
|
writable: true
|
|
}
|
|
};
|
|
|
|
types.forEach(function(type) {
|
|
descripter["on" + type] = {
|
|
get: function() { return getAttributeListener(this, type); },
|
|
set: function(listener) { setAttributeListener(this, type, listener); },
|
|
configurable: true,
|
|
enumerable: true
|
|
};
|
|
});
|
|
|
|
EventTarget.prototype = Object.create(EventTargetBase.prototype, descripter);
|
|
|
|
return EventTarget;
|
|
};
|
|
|
|
},{"./commons":1}],3:[function(require,module,exports){
|
|
/**
|
|
* @author Toru Nagashima
|
|
* @copyright 2015 Toru Nagashima. All rights reserved.
|
|
* See LICENSE file in root directory for full license.
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Requirements
|
|
//-----------------------------------------------------------------------------
|
|
|
|
var Commons = require("./commons");
|
|
var CustomEventTarget = require("./custom-event-target");
|
|
var EventWrapper = require("./event-wrapper");
|
|
var LISTENERS = Commons.LISTENERS;
|
|
var CAPTURE = Commons.CAPTURE;
|
|
var BUBBLE = Commons.BUBBLE;
|
|
var ATTRIBUTE = Commons.ATTRIBUTE;
|
|
var newNode = Commons.newNode;
|
|
var defineCustomEventTarget = CustomEventTarget.defineCustomEventTarget;
|
|
var createEventWrapper = EventWrapper.createEventWrapper;
|
|
var STOP_IMMEDIATE_PROPAGATION_FLAG =
|
|
EventWrapper.STOP_IMMEDIATE_PROPAGATION_FLAG;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Constants
|
|
//-----------------------------------------------------------------------------
|
|
|
|
/**
|
|
* A flag which shows there is the native `EventTarget` interface object.
|
|
*
|
|
* @type {boolean}
|
|
* @private
|
|
*/
|
|
var HAS_EVENTTARGET_INTERFACE = (
|
|
typeof window !== "undefined" &&
|
|
typeof window.EventTarget !== "undefined"
|
|
);
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Public Interface
|
|
//-----------------------------------------------------------------------------
|
|
|
|
/**
|
|
* An implementation for `EventTarget` interface.
|
|
*
|
|
* @constructor
|
|
* @public
|
|
*/
|
|
var EventTarget = module.exports = function EventTarget() {
|
|
if (this instanceof EventTarget) {
|
|
// this[LISTENERS] is a Map.
|
|
// Its key is event type.
|
|
// Its value is ListenerNode object or null.
|
|
//
|
|
// interface ListenerNode {
|
|
// var listener: Function
|
|
// var kind: CAPTURE|BUBBLE|ATTRIBUTE
|
|
// var next: ListenerNode|null
|
|
// }
|
|
Object.defineProperty(this, LISTENERS, {value: Object.create(null)});
|
|
}
|
|
else if (arguments.length === 1 && Array.isArray(arguments[0])) {
|
|
return defineCustomEventTarget(EventTarget, arguments[0]);
|
|
}
|
|
else if (arguments.length > 0) {
|
|
var types = Array(arguments.length);
|
|
for (var i = 0; i < arguments.length; ++i) {
|
|
types[i] = arguments[i];
|
|
}
|
|
|
|
// To use to extend with attribute listener properties.
|
|
// e.g.
|
|
// class MyCustomObject extends EventTarget("message", "error") {
|
|
// //...
|
|
// }
|
|
return defineCustomEventTarget(EventTarget, types);
|
|
}
|
|
else {
|
|
throw new TypeError("Cannot call a class as a function");
|
|
}
|
|
};
|
|
|
|
EventTarget.prototype = Object.create(
|
|
(HAS_EVENTTARGET_INTERFACE ? window.EventTarget : Object).prototype,
|
|
{
|
|
constructor: {
|
|
value: EventTarget,
|
|
writable: true,
|
|
configurable: true
|
|
},
|
|
|
|
addEventListener: {
|
|
value: function addEventListener(type, listener, capture) {
|
|
if (listener == null) {
|
|
return false;
|
|
}
|
|
if (typeof listener !== "function" && typeof listener !== "object") {
|
|
throw new TypeError("\"listener\" is not an object.");
|
|
}
|
|
|
|
var kind = (capture ? CAPTURE : BUBBLE);
|
|
var node = this[LISTENERS][type];
|
|
if (node == null) {
|
|
this[LISTENERS][type] = newNode(listener, kind);
|
|
return true;
|
|
}
|
|
|
|
var prev = null;
|
|
while (node != null) {
|
|
if (node.listener === listener && node.kind === kind) {
|
|
// Should ignore a duplicated listener.
|
|
return false;
|
|
}
|
|
prev = node;
|
|
node = node.next;
|
|
}
|
|
|
|
prev.next = newNode(listener, kind);
|
|
return true;
|
|
},
|
|
configurable: true,
|
|
writable: true
|
|
},
|
|
|
|
removeEventListener: {
|
|
value: function removeEventListener(type, listener, capture) {
|
|
if (listener == null) {
|
|
return false;
|
|
}
|
|
|
|
var kind = (capture ? CAPTURE : BUBBLE);
|
|
var prev = null;
|
|
var node = this[LISTENERS][type];
|
|
while (node != null) {
|
|
if (node.listener === listener && node.kind === kind) {
|
|
if (prev == null) {
|
|
this[LISTENERS][type] = node.next;
|
|
}
|
|
else {
|
|
prev.next = node.next;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
prev = node;
|
|
node = node.next;
|
|
}
|
|
|
|
return false;
|
|
},
|
|
configurable: true,
|
|
writable: true
|
|
},
|
|
|
|
dispatchEvent: {
|
|
value: function dispatchEvent(event) {
|
|
// If listeners aren't registered, terminate.
|
|
var node = this[LISTENERS][event.type];
|
|
if (node == null) {
|
|
return true;
|
|
}
|
|
|
|
// Since we cannot rewrite several properties, so wrap object.
|
|
var wrapped = createEventWrapper(event, this);
|
|
|
|
// This doesn't process capturing phase and bubbling phase.
|
|
// This isn't participating in a tree.
|
|
while (node != null) {
|
|
if (typeof node.listener === "function") {
|
|
node.listener.call(this, wrapped);
|
|
}
|
|
else if (node.kind !== ATTRIBUTE && typeof node.listener.handleEvent === "function") {
|
|
node.listener.handleEvent(wrapped);
|
|
}
|
|
|
|
if (wrapped[STOP_IMMEDIATE_PROPAGATION_FLAG]) {
|
|
break;
|
|
}
|
|
node = node.next;
|
|
}
|
|
|
|
return !wrapped.defaultPrevented;
|
|
},
|
|
configurable: true,
|
|
writable: true
|
|
}
|
|
}
|
|
);
|
|
|
|
},{"./commons":1,"./custom-event-target":2,"./event-wrapper":4}],4:[function(require,module,exports){
|
|
/**
|
|
* @author Toru Nagashima
|
|
* @copyright 2015 Toru Nagashima. All rights reserved.
|
|
* See LICENSE file in root directory for full license.
|
|
*/
|
|
|
|
"use strict";
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Requirements
|
|
//-----------------------------------------------------------------------------
|
|
|
|
var createUniqueKey = require("./commons").createUniqueKey;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Constsnts
|
|
//-----------------------------------------------------------------------------
|
|
|
|
/**
|
|
* The key of the flag which is turned on by `stopImmediatePropagation` method.
|
|
*
|
|
* @type {symbol|string}
|
|
* @private
|
|
*/
|
|
var STOP_IMMEDIATE_PROPAGATION_FLAG =
|
|
createUniqueKey("stop_immediate_propagation_flag");
|
|
|
|
/**
|
|
* The key of the flag which is turned on by `preventDefault` method.
|
|
*
|
|
* @type {symbol|string}
|
|
* @private
|
|
*/
|
|
var CANCELED_FLAG = createUniqueKey("canceled_flag");
|
|
|
|
/**
|
|
* The key of the original event object.
|
|
*
|
|
* @type {symbol|string}
|
|
* @private
|
|
*/
|
|
var ORIGINAL_EVENT = createUniqueKey("original_event");
|
|
|
|
/**
|
|
* Method definitions for the event wrapper.
|
|
*
|
|
* @type {object}
|
|
* @private
|
|
*/
|
|
var wrapperPrototypeDefinition = Object.freeze({
|
|
stopPropagation: Object.freeze({
|
|
value: function stopPropagation() {
|
|
var e = this[ORIGINAL_EVENT];
|
|
if (typeof e.stopPropagation === "function") {
|
|
e.stopPropagation();
|
|
}
|
|
},
|
|
writable: true,
|
|
configurable: true
|
|
}),
|
|
|
|
stopImmediatePropagation: Object.freeze({
|
|
value: function stopImmediatePropagation() {
|
|
this[STOP_IMMEDIATE_PROPAGATION_FLAG] = true;
|
|
|
|
var e = this[ORIGINAL_EVENT];
|
|
if (typeof e.stopImmediatePropagation === "function") {
|
|
e.stopImmediatePropagation();
|
|
}
|
|
},
|
|
writable: true,
|
|
configurable: true
|
|
}),
|
|
|
|
preventDefault: Object.freeze({
|
|
value: function preventDefault() {
|
|
if (this.cancelable === true) {
|
|
this[CANCELED_FLAG] = true;
|
|
}
|
|
|
|
var e = this[ORIGINAL_EVENT];
|
|
if (typeof e.preventDefault === "function") {
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
writable: true,
|
|
configurable: true
|
|
}),
|
|
|
|
defaultPrevented: Object.freeze({
|
|
get: function defaultPrevented() { return this[CANCELED_FLAG]; },
|
|
enumerable: true,
|
|
configurable: true
|
|
})
|
|
});
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Public Interface
|
|
//-----------------------------------------------------------------------------
|
|
|
|
exports.STOP_IMMEDIATE_PROPAGATION_FLAG = STOP_IMMEDIATE_PROPAGATION_FLAG;
|
|
|
|
/**
|
|
* Creates an event wrapper.
|
|
*
|
|
* We cannot modify several properties of `Event` object, so we need to create the wrapper.
|
|
* Plus, this wrapper supports non `Event` objects.
|
|
*
|
|
* @param {Event|{type: string}} event - An original event to create the wrapper.
|
|
* @param {EventTarget} eventTarget - The event target of the event.
|
|
* @returns {Event} The created wrapper. This object is implemented `Event` interface.
|
|
* @private
|
|
*/
|
|
exports.createEventWrapper = function createEventWrapper(event, eventTarget) {
|
|
var timeStamp = (
|
|
typeof event.timeStamp === "number" ? event.timeStamp : Date.now()
|
|
);
|
|
var propertyDefinition = {
|
|
type: {value: event.type, enumerable: true},
|
|
target: {value: eventTarget, enumerable: true},
|
|
currentTarget: {value: eventTarget, enumerable: true},
|
|
eventPhase: {value: 2, enumerable: true},
|
|
bubbles: {value: Boolean(event.bubbles), enumerable: true},
|
|
cancelable: {value: Boolean(event.cancelable), enumerable: true},
|
|
timeStamp: {value: timeStamp, enumerable: true},
|
|
isTrusted: {value: false, enumerable: true}
|
|
};
|
|
propertyDefinition[STOP_IMMEDIATE_PROPAGATION_FLAG] = {value: false, writable: true};
|
|
propertyDefinition[CANCELED_FLAG] = {value: false, writable: true};
|
|
propertyDefinition[ORIGINAL_EVENT] = {value: event};
|
|
|
|
// For CustomEvent.
|
|
if (typeof event.detail !== "undefined") {
|
|
propertyDefinition.detail = {value: event.detail, enumerable: true};
|
|
}
|
|
|
|
return Object.create(
|
|
Object.create(event, wrapperPrototypeDefinition),
|
|
propertyDefinition
|
|
);
|
|
};
|
|
|
|
},{"./commons":1}]},{},[3])(3)
|
|
}); |