- Delete old Vite+Svelte frontend - Initialize new SvelteKit project with TypeScript - Configure Tailwind CSS v4 + DaisyUI - Implement JWT authentication with auto-refresh - Create login page with form validation (Zod) - Add protected route guards - Update Docker configuration for single-stage build - Add E2E tests with Playwright (6/11 passing) - Fix Svelte 5 reactivity with $state() runes Known issues: - 5 E2E tests failing (timing/async issues) - Token refresh implementation needs debugging - Validation error display timing
309 lines
13 KiB
JavaScript
309 lines
13 KiB
JavaScript
"use strict";
|
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
if (k2 === undefined) k2 = k;
|
|
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
}
|
|
Object.defineProperty(o, k2, desc);
|
|
}) : (function(o, m, k, k2) {
|
|
if (k2 === undefined) k2 = k;
|
|
o[k2] = m[k];
|
|
}));
|
|
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
}) : function(o, v) {
|
|
o["default"] = v;
|
|
});
|
|
var __importStar = (this && this.__importStar) || (function () {
|
|
var ownKeys = function(o) {
|
|
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
var ar = [];
|
|
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
return ar;
|
|
};
|
|
return ownKeys(o);
|
|
};
|
|
return function (mod) {
|
|
if (mod && mod.__esModule) return mod;
|
|
var result = {};
|
|
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
__setModuleDefault(result, mod);
|
|
return result;
|
|
};
|
|
})();
|
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
};
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
const PropertySymbol = __importStar(require("../PropertySymbol.cjs"));
|
|
const EventPhaseEnum_js_1 = __importDefault(require("./EventPhaseEnum.cjs"));
|
|
const WindowBrowserContext_js_1 = __importDefault(require("../window/WindowBrowserContext.cjs"));
|
|
const BrowserErrorCaptureEnum_js_1 = __importDefault(require("../browser/enums/BrowserErrorCaptureEnum.cjs"));
|
|
/**
|
|
* Handles events.
|
|
*/
|
|
class EventTarget {
|
|
[PropertySymbol.listeners] = {
|
|
capturing: new Map(),
|
|
bubbling: new Map()
|
|
};
|
|
[PropertySymbol.listenerOptions] = {
|
|
capturing: new Map(),
|
|
bubbling: new Map()
|
|
};
|
|
/**
|
|
* Return a default description for the EventTarget class.
|
|
*/
|
|
get [Symbol.toStringTag]() {
|
|
return 'EventTarget';
|
|
}
|
|
/**
|
|
* Adds an event listener.
|
|
*
|
|
* @param type Event type.
|
|
* @param listener Listener.
|
|
* @param options An object that specifies characteristics about the event listener.(currently only once)
|
|
* @param options.once
|
|
* @param options.signal An AbortSignal. The listener will be removed when the given AbortSignal object's abort() method is called.
|
|
*/
|
|
addEventListener(type, listener, options) {
|
|
options = typeof options === 'boolean' ? { capture: options } : options || {};
|
|
const eventPhase = options.capture ? 'capturing' : 'bubbling';
|
|
let listeners = this[PropertySymbol.listeners][eventPhase].get(type);
|
|
let listenerOptions = this[PropertySymbol.listenerOptions][eventPhase].get(type);
|
|
if (!listeners) {
|
|
listeners = [];
|
|
listenerOptions = [];
|
|
this[PropertySymbol.listeners][eventPhase].set(type, listeners);
|
|
this[PropertySymbol.listenerOptions][eventPhase].set(type, listenerOptions);
|
|
}
|
|
if (listeners.includes(listener)) {
|
|
return;
|
|
}
|
|
listeners.push(listener);
|
|
listenerOptions.push(options);
|
|
if (options.signal && !options.signal.aborted) {
|
|
options.signal.addEventListener('abort', () => {
|
|
this.removeEventListener(type, listener);
|
|
});
|
|
}
|
|
}
|
|
/**
|
|
* Adds an event listener.
|
|
*
|
|
* @param type Event type.
|
|
* @param listener Listener.
|
|
*/
|
|
removeEventListener(type, listener) {
|
|
const bubblingListeners = this[PropertySymbol.listeners].bubbling.get(type);
|
|
if (bubblingListeners) {
|
|
const index = bubblingListeners.indexOf(listener);
|
|
if (index !== -1) {
|
|
bubblingListeners.splice(index, 1);
|
|
this[PropertySymbol.listenerOptions].bubbling.get(type).splice(index, 1);
|
|
return;
|
|
}
|
|
}
|
|
const capturingListeners = this[PropertySymbol.listeners].capturing.get(type);
|
|
if (capturingListeners) {
|
|
const index = capturingListeners.indexOf(listener);
|
|
if (index !== -1) {
|
|
capturingListeners.splice(index, 1);
|
|
this[PropertySymbol.listenerOptions].capturing.get(type).splice(index, 1);
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Dispatches an event.
|
|
*
|
|
* @see https://www.w3.org/TR/DOM-Level-3-Events/#event-flow
|
|
* @see https://www.quirksmode.org/js/events_order.html#link4
|
|
* @param event Event.
|
|
* @returns The return value is false if event is cancelable and at least one of the event handlers which handled this event called Event.preventDefault().
|
|
*/
|
|
dispatchEvent(event) {
|
|
// The "load" event is a special case. It should not bubble up to the window from the document.
|
|
if (!event[PropertySymbol.dispatching] &&
|
|
(event[PropertySymbol.type] !== 'load' || !event[PropertySymbol.target])) {
|
|
event[PropertySymbol.dispatching] = true;
|
|
event[PropertySymbol.target] = this[PropertySymbol.proxy] || this;
|
|
this.#goThroughDispatchEventPhases(event);
|
|
event[PropertySymbol.dispatching] = false;
|
|
return !(event[PropertySymbol.cancelable] && event[PropertySymbol.defaultPrevented]);
|
|
}
|
|
this.#callDispatchEventListeners(event);
|
|
return !(event[PropertySymbol.cancelable] && event[PropertySymbol.defaultPrevented]);
|
|
}
|
|
/**
|
|
* Adds an event listener.
|
|
*
|
|
* TODO:
|
|
* Was used by with IE8- and Opera. React believed Happy DOM was a legacy browser and used them, but that is no longer the case, so we should remove this method after that this is verified.
|
|
*
|
|
* @deprecated
|
|
* @param type Event type.
|
|
* @param listener Listener.
|
|
*/
|
|
attachEvent(type, listener) {
|
|
this.addEventListener(type.replace('on', ''), listener);
|
|
}
|
|
/**
|
|
* Removes an event listener.
|
|
*
|
|
* TODO:
|
|
* Was used by IE8- and Opera. React believed Happy DOM was a legacy browser and used them, but that is no longer the case, so we should remove this method after that this is verified.
|
|
*
|
|
* @deprecated
|
|
* @param type Event type.
|
|
* @param listener Listener.
|
|
*/
|
|
detachEvent(type, listener) {
|
|
this.removeEventListener(type.replace('on', ''), listener);
|
|
}
|
|
/**
|
|
* Goes through dispatch event phases.
|
|
*
|
|
* @param event Event.
|
|
*/
|
|
#goThroughDispatchEventPhases(event) {
|
|
const composedPath = event.composedPath();
|
|
// Capturing phase
|
|
event[PropertySymbol.eventPhase] = EventPhaseEnum_js_1.default.capturing;
|
|
for (let i = composedPath.length - 1; i >= 0; i--) {
|
|
event[PropertySymbol.currentTarget] = composedPath[i];
|
|
composedPath[i].dispatchEvent(event);
|
|
if (event[PropertySymbol.propagationStopped] ||
|
|
event[PropertySymbol.immediatePropagationStopped]) {
|
|
event[PropertySymbol.eventPhase] = EventPhaseEnum_js_1.default.none;
|
|
event[PropertySymbol.currentTarget] = null;
|
|
return;
|
|
}
|
|
}
|
|
// At target phase
|
|
event[PropertySymbol.eventPhase] = EventPhaseEnum_js_1.default.atTarget;
|
|
event[PropertySymbol.currentTarget] = this[PropertySymbol.proxy] || this;
|
|
event[PropertySymbol.target].dispatchEvent(event);
|
|
// Bubbling phase
|
|
event[PropertySymbol.eventPhase] = EventPhaseEnum_js_1.default.bubbling;
|
|
if (event[PropertySymbol.bubbles] &&
|
|
!event[PropertySymbol.propagationStopped] &&
|
|
!event[PropertySymbol.immediatePropagationStopped]) {
|
|
for (let i = 1, max = composedPath.length; i < max; i++) {
|
|
event[PropertySymbol.currentTarget] = composedPath[i];
|
|
composedPath[i].dispatchEvent(event);
|
|
if (event[PropertySymbol.propagationStopped] ||
|
|
event[PropertySymbol.immediatePropagationStopped]) {
|
|
event[PropertySymbol.eventPhase] = EventPhaseEnum_js_1.default.none;
|
|
event[PropertySymbol.currentTarget] = null;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
// None phase (done)
|
|
event[PropertySymbol.eventPhase] = EventPhaseEnum_js_1.default.none;
|
|
event[PropertySymbol.currentTarget] = null;
|
|
}
|
|
/**
|
|
* Handles dispatch event listeners.
|
|
*
|
|
* @param event Event.
|
|
*/
|
|
#callDispatchEventListeners(event) {
|
|
const window = this[PropertySymbol.window];
|
|
const browserSettings = window ? new WindowBrowserContext_js_1.default(window).getSettings() : null;
|
|
const eventPhase = event.eventPhase === EventPhaseEnum_js_1.default.capturing ? 'capturing' : 'bubbling';
|
|
// We need to clone the arrays because the listeners may remove themselves while we are iterating.
|
|
const listeners = this[PropertySymbol.listeners][eventPhase].get(event.type)?.slice();
|
|
if (listeners && listeners.length) {
|
|
const listenerOptions = this[PropertySymbol.listenerOptions][eventPhase]
|
|
.get(event.type)
|
|
?.slice();
|
|
for (let i = 0, max = listeners.length; i < max; i++) {
|
|
const listener = listeners[i];
|
|
const options = listenerOptions[i];
|
|
if (options?.passive) {
|
|
event[PropertySymbol.isInPassiveEventListener] = true;
|
|
}
|
|
// We can end up in a never ending loop if the listener for the error event on Window also throws an error.
|
|
if (window &&
|
|
(this !== window || event.type !== 'error') &&
|
|
!browserSettings?.disableErrorCapturing &&
|
|
browserSettings?.errorCapture === BrowserErrorCaptureEnum_js_1.default.tryAndCatch) {
|
|
if (listener.handleEvent) {
|
|
let result;
|
|
try {
|
|
result = listener.handleEvent.call(listener, event);
|
|
}
|
|
catch (error) {
|
|
window[PropertySymbol.dispatchError](error);
|
|
}
|
|
if (result instanceof Promise) {
|
|
result.catch((error) => window[PropertySymbol.dispatchError](error));
|
|
}
|
|
}
|
|
else {
|
|
let result;
|
|
try {
|
|
result = listener.call(this, event);
|
|
}
|
|
catch (error) {
|
|
window[PropertySymbol.dispatchError](error);
|
|
}
|
|
if (result instanceof Promise) {
|
|
result.catch((error) => window[PropertySymbol.dispatchError](error));
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (listener.handleEvent) {
|
|
listener.handleEvent.call(listener, event);
|
|
}
|
|
else {
|
|
listener.call(this, event);
|
|
}
|
|
}
|
|
event[PropertySymbol.isInPassiveEventListener] = false;
|
|
if (options?.once) {
|
|
// At this time, listeners and listenersOptions are cloned arrays. When the original value is deleted,
|
|
// The value corresponding to the cloned array is not deleted. So we need to delete the value in the cloned array.
|
|
listeners.splice(i, 1);
|
|
listenerOptions.splice(i, 1);
|
|
this.removeEventListener(event.type, listener);
|
|
i--;
|
|
max--;
|
|
}
|
|
if (event[PropertySymbol.immediatePropagationStopped]) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
if (event.eventPhase !== EventPhaseEnum_js_1.default.capturing) {
|
|
const onEventName = 'on' + event.type.toLowerCase();
|
|
const eventListener = this[onEventName];
|
|
if (typeof eventListener === 'function') {
|
|
// We can end up in a never ending loop if the listener for the error event on Window also throws an error.
|
|
if (window &&
|
|
(this !== window || event.type !== 'error') &&
|
|
!browserSettings?.disableErrorCapturing &&
|
|
browserSettings?.errorCapture === BrowserErrorCaptureEnum_js_1.default.tryAndCatch) {
|
|
let result;
|
|
try {
|
|
result = eventListener(event);
|
|
}
|
|
catch (error) {
|
|
window[PropertySymbol.dispatchError](error);
|
|
}
|
|
if (result instanceof Promise) {
|
|
result.catch((error) => window[PropertySymbol.dispatchError](error));
|
|
}
|
|
}
|
|
else {
|
|
eventListener(event);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
exports.default = EventTarget;
|
|
//# sourceMappingURL=EventTarget.cjs.map
|