feat: Reinitialize frontend with SvelteKit and TypeScript

- 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
This commit is contained in:
2026-02-17 16:19:59 -05:00
parent 54df6018f5
commit de2d83092e
28274 changed files with 3816354 additions and 90 deletions

View File

@@ -0,0 +1,34 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/**
* FileList.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/FileList
*/
class FileList extends Array {
/**
* Constructor.
*/
constructor() {
super(0);
}
/**
* Returns `Symbol.toStringTag`.
*
* @returns `Symbol.toStringTag`.
*/
get [Symbol.toStringTag]() {
return this.constructor.name;
}
/**
* Returns item by index.
*
* @param index Index.
* @returns Item.
*/
item(index) {
return this[index] || null;
}
}
exports.default = FileList;
//# sourceMappingURL=FileList.cjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"FileList.cjs","sourceRoot":"","sources":["../../../src/nodes/html-input-element/FileList.ts"],"names":[],"mappings":";;AAEA;;;;GAIG;AACH,MAAqB,QAAS,SAAQ,KAAW;IAChD;;OAEG;IACH;QACC,KAAK,CAAC,CAAC,CAAC,CAAC;IACV,CAAC;IAED;;;;OAIG;IACH,IAAW,CAAC,MAAM,CAAC,WAAW,CAAC;QAC9B,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;IAC9B,CAAC;IAED;;;;;OAKG;IACI,IAAI,CAAC,KAAa;QACxB,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC;IAC5B,CAAC;CACD;AA1BD,2BA0BC"}

View File

@@ -0,0 +1,26 @@
import File from '../../file/File.cjs';
/**
* FileList.
*
* @see https://developer.mozilla.org/en-US/docs/Web/API/FileList
*/
export default class FileList extends Array<File> {
/**
* Constructor.
*/
constructor();
/**
* Returns `Symbol.toStringTag`.
*
* @returns `Symbol.toStringTag`.
*/
get [Symbol.toStringTag](): string;
/**
* Returns item by index.
*
* @param index Index.
* @returns Item.
*/
item(index: number): File | null;
}
//# sourceMappingURL=FileList.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"FileList.d.ts","sourceRoot":"","sources":["../../../src/nodes/html-input-element/FileList.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,oBAAoB,CAAC;AAEtC;;;;GAIG;AACH,MAAM,CAAC,OAAO,OAAO,QAAS,SAAQ,KAAK,CAAC,IAAI,CAAC;IAChD;;OAEG;;IAKH;;;;OAIG;IACH,IAAW,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,MAAM,CAExC;IAED;;;;;OAKG;IACI,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;CAGvC"}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,636 @@
import HTMLElement from '../html-element/HTMLElement.cjs';
import * as PropertySymbol from '../../PropertySymbol.cjs';
import ValidityState from '../../validity-state/ValidityState.cjs';
import Event from '../../event/Event.cjs';
import HTMLInputElementSelectionModeEnum from './HTMLInputElementSelectionModeEnum.cjs';
import HTMLFormElement from '../html-form-element/HTMLFormElement.cjs';
import FileList from './FileList.cjs';
import HTMLLabelElement from '../html-label-element/HTMLLabelElement.cjs';
import HTMLDataListElement from '../html-data-list-element/HTMLDataListElement.cjs';
import NodeList from '../node/NodeList.cjs';
/**
* HTML Input Element.
*
* Reference:
* https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement.
*
* Used as reference for some of the logic (like selection range):
* https://github.com/jsdom/jsdom/blob/master/lib/jsdom/living/nodes/nodes/HTMLInputElement-impl.js (MIT licensed).
*/
export default class HTMLInputElement extends HTMLElement {
#private;
cloneNode: (deep?: boolean) => HTMLInputElement;
[PropertySymbol.value]: any;
[PropertySymbol.height]: number;
[PropertySymbol.width]: number;
[PropertySymbol.checked]: boolean | null;
[PropertySymbol.validationMessage]: string;
[PropertySymbol.validity]: ValidityState;
[PropertySymbol.files]: FileList;
[PropertySymbol.indeterminate]: boolean;
[PropertySymbol.formNode]: HTMLFormElement | null;
[PropertySymbol.popoverTargetElement]: HTMLElement | null;
get oninput(): ((event: Event) => void) | null;
set oninput(value: ((event: Event) => void) | null);
get oninvalid(): ((event: Event) => void) | null;
set oninvalid(value: ((event: Event) => void) | null);
get onselectionchange(): ((event: Event) => void) | null;
set onselectionchange(value: ((event: Event) => void) | null);
/**
* Returns default checked.
*
* @returns Default checked.
*/
get defaultChecked(): boolean;
/**
* Sets default checked.
*
* @param defaultChecked Default checked.
*/
set defaultChecked(defaultChecked: boolean);
/**
* Returns files.
*
* @returns Files.
*/
get files(): FileList;
/**
* Sets files.
*
* @param files Files.
*/
set files(files: FileList);
/**
* Returns form action.
*
* @returns Form action.
*/
get formAction(): string;
/**
* Sets form action.
*
* @param formAction Form action.
*/
set formAction(formAction: string);
/**
* Returns form enctype.
*
* @returns Form enctype.
*/
get formEnctype(): string;
/**
* Sets form enctype.
*
* @param formEnctype Form enctype.
*/
set formEnctype(formEnctype: string);
/**
* Returns form method.
*
* @returns Form method.
*/
get formMethod(): string;
/**
* Sets form method.
*
* @param formMethod Form method.
*/
set formMethod(formMethod: string);
/**
* Returns no validate.
*
* @returns No validate.
*/
get formNoValidate(): boolean;
/**
* Sets no validate.
*
* @param formNoValidate No validate.
*/
set formNoValidate(formNoValidate: boolean);
/**
* Returns form target.
*
* @returns Form target.
*/
get formTarget(): string;
/**
* Sets form target.
*
* @param formTarget Form target.
*/
set formTarget(formTarget: string);
/**
* Returns the parent form element.
*
* @returns Form.
*/
get form(): HTMLFormElement;
/**
* Returns validation message.
*
* @returns Validation message.
*/
get validationMessage(): string;
/**
* Returns validity.
*
* @returns Validity.
*/
get validity(): ValidityState;
/**
* Returns height.
*
* @returns Height.
*/
get height(): number;
/**
* Sets height.
*
* @param height Height.
*/
set height(height: number);
/**
* Returns width.
*
* @returns Width.
*/
get width(): number;
/**
* Sets width.
*
* @param width Width.
*/
set width(width: number);
/**
* Returns size.
*
* @returns Size.
*/
get size(): number;
/**
* Sets size.
*
* @param size Size.
*/
set size(size: number);
/**
* Returns minlength.
*
* @returns Min length.
*/
get minLength(): number;
/**
* Sets minlength.
*
* @param minLength Min length.
*/
set minLength(minlength: number);
/**
* Returns maxlength.
*
* @returns Max length.
*/
get maxLength(): number;
/**
* Sets maxlength.
*
* @param maxlength Max length.
*/
set maxLength(maxLength: number);
/**
* Returns type.
*
* @returns Type. Defaults to "text".
*/
get type(): string;
/**
* Sets type.
*
* @param type Type.
*/
set type(type: string);
/**
* Returns name.
*
* @returns Name.
*/
get name(): string;
/**
* Sets name.
*
* @param name Name.
*/
set name(name: string);
/**
* Returns alt.
*
* @returns Alt.
*/
get alt(): string;
/**
* Sets alt.
*
* @param alt Alt.
*/
set alt(alt: string);
/**
* Returns min.
*
* @returns Min.
*/
get min(): string;
/**
* Sets min.
*
* @param min Min.
*/
set min(min: string);
/**
* Returns max.
*
* @returns Max.
*/
get max(): string;
/**
* Sets max.
*
* @param max Max.
*/
set max(max: string);
/**
* Returns pattern.
*
* @returns Pattern.
*/
get pattern(): string;
/**
* Sets pattern.
*
* @param pattern Pattern.
*/
set pattern(pattern: string);
/**
* Returns placeholder.
*
* @returns Placeholder.
*/
get placeholder(): string;
/**
* Sets placeholder.
*
* @param placeholder Placeholder.
*/
set placeholder(placeholder: string);
/**
* Returns step.
*
* @returns Step.
*/
get step(): string;
/**
* Sets step.
*
* @param step Step.
*/
set step(step: string);
/**
* Returns inputmode.
*
* @returns Inputmode.
*/
get inputMode(): string;
/**
* Sets inputmode.
*
* @param inputmode Inputmode.
*/
set inputMode(inputmode: string);
/**
* Returns accept.
*
* @returns Accept.
*/
get accept(): string;
/**
* Sets accept.
*
* @param accept Accept.
*/
set accept(accept: string);
/**
* Returns allowdirs.
*
* @returns Allowdirs.
*/
get allowdirs(): string;
/**
* Sets allowdirs.
*
* @param allowdirs Allowdirs.
*/
set allowdirs(allowdirs: string);
/**
* Returns autocomplete.
*
* @returns Autocomplete.
*/
get autocomplete(): string;
/**
* Sets autocomplete.
*
* @param autocomplete Autocomplete.
*/
set autocomplete(autocomplete: string);
/**
* Returns src.
*
* @returns Src.
*/
get src(): string;
/**
* Sets src.
*
* @param src Src.
*/
set src(src: string);
/**
* Returns defaultValue.
*
* @returns Defaultvalue.
*/
get defaultValue(): string;
/**
* Sets defaultValue.
*
* @param defaultValue Defaultvalue.
*/
set defaultValue(defaultValue: string);
/**
* Returns read only.
*
* @returns Read only.
*/
get readOnly(): boolean;
/**
* Sets read only.
*
* @param readOnly Read only.
*/
set readOnly(readOnly: boolean);
/**
* Returns disabled.
*
* @returns Disabled.
*/
get disabled(): boolean;
/**
* Sets disabled.
*
* @param disabled Disabled.
*/
set disabled(disabled: boolean);
/**
* Returns autofocus.
*
* @returns Autofocus.
*/
get autofocus(): boolean;
/**
* Sets autofocus.
*
* @param autofocus Autofocus.
*/
set autofocus(autofocus: boolean);
/**
* Returns required.
*
* @returns Required.
*/
get required(): boolean;
/**
* Sets required.
*
* @param required Required.
*/
set required(required: boolean);
/**
* Returns indeterminate.
*
* @returns Indeterminate.
*/
get indeterminate(): boolean;
/**
* Sets indeterminate.
*
* @param indeterminate Indeterminate.
*/
set indeterminate(indeterminate: boolean);
/**
* Returns multiple.
*
* @returns Multiple.
*/
get multiple(): boolean;
/**
* Sets multiple.
*
* @param multiple Multiple.
*/
set multiple(multiple: boolean);
/**
* Returns checked.
*
* @returns Checked.
*/
get checked(): boolean;
/**
* Sets checked.
*
* @param checked Checked.
*/
set checked(checked: boolean);
/**
* Returns value.
*
* @returns Value.
*/
get value(): string;
/**
* Sets value.
*
* @param value Value.
*/
set value(value: string | null);
/**
* Returns selection start.
*
* @returns Selection start.
*/
get selectionStart(): number;
/**
* Sets selection start.
*
* @param start Start.
*/
set selectionStart(start: number);
/**
* Returns selection end.
*
* @returns Selection end.
*/
get selectionEnd(): number;
/**
* Sets selection end.
*
* @param end End.
*/
set selectionEnd(end: number);
/**
* Returns selection direction.
*
* @returns Selection direction.
*/
get selectionDirection(): string;
/**
* Sets selection direction.
*
* @param direction Direction.
*/
set selectionDirection(direction: string);
/**
* Returns "true" if it will validate.
*
* @returns "true" if it will validate.
*/
get willValidate(): boolean;
/**
* Returns value as Date.
*
* @returns Date.
*/
get valueAsDate(): Date;
/**
* Sets value from a Date.
*
* @param value Date.
*/
set valueAsDate(value: Date | null);
/**
* Returns value as number.
*
* @returns Number.
*/
get valueAsNumber(): number;
/**
* Sets value from a number.
*
* @param value number.
*/
set valueAsNumber(value: number);
/**
* Returns the associated label elements.
*
* @returns Label elements.
*/
get labels(): NodeList<HTMLLabelElement>;
/**
* Returns associated datalist element.
*
* @returns Data list element.
*/
get list(): HTMLDataListElement | null;
/**
* Returns popover target element.
*
* @returns Popover target element.
*/
get popoverTargetElement(): HTMLElement | null;
/**
* Sets popover target element.
*
* @param popoverTargetElement Popover target element.
*/
set popoverTargetElement(popoverTargetElement: HTMLElement | null);
/**
* Returns popover target action.
*
* @returns Popover target action.
*/
get popoverTargetAction(): string;
/**
* Sets popover target action.
*
* @param value Popover target action.
*/
set popoverTargetAction(value: string);
/**
* @override
*/
get tabIndex(): number;
/**
* @override
*/
set tabIndex(tabIndex: number);
/**
* Sets validation message.
*
* @param message Message.
*/
setCustomValidity(message: string): void;
/**
* Selects the text.
*/
select(): void;
/**
* Set selection range.
*
* @param start Start.
* @param end End.
* @param [direction="none"] Direction.
*/
setSelectionRange(start: number, end: number, direction?: string): void;
/**
* Set range text.
*
* @param replacement Replacement.
* @param [start] Start.
* @param [end] End.
* @param [direction] Direction.
* @param selectionMode
*/
setRangeText(replacement: string, start?: number, end?: number, selectionMode?: HTMLInputElementSelectionModeEnum): void;
/**
* Checks validity.
*
* @returns "true" if the field is valid.
*/
checkValidity(): boolean;
/**
* Reports validity.
*
* @returns "true" if the field is valid.
*/
reportValidity(): boolean;
/**
* Steps up.
*
* @param [increment] Increment.
*/
stepUp(increment?: number): void;
/**
* Steps down.
*
* @param [increment] Increment.
*/
stepDown(increment?: number): void;
/**
* @override
*/
[PropertySymbol.cloneNode](deep?: boolean): HTMLInputElement;
/**
* @override
*/
dispatchEvent(event: Event): boolean;
}
//# sourceMappingURL=HTMLInputElement.d.ts.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,51 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/**
* Date utility for HTML input elements.
*/
class HTMLInputElementDateUtility {
/**
* Returns iso week number from given date
*
* @see https://stackoverflow.com/a/6117889
* @param date Date or number.
* @returns Iso-week string.
*/
static dateIsoWeek(date) {
date = typeof date === 'number' ? new Date(date) : date;
// Copy date so don't modify original
date = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
// Set to nearest Thursday: current date + 4 - current day number
// Make Sunday's day number 7
date.setUTCDate(date.getUTCDate() + 4 - (date.getUTCDay() || 7));
// Get first day of year
const yearStart = new Date(Date.UTC(date.getUTCFullYear(), 0, 1));
// Calculate full weeks to nearest Thursday
const weekNo = Math.ceil(((date - yearStart) / 86400000 + 1) / 7);
return `${date.getUTCFullYear()}-W${weekNo < 10 ? '0' : ''}${weekNo}`;
}
/**
* Returns a date object for monday of given iso week string (\d\d\d\d-W\d\d)
*
* @param isoWeek Iso-week string.
* @returns Date.
*/
static isoWeekDate(isoWeek) {
// Algorythm adapted from https://en.wikipedia.org/wiki/ISO_week_date
const [, Y, W] = isoWeek.match(/^(\d{4})-W(\d{2})$/) || [];
if (!Y || !W || Number(W) > 53 || Number(W) < 1) {
return new Date('x'); // Return an invalid date
}
const date = new Date(`${Y}-01-01T00:00Z`);
const jan4th = new Date(`${Y}-01-04T00:00Z`);
const jan4thDay = (jan4th.getUTCDay() + 6) % 7;
const ordinalDate = 1 + (Number(W) - 1) * 7 - jan4thDay + 3;
date.setUTCDate(ordinalDate);
if (date.getUTCFullYear() > Number(Y)) {
return new Date('x'); // Return an invalid date
}
return date;
}
}
exports.default = HTMLInputElementDateUtility;
//# sourceMappingURL=HTMLInputElementDateUtility.cjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"HTMLInputElementDateUtility.cjs","sourceRoot":"","sources":["../../../src/nodes/html-input-element/HTMLInputElementDateUtility.ts"],"names":[],"mappings":";;AAAA;;GAEG;AACH,MAAqB,2BAA2B;IAC/C;;;;;;OAMG;IACI,MAAM,CAAC,WAAW,CAAC,IAAmB;QAC5C,IAAI,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACxD,qCAAqC;QACrC,IAAI,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,IAAI,CAAC,QAAQ,EAAE,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAC/E,iEAAiE;QACjE,6BAA6B;QAC7B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;QACjE,wBAAwB;QACxB,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,cAAc,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QAClE,2CAA2C;QAC3C,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CACvB,CAAC,CAAmB,IAAK,GAAqB,SAAU,CAAC,GAAG,QAAQ,GAAG,CAAC,CAAC,GAAG,CAAC,CAC7E,CAAC;QACF,OAAO,GAAG,IAAI,CAAC,cAAc,EAAE,KAAK,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,MAAM,EAAE,CAAC;IACvE,CAAC;IAED;;;;;OAKG;IACI,MAAM,CAAC,WAAW,CAAC,OAAe;QACxC,qEAAqE;QACrE,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,oBAAoB,CAAC,IAAI,EAAE,CAAC;QAC3D,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;YACjD,OAAO,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,yBAAyB;QAChD,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;QAC3C,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;QAC7C,MAAM,SAAS,GAAG,CAAC,MAAM,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;QAC/C,MAAM,WAAW,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,SAAS,GAAG,CAAC,CAAC;QAC5D,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;QAC7B,IAAI,IAAI,CAAC,cAAc,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;YACvC,OAAO,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,yBAAyB;QAChD,CAAC;QACD,OAAO,IAAI,CAAC;IACb,CAAC;CACD;AA9CD,8CA8CC"}

