mirror of
https://github.com/denoland/deno.git
synced 2025-01-05 05:49:20 -05:00
Add Event web API (#1059)
This commit is contained in:
parent
ad01085406
commit
f44322128b
9 changed files with 504 additions and 35 deletions
2
BUILD.gn
2
BUILD.gn
|
@ -60,6 +60,8 @@ ts_sources = [
|
||||||
"js/dispatch.ts",
|
"js/dispatch.ts",
|
||||||
"js/dom_types.ts",
|
"js/dom_types.ts",
|
||||||
"js/errors.ts",
|
"js/errors.ts",
|
||||||
|
"js/event.ts",
|
||||||
|
"js/event_target.ts",
|
||||||
"js/fetch.ts",
|
"js/fetch.ts",
|
||||||
"js/dom_file.ts",
|
"js/dom_file.ts",
|
||||||
"js/file_info.ts",
|
"js/file_info.ts",
|
||||||
|
|
|
@ -53,14 +53,6 @@ export interface DomIterable<K, V> {
|
||||||
): void;
|
): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Element {
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface HTMLFormElement {
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
type EndingType = "transparent" | "native";
|
type EndingType = "transparent" | "native";
|
||||||
|
|
||||||
export interface BlobPropertyBag {
|
export interface BlobPropertyBag {
|
||||||
|
@ -72,7 +64,7 @@ interface AbortSignalEventMap {
|
||||||
abort: ProgressEvent;
|
abort: ProgressEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface EventTarget {
|
export interface EventTarget {
|
||||||
addEventListener(
|
addEventListener(
|
||||||
type: string,
|
type: string,
|
||||||
listener: EventListenerOrEventListenerObject | null,
|
listener: EventListenerOrEventListenerObject | null,
|
||||||
|
@ -140,39 +132,52 @@ export interface URLSearchParams {
|
||||||
): void;
|
): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface EventListener {
|
export interface EventListener {
|
||||||
(evt: Event): void;
|
(evt: Event): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface EventInit {
|
export interface EventInit {
|
||||||
bubbles?: boolean;
|
bubbles?: boolean;
|
||||||
cancelable?: boolean;
|
cancelable?: boolean;
|
||||||
composed?: boolean;
|
composed?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Event {
|
export enum EventPhase {
|
||||||
readonly bubbles: boolean;
|
NONE = 0,
|
||||||
cancelBubble: boolean;
|
CAPTURING_PHASE = 1,
|
||||||
readonly cancelable: boolean;
|
AT_TARGET = 2,
|
||||||
readonly composed: boolean;
|
BUBBLING_PHASE = 3
|
||||||
readonly currentTarget: EventTarget | null;
|
}
|
||||||
readonly defaultPrevented: boolean;
|
|
||||||
readonly eventPhase: number;
|
export interface EventPath {
|
||||||
readonly isTrusted: boolean;
|
item: EventTarget;
|
||||||
returnValue: boolean;
|
itemInShadowTree: boolean;
|
||||||
readonly srcElement: Element | null;
|
relatedTarget: EventTarget | null;
|
||||||
readonly target: EventTarget | null;
|
rootOfClosedTree: boolean;
|
||||||
readonly timeStamp: number;
|
slotInClosedTree: boolean;
|
||||||
|
target: EventTarget | null;
|
||||||
|
touchTargetList: EventTarget[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Event {
|
||||||
readonly type: string;
|
readonly type: string;
|
||||||
deepPath(): EventTarget[];
|
readonly target: EventTarget | null;
|
||||||
initEvent(type: string, bubbles?: boolean, cancelable?: boolean): void;
|
readonly currentTarget: EventTarget | null;
|
||||||
preventDefault(): void;
|
composedPath(): EventPath[];
|
||||||
stopImmediatePropagation(): void;
|
|
||||||
|
readonly eventPhase: number;
|
||||||
|
|
||||||
stopPropagation(): void;
|
stopPropagation(): void;
|
||||||
readonly AT_TARGET: number;
|
stopImmediatePropagation(): void;
|
||||||
readonly BUBBLING_PHASE: number;
|
|
||||||
readonly CAPTURING_PHASE: number;
|
readonly bubbles: boolean;
|
||||||
readonly NONE: number;
|
readonly cancelable: boolean;
|
||||||
|
preventDefault(): void;
|
||||||
|
readonly defaultPrevented: boolean;
|
||||||
|
readonly composed: boolean;
|
||||||
|
|
||||||
|
readonly isTrusted: boolean;
|
||||||
|
readonly timeStamp: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO(ry) Re-expose this interface. There is currently some interference
|
/* TODO(ry) Re-expose this interface. There is currently some interference
|
||||||
|
@ -193,11 +198,11 @@ interface ProgressEvent extends Event {
|
||||||
readonly total: number;
|
readonly total: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface EventListenerOptions {
|
export interface EventListenerOptions {
|
||||||
capture?: boolean;
|
capture?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AddEventListenerOptions extends EventListenerOptions {
|
export interface AddEventListenerOptions extends EventListenerOptions {
|
||||||
once?: boolean;
|
once?: boolean;
|
||||||
passive?: boolean;
|
passive?: boolean;
|
||||||
}
|
}
|
||||||
|
@ -236,7 +241,7 @@ export interface ReadableStream {
|
||||||
getReader(): ReadableStreamReader;
|
getReader(): ReadableStreamReader;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface EventListenerObject {
|
export interface EventListenerObject {
|
||||||
handleEvent(evt: Event): void;
|
handleEvent(evt: Event): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
252
js/event.ts
Normal file
252
js/event.ts
Normal file
|
@ -0,0 +1,252 @@
|
||||||
|
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
|
||||||
|
import * as domTypes from "./dom_types";
|
||||||
|
import { getPrivateValue } from "./util";
|
||||||
|
|
||||||
|
// WeakMaps are recommended for private attributes (see MDN link below)
|
||||||
|
// tslint:disable-next-line:max-line-length
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Archive/Add-ons/Add-on_SDK/Guides/Contributor_s_Guide/Private_Properties#Using_WeakMaps
|
||||||
|
export const eventAttributes = new WeakMap();
|
||||||
|
|
||||||
|
export class EventInit implements domTypes.EventInit {
|
||||||
|
bubbles = false;
|
||||||
|
cancelable = false;
|
||||||
|
composed = false;
|
||||||
|
|
||||||
|
constructor({ bubbles = false, cancelable = false, composed = false } = {}) {
|
||||||
|
this.bubbles = bubbles;
|
||||||
|
this.cancelable = cancelable;
|
||||||
|
this.composed = composed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Event implements domTypes.Event {
|
||||||
|
// Each event has the following associated flags
|
||||||
|
private _canceledFlag = false;
|
||||||
|
private _inPassiveListenerFlag = false;
|
||||||
|
private _stopImmediatePropagationFlag = false;
|
||||||
|
private _stopPropagationFlag = false;
|
||||||
|
|
||||||
|
// Property for objects on which listeners will be invoked
|
||||||
|
private _path: domTypes.EventPath[] = [];
|
||||||
|
|
||||||
|
constructor(type: string, eventInitDict: domTypes.EventInit = {}) {
|
||||||
|
eventAttributes.set(this, {
|
||||||
|
type,
|
||||||
|
bubbles: eventInitDict.bubbles || false,
|
||||||
|
cancelable: eventInitDict.cancelable || false,
|
||||||
|
composed: eventInitDict.composed || false,
|
||||||
|
currentTarget: null,
|
||||||
|
eventPhase: domTypes.EventPhase.NONE,
|
||||||
|
isTrusted: false,
|
||||||
|
target: null,
|
||||||
|
timeStamp: Date.now()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
get bubbles(): boolean {
|
||||||
|
return getPrivateValue(this, eventAttributes, "bubbles");
|
||||||
|
}
|
||||||
|
|
||||||
|
get cancelBubble(): boolean {
|
||||||
|
return this._stopPropagationFlag;
|
||||||
|
}
|
||||||
|
|
||||||
|
get cancelBubbleImmediately(): boolean {
|
||||||
|
return this._stopImmediatePropagationFlag;
|
||||||
|
}
|
||||||
|
|
||||||
|
get cancelable(): boolean {
|
||||||
|
return getPrivateValue(this, eventAttributes, "cancelable");
|
||||||
|
}
|
||||||
|
|
||||||
|
get composed(): boolean {
|
||||||
|
return getPrivateValue(this, eventAttributes, "composed");
|
||||||
|
}
|
||||||
|
|
||||||
|
get currentTarget(): domTypes.EventTarget {
|
||||||
|
return getPrivateValue(this, eventAttributes, "currentTarget");
|
||||||
|
}
|
||||||
|
|
||||||
|
get defaultPrevented(): boolean {
|
||||||
|
return this._canceledFlag;
|
||||||
|
}
|
||||||
|
|
||||||
|
get eventPhase(): number {
|
||||||
|
return getPrivateValue(this, eventAttributes, "eventPhase");
|
||||||
|
}
|
||||||
|
|
||||||
|
get isTrusted(): boolean {
|
||||||
|
return getPrivateValue(this, eventAttributes, "isTrusted");
|
||||||
|
}
|
||||||
|
|
||||||
|
get target(): domTypes.EventTarget {
|
||||||
|
return getPrivateValue(this, eventAttributes, "target");
|
||||||
|
}
|
||||||
|
|
||||||
|
get timeStamp(): Date {
|
||||||
|
return getPrivateValue(this, eventAttributes, "timeStamp");
|
||||||
|
}
|
||||||
|
|
||||||
|
get type(): string {
|
||||||
|
return getPrivateValue(this, eventAttributes, "type");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the event’s path (objects on which listeners will be
|
||||||
|
* invoked). This does not include nodes in shadow trees if the
|
||||||
|
* shadow root was created with its ShadowRoot.mode closed.
|
||||||
|
*
|
||||||
|
* event.composedPath();
|
||||||
|
*/
|
||||||
|
composedPath(): domTypes.EventPath[] {
|
||||||
|
if (this._path.length === 0) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const composedPath: domTypes.EventPath[] = [
|
||||||
|
{
|
||||||
|
item: this.currentTarget,
|
||||||
|
itemInShadowTree: false,
|
||||||
|
relatedTarget: null,
|
||||||
|
rootOfClosedTree: false,
|
||||||
|
slotInClosedTree: false,
|
||||||
|
target: null,
|
||||||
|
touchTargetList: []
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
let currentTargetIndex = 0;
|
||||||
|
let currentTargetHiddenSubtreeLevel = 0;
|
||||||
|
|
||||||
|
for (let index = this._path.length - 1; index >= 0; index--) {
|
||||||
|
const { item, rootOfClosedTree, slotInClosedTree } = this._path[index];
|
||||||
|
|
||||||
|
if (rootOfClosedTree) {
|
||||||
|
currentTargetHiddenSubtreeLevel++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item === this.currentTarget) {
|
||||||
|
currentTargetIndex = index;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slotInClosedTree) {
|
||||||
|
currentTargetHiddenSubtreeLevel--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentHiddenLevel = currentTargetHiddenSubtreeLevel;
|
||||||
|
let maxHiddenLevel = currentTargetHiddenSubtreeLevel;
|
||||||
|
|
||||||
|
for (let i = currentTargetIndex - 1; i >= 0; i--) {
|
||||||
|
const { item, rootOfClosedTree, slotInClosedTree } = this._path[i];
|
||||||
|
|
||||||
|
if (rootOfClosedTree) {
|
||||||
|
currentHiddenLevel++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentHiddenLevel <= maxHiddenLevel) {
|
||||||
|
composedPath.unshift({
|
||||||
|
item,
|
||||||
|
itemInShadowTree: false,
|
||||||
|
relatedTarget: null,
|
||||||
|
rootOfClosedTree: false,
|
||||||
|
slotInClosedTree: false,
|
||||||
|
target: null,
|
||||||
|
touchTargetList: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slotInClosedTree) {
|
||||||
|
currentHiddenLevel--;
|
||||||
|
|
||||||
|
if (currentHiddenLevel < maxHiddenLevel) {
|
||||||
|
maxHiddenLevel = currentHiddenLevel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentHiddenLevel = currentTargetHiddenSubtreeLevel;
|
||||||
|
maxHiddenLevel = currentTargetHiddenSubtreeLevel;
|
||||||
|
|
||||||
|
for (
|
||||||
|
let index = currentTargetIndex + 1;
|
||||||
|
index < this._path.length;
|
||||||
|
index++
|
||||||
|
) {
|
||||||
|
const { item, rootOfClosedTree, slotInClosedTree } = this._path[index];
|
||||||
|
|
||||||
|
if (slotInClosedTree) {
|
||||||
|
currentHiddenLevel++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentHiddenLevel <= maxHiddenLevel) {
|
||||||
|
composedPath.push({
|
||||||
|
item,
|
||||||
|
itemInShadowTree: false,
|
||||||
|
relatedTarget: null,
|
||||||
|
rootOfClosedTree: false,
|
||||||
|
slotInClosedTree: false,
|
||||||
|
target: null,
|
||||||
|
touchTargetList: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rootOfClosedTree) {
|
||||||
|
currentHiddenLevel--;
|
||||||
|
|
||||||
|
if (currentHiddenLevel < maxHiddenLevel) {
|
||||||
|
maxHiddenLevel = currentHiddenLevel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return composedPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Cancels the event (if it is cancelable).
|
||||||
|
* See https://dom.spec.whatwg.org/#set-the-canceled-flag
|
||||||
|
*
|
||||||
|
* event.preventDefault();
|
||||||
|
*/
|
||||||
|
preventDefault(): void {
|
||||||
|
if (this.cancelable && !this._inPassiveListenerFlag) {
|
||||||
|
this._canceledFlag = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Stops the propagation of events further along in the DOM.
|
||||||
|
*
|
||||||
|
* event.stopPropagation();
|
||||||
|
*/
|
||||||
|
stopPropagation(): void {
|
||||||
|
this._stopPropagationFlag = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** For this particular event, no other listener will be called.
|
||||||
|
* Neither those attached on the same element, nor those attached
|
||||||
|
* on elements which will be traversed later (in capture phase,
|
||||||
|
* for instance).
|
||||||
|
*
|
||||||
|
* event.stopImmediatePropagation();
|
||||||
|
*/
|
||||||
|
stopImmediatePropagation(): void {
|
||||||
|
this._stopPropagationFlag = true;
|
||||||
|
this._stopImmediatePropagationFlag = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Built-in objects providing `get` methods for our
|
||||||
|
* interceptable JavaScript operations.
|
||||||
|
*/
|
||||||
|
Reflect.defineProperty(Event.prototype, "bubbles", { enumerable: true });
|
||||||
|
Reflect.defineProperty(Event.prototype, "cancelable", { enumerable: true });
|
||||||
|
Reflect.defineProperty(Event.prototype, "composed", { enumerable: true });
|
||||||
|
Reflect.defineProperty(Event.prototype, "currentTarget", { enumerable: true });
|
||||||
|
Reflect.defineProperty(Event.prototype, "defaultPrevented", {
|
||||||
|
enumerable: true
|
||||||
|
});
|
||||||
|
Reflect.defineProperty(Event.prototype, "eventPhase", { enumerable: true });
|
||||||
|
Reflect.defineProperty(Event.prototype, "isTrusted", { enumerable: true });
|
||||||
|
Reflect.defineProperty(Event.prototype, "target", { enumerable: true });
|
||||||
|
Reflect.defineProperty(Event.prototype, "timeStamp", { enumerable: true });
|
||||||
|
Reflect.defineProperty(Event.prototype, "type", { enumerable: true });
|
52
js/event_target.ts
Normal file
52
js/event_target.ts
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
|
||||||
|
import * as domTypes from "./dom_types";
|
||||||
|
|
||||||
|
/* TODO: This is an incomplete implementation to provide functionality
|
||||||
|
* for Event. A proper spec is still required for a proper Web API.
|
||||||
|
*/
|
||||||
|
export class EventTarget implements domTypes.EventTarget {
|
||||||
|
public listeners: {
|
||||||
|
[type in string]: domTypes.EventListenerOrEventListenerObject[]
|
||||||
|
} = {};
|
||||||
|
|
||||||
|
public addEventListener(
|
||||||
|
type: string,
|
||||||
|
listener: domTypes.EventListenerOrEventListenerObject | null,
|
||||||
|
options?: boolean | domTypes.AddEventListenerOptions
|
||||||
|
): void {
|
||||||
|
if (!(type in this.listeners)) {
|
||||||
|
this.listeners[type] = [];
|
||||||
|
}
|
||||||
|
if (listener !== null) {
|
||||||
|
this.listeners[type].push(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public removeEventListener(
|
||||||
|
type: string,
|
||||||
|
callback: domTypes.EventListenerOrEventListenerObject | null,
|
||||||
|
options?: domTypes.EventListenerOptions | boolean
|
||||||
|
): void {
|
||||||
|
if (type in this.listeners && callback !== null) {
|
||||||
|
this.listeners[type] = this.listeners[type].filter(
|
||||||
|
listener => listener !== callback
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public dispatchEvent(event: domTypes.Event): boolean {
|
||||||
|
if (!(event.type in this.listeners)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const stack = this.listeners[event.type].slice();
|
||||||
|
|
||||||
|
for (const stackElement of stack) {
|
||||||
|
if ((stackElement as domTypes.EventListenerObject).handleEvent) {
|
||||||
|
(stackElement as domTypes.EventListenerObject).handleEvent(event);
|
||||||
|
} else {
|
||||||
|
(stackElement as domTypes.EventListener).call(this, event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return !event.defaultPrevented;
|
||||||
|
}
|
||||||
|
}
|
66
js/event_target_test.ts
Normal file
66
js/event_target_test.ts
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
|
||||||
|
import { test, assert, assertEqual } from "./test_util.ts";
|
||||||
|
|
||||||
|
test(function addEventListenerTest() {
|
||||||
|
const document = new EventTarget();
|
||||||
|
|
||||||
|
assertEqual(document.addEventListener("x", null, false), undefined);
|
||||||
|
assertEqual(document.addEventListener("x", null, true), undefined);
|
||||||
|
assertEqual(document.addEventListener("x", null), undefined);
|
||||||
|
});
|
||||||
|
|
||||||
|
test(function constructedEventTargetCanBeUsedAsExpected() {
|
||||||
|
const target = new EventTarget();
|
||||||
|
const event = new Event("foo", { bubbles: true, cancelable: false });
|
||||||
|
let callCount = 0;
|
||||||
|
|
||||||
|
function listener(e) {
|
||||||
|
assertEqual(e, event);
|
||||||
|
++callCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
target.addEventListener("foo", listener);
|
||||||
|
|
||||||
|
target.dispatchEvent(event);
|
||||||
|
assertEqual(callCount, 1);
|
||||||
|
|
||||||
|
target.dispatchEvent(event);
|
||||||
|
assertEqual(callCount, 2);
|
||||||
|
|
||||||
|
target.removeEventListener("foo", listener);
|
||||||
|
target.dispatchEvent(event);
|
||||||
|
assertEqual(callCount, 2);
|
||||||
|
});
|
||||||
|
|
||||||
|
test(function anEventTargetCanBeSubclassed() {
|
||||||
|
class NicerEventTarget extends EventTarget {
|
||||||
|
on(type, listener?, options?) {
|
||||||
|
this.addEventListener(type, listener, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
off(type, callback?, options?) {
|
||||||
|
this.removeEventListener(type, callback, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const target = new NicerEventTarget();
|
||||||
|
const event = new Event("foo", { bubbles: true, cancelable: false });
|
||||||
|
let callCount = 0;
|
||||||
|
|
||||||
|
function listener() {
|
||||||
|
++callCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
target.on("foo", listener);
|
||||||
|
assertEqual(callCount, 0);
|
||||||
|
|
||||||
|
target.off("foo", listener);
|
||||||
|
assertEqual(callCount, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test(function removingNullEventListenerShouldSucceed() {
|
||||||
|
const document = new EventTarget();
|
||||||
|
assertEqual(document.removeEventListener("x", null, false), undefined);
|
||||||
|
assertEqual(document.removeEventListener("x", null, true), undefined);
|
||||||
|
assertEqual(document.removeEventListener("x", null), undefined);
|
||||||
|
});
|
70
js/event_test.ts
Normal file
70
js/event_test.ts
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
|
||||||
|
import { test, assertEqual } from "./test_util.ts";
|
||||||
|
|
||||||
|
test(function eventInitializedWithType() {
|
||||||
|
const type = "click";
|
||||||
|
const event = new Event(type);
|
||||||
|
|
||||||
|
assertEqual(event.isTrusted, false);
|
||||||
|
assertEqual(event.target, null);
|
||||||
|
assertEqual(event.currentTarget, null);
|
||||||
|
assertEqual(event.type, "click");
|
||||||
|
assertEqual(event.bubbles, false);
|
||||||
|
assertEqual(event.cancelable, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test(function eventInitializedWithTypeAndDict() {
|
||||||
|
const init = "submit";
|
||||||
|
const eventInitDict = new EventInit({ bubbles: true, cancelable: true });
|
||||||
|
const event = new Event(init, eventInitDict);
|
||||||
|
|
||||||
|
assertEqual(event.isTrusted, false);
|
||||||
|
assertEqual(event.target, null);
|
||||||
|
assertEqual(event.currentTarget, null);
|
||||||
|
assertEqual(event.type, "submit");
|
||||||
|
assertEqual(event.bubbles, true);
|
||||||
|
assertEqual(event.cancelable, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test(function eventComposedPathSuccess() {
|
||||||
|
const type = "click";
|
||||||
|
const event = new Event(type);
|
||||||
|
const composedPath = event.composedPath();
|
||||||
|
|
||||||
|
assertEqual(composedPath, []);
|
||||||
|
});
|
||||||
|
|
||||||
|
test(function eventStopPropagationSuccess() {
|
||||||
|
const type = "click";
|
||||||
|
const event = new Event(type);
|
||||||
|
|
||||||
|
assertEqual(event.cancelBubble, false);
|
||||||
|
event.stopPropagation();
|
||||||
|
assertEqual(event.cancelBubble, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test(function eventStopImmediatePropagationSuccess() {
|
||||||
|
const type = "click";
|
||||||
|
const event = new Event(type);
|
||||||
|
|
||||||
|
assertEqual(event.cancelBubble, false);
|
||||||
|
assertEqual(event.cancelBubbleImmediately, false);
|
||||||
|
event.stopImmediatePropagation();
|
||||||
|
assertEqual(event.cancelBubble, true);
|
||||||
|
assertEqual(event.cancelBubbleImmediately, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test(function eventPreventDefaultSuccess() {
|
||||||
|
const type = "click";
|
||||||
|
const event = new Event(type);
|
||||||
|
|
||||||
|
assertEqual(event.defaultPrevented, false);
|
||||||
|
event.preventDefault();
|
||||||
|
assertEqual(event.defaultPrevented, false);
|
||||||
|
|
||||||
|
const eventInitDict = new EventInit({ bubbles: true, cancelable: true });
|
||||||
|
const cancelableEvent = new Event(type, eventInitDict);
|
||||||
|
assertEqual(cancelableEvent.defaultPrevented, false);
|
||||||
|
cancelableEvent.preventDefault();
|
||||||
|
assertEqual(cancelableEvent.defaultPrevented, true);
|
||||||
|
});
|
|
@ -10,6 +10,8 @@
|
||||||
import * as blob from "./blob";
|
import * as blob from "./blob";
|
||||||
import * as consoleTypes from "./console";
|
import * as consoleTypes from "./console";
|
||||||
import * as domTypes from "./dom_types";
|
import * as domTypes from "./dom_types";
|
||||||
|
import * as event from "./event";
|
||||||
|
import * as eventTarget from "./event_target";
|
||||||
import * as formData from "./form_data";
|
import * as formData from "./form_data";
|
||||||
import * as fetchTypes from "./fetch";
|
import * as fetchTypes from "./fetch";
|
||||||
import * as headers from "./headers";
|
import * as headers from "./headers";
|
||||||
|
@ -61,6 +63,12 @@ export type Blob = blob.DenoBlob;
|
||||||
// window.File = file.DenoFile;
|
// window.File = file.DenoFile;
|
||||||
// export type File = file.DenoFile;
|
// export type File = file.DenoFile;
|
||||||
|
|
||||||
|
window.EventInit = event.EventInit;
|
||||||
|
export type EventInit = event.EventInit;
|
||||||
|
window.Event = event.Event;
|
||||||
|
export type Event = event.Event;
|
||||||
|
window.EventTarget = eventTarget.EventTarget;
|
||||||
|
export type EventTarget = eventTarget.EventTarget;
|
||||||
window.URL = url.URL;
|
window.URL = url.URL;
|
||||||
export type URL = url.URL;
|
export type URL = url.URL;
|
||||||
window.URLSearchParams = urlSearchParams.URLSearchParams;
|
window.URLSearchParams = urlSearchParams.URLSearchParams;
|
||||||
|
|
|
@ -10,6 +10,8 @@ import "./compiler_test.ts";
|
||||||
import "./console_test.ts";
|
import "./console_test.ts";
|
||||||
import "./copy_file_test.ts";
|
import "./copy_file_test.ts";
|
||||||
import "./dir_test.ts";
|
import "./dir_test.ts";
|
||||||
|
import "./event_test.ts";
|
||||||
|
import "./event_target_test.ts";
|
||||||
import "./fetch_test.ts";
|
import "./fetch_test.ts";
|
||||||
// TODO The global "File" has been temporarily disabled. See the note in
|
// TODO The global "File" has been temporarily disabled. See the note in
|
||||||
// js/globals.ts
|
// js/globals.ts
|
||||||
|
|
12
js/util.ts
12
js/util.ts
|
@ -151,3 +151,15 @@ export function requiredArguments(
|
||||||
throw new TypeError(errMsg);
|
throw new TypeError(errMsg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns values from a WeakMap to emulate private properties in JavaScript
|
||||||
|
export function getPrivateValue<
|
||||||
|
K extends object,
|
||||||
|
V extends object,
|
||||||
|
W extends keyof V
|
||||||
|
>(instance: K, weakMap: WeakMap<K, V>, key: W): V[W] {
|
||||||
|
if (weakMap.has(instance)) {
|
||||||
|
return weakMap.get(instance)![key];
|
||||||
|
}
|
||||||
|
throw new TypeError("Illegal invocation");
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue