event-target-shim
Advanced tools
Comparing version 0.0.1 to 0.1.0
{ | ||
"name": "event-target-shim", | ||
"version": "0.0.1", | ||
"version": "0.1.0", | ||
"description": "A polyfill for W3C EventTarget Constructor.", | ||
"main": "lib/index.js", | ||
"main": "lib/EventTarget.js", | ||
"directories": { | ||
@@ -7,0 +7,0 @@ "test": "test" |
@@ -10,10 +10,7 @@ # event-target-shim | ||
~~This module uses native implementation of EventTarget if it exists.~~ | ||
~~otherwise, defines shim.~~ | ||
This module provides `EventTarget` constructor that can inherit for your custom object. | ||
And this module provides an utility to define properties for attribute listeners (e.g. `obj.onclick`). | ||
Ummm, occured `Illegal constructor` exception at native. | ||
This module always defines shim. | ||
If `window.EventTarget` exists, `EventTarget` is inherit from `window.EventTarget`. | ||
And this provides an utility to define properties for attribute listeners (e.g. `obj.onclick`). | ||
```ts | ||
@@ -39,3 +36,3 @@ declare class EventTarget { | ||
This module has been desined that uses together [Browserify](http://browserify.org/). | ||
This module has been designed that uses together [Browserify](http://browserify.org/). | ||
@@ -42,0 +39,0 @@ ```js |
"use strict"; | ||
import {createEventWrapper, STOP_IMMEDIATE_PROPAGATION_FLAG, DISPATCH_FLAG, | ||
CANCELED_FLAG} from "./EventWrapper"; | ||
let EventTarget; | ||
if (false/*typeof window !== "undefined" && typeof window.EventTarget !== "undefined"*/) { | ||
// Occored `Illegal constructor` exception when called. | ||
EventTarget = window.EventTarget; | ||
const LISTENERS = Symbol("listeners"); | ||
const SET_ATTRIBUTE_LISTENER = Symbol("setAttributeListener"); | ||
const GET_ATTRIBUTE_LISTENER = Symbol("getAttributeListener"); | ||
const CAPTURE = 1; | ||
const BUBBLE = 2; | ||
const ATTRIBUTE = 3; | ||
// Return definition of an attribute listener. | ||
function defineAttributeListener(type) { | ||
type = type.replace(/"/g, "\\\""); | ||
return ` | ||
"on${type}": { | ||
get: function() { return this[GET_ATTRIBUTE_LISTENER]("${type}"); }, | ||
set: function(value) { this[SET_ATTRIBUTE_LISTENER]("${type}", value); }, | ||
configurable: true | ||
}, | ||
`; | ||
} | ||
else { | ||
// Event's private members. | ||
const STOP_IMMEDIATE_PROPAGATION_FLAG = Symbol("stop immediate propagation flag"); | ||
const CANCELED_FLAG = Symbol("canceled flag"); | ||
const DISPATCH_FLAG = Symbol("dispatch flag"); | ||
// EventTarget's private members. | ||
const LISTENERS = Symbol("listeners"); | ||
// Create a LinkedList structure for EventListener. | ||
function newNode(listener, capture) { | ||
return {listener, capture, next: null}; | ||
// Create a LinkedList structure for EventListener. | ||
function newNode(listener, kind) { | ||
return {listener, kind, next: null}; | ||
} | ||
// | ||
// Class Definition. | ||
// | ||
let ET = function EventTarget(...types) { | ||
if (this instanceof EventTarget) { | ||
// this[LISTENERS] is a Map. | ||
// Its key is event type. | ||
// Its value is ListenerNode object or null. | ||
// | ||
// interface ListenerNode { | ||
// let listener: Function | ||
// let kind: CAPTURE|BUBBLE|ATTRIBUTE | ||
// let next: ListenerNode|null | ||
// } | ||
this[LISTENERS] = Object.create(null); | ||
} | ||
else if (types.length > 0) { | ||
// To use to extend with attribute listener properties. | ||
// e.g. | ||
// class MyCustomObject extends EventTarget("message", "error") { | ||
// //... | ||
// } | ||
return Function( | ||
"EventTargetBase", | ||
"GET_ATTRIBUTE_LISTENER", | ||
"SET_ATTRIBUTE_LISTENER", | ||
` | ||
function EventTarget() { | ||
EventTargetBase.call(this); | ||
} | ||
EventTarget.prototype = Object.create(EventTargetBase.prototype, { | ||
${types.map(defineAttributeListener).join()} | ||
constructor: { | ||
value: EventTarget, | ||
writable: true, | ||
configurable: true | ||
} | ||
}); | ||
return EventTarget; | ||
` | ||
)(EventTarget, GET_ATTRIBUTE_LISTENER, SET_ATTRIBUTE_LISTENER); | ||
} | ||
else { | ||
throw new TypeError("Cannot call a class as a function"); | ||
} | ||
}; | ||
// Create a Event wrapper to rewrite readonly properties. | ||
function newEventWrapper(event, eventTarget) { | ||
let timeStamp = (typeof event.timeStamp === "number" | ||
? event.timeStamp | ||
: Date.now()); | ||
ET.prototype = Object.create( | ||
(typeof EventTarget === "function" ? EventTarget : Object).prototype, | ||
{ | ||
constructor: { | ||
value: ET, | ||
writable: true, | ||
configurable: true | ||
}, | ||
return Object.create(event, { | ||
type: {value: event.type, enumerable: true}, | ||
target: {value: eventTarget, enumerable: true}, | ||
currentTarget: {value: eventTarget, enumerable: true}, | ||
eventPhase: {value: 2, enumerable: true}, | ||
stopPropagation: {value: function stopPropagation() {}}, | ||
stopImmediatePropagation: {value: function stopImmediatePropagation() { | ||
this[STOP_IMMEDIATE_PROPAGATION_FLAG] = true; | ||
}}, | ||
bubbles: {value: Boolean(event.bubbles), enumerable: true}, | ||
cancelable: {value: Boolean(event.cancelable), enumerable: true}, | ||
preventDefault: {value: function preventDefault() { | ||
if (this.cancelable === true) { | ||
this[CANCELED_FLAG] = true; | ||
addEventListener: { | ||
value: function addEventListener(type, listener, capture = false) { | ||
if (listener == null) { | ||
return false; | ||
} | ||
}}, | ||
defaultPrevented: { | ||
get: function() { return this[CANCELED_FLAG]; }, | ||
enumerable: true | ||
}, | ||
isTrusted: {value: false, enumerable: true}, | ||
timeStamp: {value: timeStamp, enumerable: true}, | ||
[STOP_IMMEDIATE_PROPAGATION_FLAG]: {value: false, writable: true}, | ||
[CANCELED_FLAG]: {value: false, writable: true}, | ||
[DISPATCH_FLAG]: {value: true} | ||
}); | ||
} | ||
if (typeof listener !== "function") { | ||
throw TypeError("listener should be a function."); | ||
} | ||
// Define EventTarget. | ||
// See Also: https://dom.spec.whatwg.org/#interface-eventtarget | ||
EventTarget = class EventTarget { | ||
constructor() { | ||
// This object is a Map. | ||
// Its key is event type. | ||
// Its value is ListenerNode object or null. | ||
// | ||
// interface ListenerNode { | ||
// let listener: Function | ||
// let capture: boolean | ||
// let next: ListenerNode|null | ||
// } | ||
this[LISTENERS] = Object.create(null); | ||
} | ||
let kind = (capture ? CAPTURE : BUBBLE); | ||
let node = this[LISTENERS][type]; | ||
if (node == null) { | ||
this[LISTENERS][type] = newNode(listener, kind); | ||
return true; | ||
} | ||
addEventListener(type, listener, capture = false) { | ||
if (listener == null) { | ||
return false; | ||
} | ||
capture = Boolean(capture); | ||
let prev = null; | ||
while (node != null) { | ||
if (node.listener === listener && node.kind === kind) { | ||
// Should ignore a duplicated listener. | ||
return false; | ||
} | ||
prev = node; | ||
node = node.next; | ||
} | ||
let node = this[LISTENERS][type]; | ||
if (node == null) { | ||
this[LISTENERS][type] = newNode(listener, capture); | ||
prev.next = newNode(listener, kind); | ||
return true; | ||
} | ||
}, | ||
writable: true, | ||
configurable: true | ||
}, | ||
let prev = null; | ||
while (node != null) { | ||
if (node.listener === listener && node.capture === capture) { | ||
// Should ignore a duplicated listener. | ||
removeEventListener: { | ||
value: function removeEventListener(type, listener, capture = false) { | ||
if (listener == null) { | ||
return false; | ||
} | ||
prev = node; | ||
node = node.next; | ||
} | ||
prev.next = newNode(listener, capture); | ||
return true; | ||
} | ||
let kind = (capture ? CAPTURE : BUBBLE); | ||
let prev = null; | ||
let 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; | ||
} | ||
removeEventListener(type, listener, capture = false) { | ||
if (listener == null) { | ||
prev = node; | ||
node = node.next; | ||
} | ||
return false; | ||
} | ||
capture = Boolean(capture); | ||
}, | ||
writable: true, | ||
configurable: true | ||
}, | ||
let prev = null; | ||
let node = this[LISTENERS][type]; | ||
while (node != null) { | ||
if (node.listener === listener && node.capture === capture) { | ||
if (prev == null) { | ||
this[LISTENERS][type] = node.next; | ||
} | ||
else { | ||
prev.next = node.next; | ||
} | ||
dispatchEvent: { | ||
value: function dispatchEvent(event) { | ||
// Should check initialized flag, but impossible. | ||
if (event[DISPATCH_FLAG]) { | ||
throw Error("InvalidStateError"); | ||
} | ||
// If listeners aren't registered, terminate. | ||
let node = this[LISTENERS][event.type]; | ||
if (node == null) { | ||
return true; | ||
} | ||
prev = node; | ||
node = node.next; | ||
} | ||
// Since we cannot rewrite several properties, so wrap object. | ||
event = createEventWrapper(event, this); | ||
return false; | ||
} | ||
// This doesn't process capturing phase and bubbling phase. | ||
// This isn't participating in a tree. | ||
while (node != null) { | ||
node.listener.call(this, event); | ||
if (event[STOP_IMMEDIATE_PROPAGATION_FLAG]) { | ||
break; | ||
} | ||
node = node.next; | ||
} | ||
dispatchEvent(event) { | ||
// Should check initialized flag, but impossible. | ||
if (event[DISPATCH_FLAG]) { | ||
throw Error("InvalidStateError"); | ||
} | ||
return !event[CANCELED_FLAG]; | ||
}, | ||
writable: true, | ||
configurable: true | ||
}, | ||
// If listeners aren't registered, terminate. | ||
let node = this[LISTENERS][event.type]; | ||
if (node == null) { | ||
return true; | ||
} | ||
[GET_ATTRIBUTE_LISTENER]: { | ||
value: function getAttributeListener(type) { | ||
let node = this[LISTENERS][type]; | ||
while (node != null) { | ||
if (node.kind === ATTRIBUTE) { | ||
return node.listener; | ||
} | ||
node = node.next; | ||
} | ||
return null; | ||
}, | ||
writable: true, | ||
configurable: true | ||
}, | ||
// Since we cannot rewrite several properties, so wrap object. | ||
event = newEventWrapper(event, this); | ||
[SET_ATTRIBUTE_LISTENER]: { | ||
value: function setAttributeListener(type, listener) { | ||
if (listener != null && typeof listener !== "function") { | ||
throw TypeError("listener should be a function."); | ||
} | ||
// This doesn't process capturing phase and bubbling phase. | ||
// This isn't participating in a tree. | ||
while (node != null) { | ||
node.listener.call(this, event); | ||
if (event[STOP_IMMEDIATE_PROPAGATION_FLAG]) { | ||
break; | ||
let prev = null; | ||
let node = this[LISTENERS][type]; | ||
while (node != null) { | ||
if (node.kind === ATTRIBUTE) { | ||
// Remove old value. | ||
if (prev == null) { | ||
this[LISTENERS][type] = node.next; | ||
} | ||
else { | ||
prev.next = node.next; | ||
} | ||
} | ||
else { | ||
prev = node; | ||
} | ||
node = node.next; | ||
} | ||
node = node.next; | ||
} | ||
return !event[CANCELED_FLAG]; | ||
// Add new value. | ||
if (listener != null) { | ||
if (prev == null) { | ||
this[LISTENERS][type] = newNode(listener, ATTRIBUTE); | ||
} | ||
else { | ||
prev.next = newNode(listener, ATTRIBUTE); | ||
} | ||
} | ||
}, | ||
writable: true, | ||
configurable: true | ||
} | ||
}; | ||
} | ||
} | ||
); | ||
export default EventTarget; | ||
export default ET; |
@@ -5,3 +5,3 @@ "use strict"; | ||
import spies from "chai-spies"; | ||
import EventTarget from "../src/index"; | ||
import EventTarget from "../src/EventTarget"; | ||
@@ -37,2 +37,12 @@ chai.use(spies); | ||
if (typeof window !== "undefined" && typeof window.EventTarget !== "undefined") { | ||
it("should be instanceof `window.EventTarget`.", () => { | ||
expect(target).to.be.instanceof(window.EventTarget); | ||
}); | ||
it("should not equal `EventTarget` and `window.EventTarget`.", () => { | ||
expect(EventTarget).to.not.equal(window.EventTarget); | ||
}); | ||
} | ||
it("should call registered listeners on called `dispatchEvent()`.", () => { | ||
@@ -192,2 +202,6 @@ let lastEvent = null; | ||
it("should properties of attribute listener are null by default.", () => { | ||
expect(target.ontest).to.be.null; | ||
}); | ||
it("should call attribute listeners when called `dispatchEvent()`.", () => { | ||
@@ -199,2 +213,3 @@ let listener = chai.spy(); | ||
expect(target.ontest).to.equal(listener); | ||
expect(listener).to.have.been.called.once; | ||
@@ -210,2 +225,3 @@ }); | ||
expect(target.ontest).to.be.null; | ||
expect(listener).to.not.have.been.called(); | ||
@@ -221,4 +237,5 @@ }); | ||
expect(target.ontest).to.equal(listener); | ||
expect(listener).to.have.been.called.once; | ||
}); | ||
}); |
Uses eval
Supply chain riskPackage uses eval() which is a dangerous function. This prevents the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses eval() which is a dangerous function. This prevents the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
19661
476
62