View File

@@ -0,0 +1,21 @@
/**
* Date utility for HTML input elements.
*/
export default class HTMLInputElementDateUtility {
/**
* Returns iso week number from given date
*
* @see https://stackoverflow.com/a/6117889
* @param date Date or number.
* @returns Iso-week string.
*/
static dateIsoWeek(date: Date | number): string;
/**
* Returns a date object for monday of given iso week string (\d\d\d\d-W\d\d)
*
* @param isoWeek Iso-week string.
* @returns Date.
*/
static isoWeekDate(isoWeek: string): Date;
}
//# sourceMappingURL=HTMLInputElementDateUtility.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"HTMLInputElementDateUtility.d.ts","sourceRoot":"","sources":["../../../src/nodes/html-input-element/HTMLInputElementDateUtility.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,2BAA2B;IAC/C;;;;;;OAMG;WACW,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM;IAgBtD;;;;;OAKG;WACW,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;CAgBhD"}

View File

@@ -0,0 +1,10 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var HTMLInputElementSelectionDirectionEnum;
(function (HTMLInputElementSelectionDirectionEnum) {
HTMLInputElementSelectionDirectionEnum["none"] = "none";
HTMLInputElementSelectionDirectionEnum["forward"] = "forward";
HTMLInputElementSelectionDirectionEnum["backward"] = "backward";
})(HTMLInputElementSelectionDirectionEnum || (HTMLInputElementSelectionDirectionEnum = {}));
exports.default = HTMLInputElementSelectionDirectionEnum;
//# sourceMappingURL=HTMLInputElementSelectionDirectionEnum.cjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"HTMLInputElementSelectionDirectionEnum.cjs","sourceRoot":"","sources":["../../../src/nodes/html-input-element/HTMLInputElementSelectionDirectionEnum.ts"],"names":[],"mappings":";;AAAA,IAAK,sCAIJ;AAJD,WAAK,sCAAsC;IAC1C,uDAAa,CAAA;IACb,6DAAmB,CAAA;IACnB,+DAAqB,CAAA;AACtB,CAAC,EAJI,sCAAsC,KAAtC,sCAAsC,QAI1C;AACD,kBAAe,sCAAsC,CAAC"}

View File

@@ -0,0 +1,7 @@
declare enum HTMLInputElementSelectionDirectionEnum {
none = "none",
forward = "forward",
backward = "backward"
}
export default HTMLInputElementSelectionDirectionEnum;
//# sourceMappingURL=HTMLInputElementSelectionDirectionEnum.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"HTMLInputElementSelectionDirectionEnum.d.ts","sourceRoot":"","sources":["../../../src/nodes/html-input-element/HTMLInputElementSelectionDirectionEnum.ts"],"names":[],"mappings":"AAAA,aAAK,sCAAsC;IAC1C,IAAI,SAAS;IACb,OAAO,YAAY;IACnB,QAAQ,aAAa;CACrB;AACD,eAAe,sCAAsC,CAAC"}

View File

@@ -0,0 +1,11 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var HTMLInputElementSelectionModeEnum;
(function (HTMLInputElementSelectionModeEnum) {
HTMLInputElementSelectionModeEnum["preserve"] = "preserve";
HTMLInputElementSelectionModeEnum["select"] = "select";
HTMLInputElementSelectionModeEnum["start"] = "start";
HTMLInputElementSelectionModeEnum["end"] = "end";
})(HTMLInputElementSelectionModeEnum || (HTMLInputElementSelectionModeEnum = {}));
exports.default = HTMLInputElementSelectionModeEnum;
//# sourceMappingURL=HTMLInputElementSelectionModeEnum.cjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"HTMLInputElementSelectionModeEnum.cjs","sourceRoot":"","sources":["../../../src/nodes/html-input-element/HTMLInputElementSelectionModeEnum.ts"],"names":[],"mappings":";;AAAA,IAAK,iCAKJ;AALD,WAAK,iCAAiC;IACrC,0DAAqB,CAAA;IACrB,sDAAiB,CAAA;IACjB,oDAAe,CAAA;IACf,gDAAW,CAAA;AACZ,CAAC,EALI,iCAAiC,KAAjC,iCAAiC,QAKrC;AACD,kBAAe,iCAAiC,CAAC"}

View File

@@ -0,0 +1,8 @@
declare enum HTMLInputElementSelectionModeEnum {
preserve = "preserve",
select = "select",
start = "start",
end = "end"
}
export default HTMLInputElementSelectionModeEnum;
//# sourceMappingURL=HTMLInputElementSelectionModeEnum.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"HTMLInputElementSelectionModeEnum.d.ts","sourceRoot":"","sources":["../../../src/nodes/html-input-element/HTMLInputElementSelectionModeEnum.ts"],"names":[],"mappings":"AAAA,aAAK,iCAAiC;IACrC,QAAQ,aAAa;IACrB,MAAM,WAAW;IACjB,KAAK,UAAU;IACf,GAAG,QAAQ;CACX;AACD,eAAe,iCAAiC,CAAC"}

View File

@@ -0,0 +1,209 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const NEW_LINES_REGEXP = /[\n\r]/gm;
const parseInts = (a) => a.map((v) => parseInt(v, 10));
/**
* HTML input element value sanitizer.
*/
class HTMLInputElementValueSanitizer {
/**
* Sanitizes a value.
*
* @param input Input.
* @param value Value.
*/
static sanitize(input, value) {
switch (input.type) {
case 'password':
case 'search':
case 'tel':
case 'text':
return value.replace(NEW_LINES_REGEXP, '');
case 'color':
// https://html.spec.whatwg.org/multipage/forms.html#color-state-(type=color):value-sanitization-algorithm
return /^#[a-fA-F\d]{6}$/.test(value) ? value.toLowerCase() : '#000000';
case 'email':
// https://html.spec.whatwg.org/multipage/forms.html#e-mail-state-(type=email):value-sanitization-algorithm
// https://html.spec.whatwg.org/multipage/forms.html#e-mail-state-(type=email):value-sanitization-algorithm-2
if (input.multiple) {
return value
.split(',')
.map((token) => token.trim())
.join(',');
}
return value.trim().replace(NEW_LINES_REGEXP, '');
case 'number':
// https://html.spec.whatwg.org/multipage/input.html#number-state-(type=number):value-sanitization-algorithm
return !isNaN(Number.parseFloat(value)) ? value : '';
case 'range': {
// https://html.spec.whatwg.org/multipage/input.html#range-state-(type=range):value-sanitization-algorithm
const number = Number.parseFloat(value);
const min = parseFloat(input.min) || 0;
const max = parseFloat(input.max) || 100;
if (isNaN(number)) {
return max < min ? String(min) : String((min + max) / 2);
}
else if (number < min) {
return String(min);
}
else if (number > max) {
return String(max);
}
return value;
}
case 'url':
// https://html.spec.whatwg.org/multipage/forms.html#url-state-(type=url):value-sanitization-algorithm
return value.trim().replace(NEW_LINES_REGEXP, '');
case 'date':
// https://html.spec.whatwg.org/multipage/input.html#date-state-(type=date):value-sanitization-algorithm
return this.sanitizeDate(value);
case 'datetime-local': {
// https://html.spec.whatwg.org/multipage/input.html#local-date-and-time-state-(type=datetime-local):value-sanitization-algorithm
const match = value.match(/^(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d):(\d\d)(?::(\d\d)(?:\.(\d{1,3}))?)?$/);
if (!match) {
return '';
}
const dateString = this.sanitizeDate(value.slice(0, 10));
let timeString = this.sanitizeTime(value.slice(11));
if (!(dateString && timeString)) {
return '';
}
// Has seconds so needs to remove trailing zeros
if (match[6] !== undefined) {
if (timeString.indexOf('.') !== -1) {
// Remove unecessary zeros milliseconds
timeString = timeString.replace(/(?:\.0*|(\.\d+?)0+)$/, '$1');
}
timeString = timeString.replace(/(\d\d:\d\d)(:00)$/, '$1');
}
return dateString + 'T' + timeString;
}
case 'month':
// https://html.spec.whatwg.org/multipage/input.html#month-state-(type=month):value-sanitization-algorithm
if (!(value.match(/^(\d\d\d\d)-(\d\d)$/) && this.parseMonthComponent(value))) {
return '';
}
return this.checkBoundaries(value, input.min, input.max) ? value : '';
case 'time': {
// https://html.spec.whatwg.org/multipage/input.html#time-state-(type=time):value-sanitization-algorithm
value = this.sanitizeTime(value);
return value && this.checkBoundaries(value, input.min, input.max) ? value : '';
}
case 'week': {
// https://html.spec.whatwg.org/multipage/input.html#week-state-(type=week):value-sanitization-algorithm
const match = value.match(/^(\d\d\d\d)-W(\d\d)$/);
if (!match) {
return '';
}
const [intY, intW] = parseInts(match.slice(1, 3));
if (intY <= 0 || intW < 1 || intW > 53) {
return '';
}
// Check date is valid
const lastWeek = this.lastIsoWeekOfYear(intY);
if (intW < 1 || intW > 52 + lastWeek) {
return '';
}
if (!this.checkBoundaries(value, input.min, input.max)) {
return '';
}
return value;
}
}
return value;
}
/**
* Checks if a value is within the boundaries of min and max.
*
* @param value
* @param min
* @param max
*/
static checkBoundaries(value, min, max) {
if (min && min > value) {
return false;
}
else if (max && max < value) {
return false;
}
return true;
}
/**
* Parses the month component of a date string.
*
* @param value
*/
static parseMonthComponent(value) {
const [Y, M] = value.split('-');
const [intY, intM] = parseInts([Y, M]);
if (isNaN(intY) || isNaN(intM) || intY <= 0 || intM < 1 || intM > 12) {
return '';
}
return value;
}
/**
* Returns the last ISO week of a year.
*
* @param year
*/
static lastIsoWeekOfYear = (year) => {
const date = new Date(+year, 11, 31);
const day = (date.getDay() + 6) % 7;
date.setDate(date.getDate() - day + 3);
const firstThursday = date.getTime();
date.setMonth(0, 1);
if (date.getDay() !== 4) {
date.setMonth(0, 1 + ((4 - date.getDay() + 7) % 7));
}
return 1 + Math.ceil((firstThursday - date.getTime()) / 604800000);
};
/**
* Sanitizes a date string.
*
* @param value
*/
static sanitizeDate(value) {
const match = value.match(/^(\d{4})-(\d{2})-(\d{2})$/);
if (!match) {
return '';
}
const month = this.parseMonthComponent(value.slice(0, 7));
if (!month) {
return '';
}
const [intY, intM, intD] = parseInts(match.slice(1, 4));
if (intD < 1 || intD > 31) {
return '';
}
// Check date is valid
const lastDayOfMonth = new Date(intY, intM, 0).getDate();
if (intD > lastDayOfMonth) {
return '';
}
return value;
}
/**
* Sanitizes a time string.
*
* @param value
*/
static sanitizeTime(value) {
const match = value.match(/^(\d{2}):(\d{2})(?::(\d{2}(?:\.(\d{1,3}))?))?$/);
if (!match) {
return '';
}
const [intH, intM] = parseInts(match.slice(1, 3));
const ms = parseFloat(match[3] || '0') * 1000;
if (intH > 23 || intM > 59 || ms > 59999) {
return '';
}
if (ms === 0) {
return `${match[1]}:${match[2]}`;
}
else {
return `${match[1]}:${match[2]}${ms >= 10000 ? `:${ms / 1000}` : `:0${ms / 1000}`}`;
}
}
}
exports.default = HTMLInputElementValueSanitizer;
//# sourceMappingURL=HTMLInputElementValueSanitizer.cjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,46 @@
import HTMLInputElement from './HTMLInputElement.cjs';
/**
* HTML input element value sanitizer.
*/
export default class HTMLInputElementValueSanitizer {
/**
* Sanitizes a value.
*
* @param input Input.
* @param value Value.
*/
static sanitize(input: HTMLInputElement, value: string): string;
/**
* Checks if a value is within the boundaries of min and max.
*
* @param value
* @param min
* @param max
*/
private static checkBoundaries;
/**
* Parses the month component of a date string.
*
* @param value
*/
private static parseMonthComponent;
/**
* Returns the last ISO week of a year.
*
* @param year
*/
private static lastIsoWeekOfYear;
/**
* Sanitizes a date string.
*
* @param value
*/
private static sanitizeDate;
/**
* Sanitizes a time string.
*
* @param value
*/
private static sanitizeTime;
}
//# sourceMappingURL=HTMLInputElementValueSanitizer.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"HTMLInputElementValueSanitizer.d.ts","sourceRoot":"","sources":["../../../src/nodes/html-input-element/HTMLInputElementValueSanitizer.ts"],"names":[],"mappings":"AAAA,OAAO,gBAAgB,MAAM,uBAAuB,CAAC;AAKrD;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,8BAA8B;IAClD;;;;;OAKG;WACW,QAAQ,CAAC,KAAK,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM;IAuGtE;;;;;;OAMG;IACH,OAAO,CAAC,MAAM,CAAC,eAAe;IAQ9B;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,mBAAmB;IAQlC;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAU9B;IAEF;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,YAAY;IAqB3B;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,YAAY;CAgB3B"}

View File

@@ -0,0 +1,38 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const DOMException_js_1 = __importDefault(require("../../exception/DOMException.cjs"));
/**
* HTML input element value stepping.
*/
class HTMLInputElementValueStepping {
/**
* Steps up or down.
*
* @param type Type.
* @param value Value.
* @param direction Direction.
* @param [increment] Increment.
* @returns New value.
*/
static step(type, value, direction, increment) {
switch (type) {
case 'number':
return String(Number(value) + (increment !== undefined ? increment * direction : direction));
case 'date':
case 'month':
case 'week':
case 'time':
case 'datetime-local':
case 'range':
// TODO: Implement support for additional types
return null;
default:
throw new DOMException_js_1.default('This form element is not steppable.');
}
}
}
exports.default = HTMLInputElementValueStepping;
//# sourceMappingURL=HTMLInputElementValueStepping.cjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"HTMLInputElementValueStepping.cjs","sourceRoot":"","sources":["../../../src/nodes/html-input-element/HTMLInputElementValueStepping.ts"],"names":[],"mappings":";;;;;AAAA,sFAA2D;AAE3D;;GAEG;AACH,MAAqB,6BAA6B;IACjD;;;;;;;;OAQG;IACI,MAAM,CAAC,IAAI,CAAC,IAAY,EAAE,KAAa,EAAE,SAAiB,EAAE,SAAkB;QACpF,QAAQ,IAAI,EAAE,CAAC;YACd,KAAK,QAAQ;gBACZ,OAAO,MAAM,CACZ,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,GAAG,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAC7E,CAAC;YACH,KAAK,MAAM,CAAC;YACZ,KAAK,OAAO,CAAC;YACb,KAAK,MAAM,CAAC;YACZ,KAAK,MAAM,CAAC;YACZ,KAAK,gBAAgB,CAAC;YACtB,KAAK,OAAO;gBACX,+CAA+C;gBAC/C,OAAO,IAAI,CAAC;YACb;gBACC,MAAM,IAAI,yBAAY,CAAC,qCAAqC,CAAC,CAAC;QAChE,CAAC;IACF,CAAC;CACD;AA5BD,gDA4BC"}

View File

@@ -0,0 +1,16 @@
/**
* HTML input element value stepping.
*/
export default class HTMLInputElementValueStepping {
/**
* Steps up or down.
*
* @param type Type.
* @param value Value.
* @param direction Direction.
* @param [increment] Increment.
* @returns New value.
*/
static step(type: string, value: string, direction: -1 | 1, increment?: number): string;
}
//# sourceMappingURL=HTMLInputElementValueStepping.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"HTMLInputElementValueStepping.d.ts","sourceRoot":"","sources":["../../../src/nodes/html-input-element/HTMLInputElementValueStepping.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,6BAA6B;IACjD;;;;;;;;OAQG;WACW,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM;CAkB9F"}