- 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
458 lines
18 KiB
JavaScript
458 lines
18 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 DOMException_js_1 = __importDefault(require("../exception/DOMException.cjs"));
|
|
const PropertySymbol = __importStar(require("../PropertySymbol.cjs"));
|
|
const SelectorCombinatorEnum_js_1 = __importDefault(require("./SelectorCombinatorEnum.cjs"));
|
|
const SPACE_REGEXP = /\s+/;
|
|
/**
|
|
* Selector item.
|
|
*/
|
|
class SelectorItem {
|
|
tagName;
|
|
id;
|
|
classNames;
|
|
attributes;
|
|
pseudos;
|
|
isPseudoElement;
|
|
combinator;
|
|
ignoreErrors;
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* @param [options] Options.
|
|
* @param [options.combinator] Combinator.
|
|
* @param [options.tagName] Tag name.
|
|
* @param [options.id] ID.
|
|
* @param [options.classNames] Class names.
|
|
* @param [options.attributes] Attributes.
|
|
* @param [options.pseudos] Pseudos.
|
|
* @param [options.isPseudoElement] Is pseudo element.
|
|
* @param [options.ignoreErrors] Ignore errors.
|
|
*/
|
|
constructor(options) {
|
|
this.tagName = options?.tagName || null;
|
|
this.id = options?.id || null;
|
|
this.classNames = options?.classNames || null;
|
|
this.attributes = options?.attributes || null;
|
|
this.pseudos = options?.pseudos || null;
|
|
this.isPseudoElement = options?.isPseudoElement || false;
|
|
this.combinator = options?.combinator || SelectorCombinatorEnum_js_1.default.descendant;
|
|
this.ignoreErrors = options?.ignoreErrors || false;
|
|
}
|
|
/**
|
|
* Matches a selector against an element.
|
|
*
|
|
* @param element HTML element.
|
|
* @returns Result.
|
|
*/
|
|
match(element) {
|
|
let priorityWeight = 0;
|
|
if (this.isPseudoElement) {
|
|
return null;
|
|
}
|
|
// Tag name match
|
|
if (this.tagName) {
|
|
if (this.tagName !== '*' && this.tagName !== element[PropertySymbol.tagName].toUpperCase()) {
|
|
return null;
|
|
}
|
|
priorityWeight += 1;
|
|
}
|
|
// ID Match
|
|
if (this.id) {
|
|
if (this.id !== element.id) {
|
|
return null;
|
|
}
|
|
priorityWeight += 100;
|
|
}
|
|
// Class match
|
|
if (this.classNames) {
|
|
const result = this.matchClass(element);
|
|
if (!result) {
|
|
return null;
|
|
}
|
|
priorityWeight += result.priorityWeight;
|
|
}
|
|
// Attribute match
|
|
if (this.attributes) {
|
|
const result = this.matchAttributes(element);
|
|
if (!result) {
|
|
return null;
|
|
}
|
|
priorityWeight += result.priorityWeight;
|
|
}
|
|
// Pseudo match
|
|
if (this.pseudos) {
|
|
const result = this.matchPseudo(element);
|
|
if (!result) {
|
|
return null;
|
|
}
|
|
priorityWeight += result.priorityWeight;
|
|
}
|
|
return { priorityWeight };
|
|
}
|
|
/**
|
|
* Matches a pseudo selector.
|
|
*
|
|
* @param element Element.
|
|
* @returns Result.
|
|
*/
|
|
matchPseudo(element) {
|
|
const parent = element[PropertySymbol.parentNode];
|
|
const parentChildren = element[PropertySymbol.parentNode]
|
|
? element[PropertySymbol.parentNode][PropertySymbol.elementArray]
|
|
: [];
|
|
if (!this.pseudos) {
|
|
return { priorityWeight: 0 };
|
|
}
|
|
let priorityWeight = 0;
|
|
for (const pseudo of this.pseudos) {
|
|
// Validation
|
|
switch (pseudo.name) {
|
|
case 'not':
|
|
case 'nth-child':
|
|
case 'nth-of-type':
|
|
case 'nth-last-child':
|
|
case 'nth-last-of-type':
|
|
case 'is':
|
|
case 'where':
|
|
if (!pseudo.arguments) {
|
|
if (this.ignoreErrors) {
|
|
return null;
|
|
}
|
|
throw new DOMException_js_1.default(`Failed to execute 'matches' on '${element.constructor.name}': '${this.getSelectorString()}' is not a valid selector.`);
|
|
}
|
|
break;
|
|
}
|
|
// Check if parent exists
|
|
if (!parent) {
|
|
switch (pseudo.name) {
|
|
case 'first-child':
|
|
case 'last-child':
|
|
case 'only-child':
|
|
case 'first-of-type':
|
|
case 'last-of-type':
|
|
case 'only-of-type':
|
|
case 'nth-child':
|
|
case 'nth-of-type':
|
|
case 'nth-last-child':
|
|
case 'nth-last-of-type':
|
|
return null;
|
|
}
|
|
}
|
|
const selectorMatch = this.matchPseudoItem(element, parentChildren, pseudo);
|
|
if (!selectorMatch) {
|
|
return null;
|
|
}
|
|
priorityWeight += selectorMatch.priorityWeight;
|
|
}
|
|
return { priorityWeight };
|
|
}
|
|
/**
|
|
* Matches a pseudo selector.
|
|
*
|
|
* @param element Element.
|
|
* @param parentChildren Parent children.
|
|
* @param pseudo Pseudo.
|
|
*/
|
|
matchPseudoItem(element, parentChildren, pseudo) {
|
|
switch (pseudo.name) {
|
|
case 'first-child':
|
|
return parentChildren[0] === element ? { priorityWeight: 10 } : null;
|
|
case 'last-child':
|
|
return parentChildren.length && parentChildren[parentChildren.length - 1] === element
|
|
? { priorityWeight: 10 }
|
|
: null;
|
|
case 'only-child':
|
|
return parentChildren.length === 1 && parentChildren[0] === element
|
|
? { priorityWeight: 10 }
|
|
: null;
|
|
case 'first-of-type':
|
|
for (const child of parentChildren) {
|
|
if (child[PropertySymbol.tagName] === element[PropertySymbol.tagName]) {
|
|
return child === element ? { priorityWeight: 10 } : null;
|
|
}
|
|
}
|
|
return null;
|
|
case 'last-of-type':
|
|
for (let i = parentChildren.length - 1; i >= 0; i--) {
|
|
const child = parentChildren[i];
|
|
if (child[PropertySymbol.tagName] === element[PropertySymbol.tagName]) {
|
|
return child === element ? { priorityWeight: 10 } : null;
|
|
}
|
|
}
|
|
return null;
|
|
case 'only-of-type':
|
|
let isFound = false;
|
|
for (const child of parentChildren) {
|
|
if (child[PropertySymbol.tagName] === element[PropertySymbol.tagName]) {
|
|
if (isFound || child !== element) {
|
|
return null;
|
|
}
|
|
isFound = true;
|
|
}
|
|
}
|
|
return isFound ? { priorityWeight: 10 } : null;
|
|
case 'checked':
|
|
return element[PropertySymbol.tagName] === 'INPUT' && element.checked
|
|
? { priorityWeight: 10 }
|
|
: null;
|
|
case 'disabled':
|
|
return 'disabled' in element && element.hasAttribute('disabled')
|
|
? { priorityWeight: 10 }
|
|
: null;
|
|
case 'empty':
|
|
return !element[PropertySymbol.elementArray].length
|
|
? { priorityWeight: 10 }
|
|
: null;
|
|
case 'root':
|
|
return element[PropertySymbol.tagName] === 'HTML' ? { priorityWeight: 10 } : null;
|
|
case 'not':
|
|
for (const selectorItem of pseudo.selectorItems) {
|
|
if (selectorItem.match(element)) {
|
|
return null;
|
|
}
|
|
}
|
|
return { priorityWeight: 10 };
|
|
case 'nth-child':
|
|
let nthChildIndex = -1;
|
|
if (pseudo.selectorItems[0] && !pseudo.selectorItems[0].match(element)) {
|
|
return null;
|
|
}
|
|
for (let i = 0, max = parentChildren.length; i < max; i++) {
|
|
if (!pseudo.selectorItems[0] || pseudo.selectorItems[0].match(parentChildren[i])) {
|
|
nthChildIndex++;
|
|
}
|
|
if (parentChildren[i] === element) {
|
|
return nthChildIndex !== -1 && pseudo.nthFunction(nthChildIndex + 1)
|
|
? { priorityWeight: 10 }
|
|
: null;
|
|
}
|
|
}
|
|
return null;
|
|
case 'nth-of-type':
|
|
let nthOfTypeIndex = -1;
|
|
for (let i = 0, max = parentChildren.length; i < max; i++) {
|
|
if (parentChildren[i][PropertySymbol.tagName] === element[PropertySymbol.tagName]) {
|
|
nthOfTypeIndex++;
|
|
}
|
|
if (parentChildren[i] === element) {
|
|
return nthOfTypeIndex !== -1 && pseudo.nthFunction(nthOfTypeIndex + 1)
|
|
? { priorityWeight: 10 }
|
|
: null;
|
|
}
|
|
}
|
|
return null;
|
|
case 'nth-last-child':
|
|
let nthLastChildIndex = -1;
|
|
if (pseudo.selectorItems[0] && !pseudo.selectorItems[0].match(element)) {
|
|
return null;
|
|
}
|
|
for (let i = parentChildren.length - 1; i >= 0; i--) {
|
|
if (!pseudo.selectorItems[0] || pseudo.selectorItems[0].match(parentChildren[i])) {
|
|
nthLastChildIndex++;
|
|
}
|
|
if (parentChildren[i] === element) {
|
|
return nthLastChildIndex !== -1 && pseudo.nthFunction(nthLastChildIndex + 1)
|
|
? { priorityWeight: 10 }
|
|
: null;
|
|
}
|
|
}
|
|
return null;
|
|
case 'nth-last-of-type':
|
|
let nthLastOfTypeIndex = -1;
|
|
for (let i = parentChildren.length - 1; i >= 0; i--) {
|
|
if (parentChildren[i][PropertySymbol.tagName] === element[PropertySymbol.tagName]) {
|
|
nthLastOfTypeIndex++;
|
|
}
|
|
if (parentChildren[i] === element) {
|
|
return nthLastOfTypeIndex !== -1 && pseudo.nthFunction(nthLastOfTypeIndex + 1)
|
|
? { priorityWeight: 10 }
|
|
: null;
|
|
}
|
|
}
|
|
return null;
|
|
case 'target':
|
|
const hash = element[PropertySymbol.ownerDocument].location.hash;
|
|
if (!hash) {
|
|
return null;
|
|
}
|
|
return element.isConnected && element.id === hash.slice(1) ? { priorityWeight: 10 } : null;
|
|
case 'is':
|
|
let priorityWeightForIs = 0;
|
|
for (const selectorItem of pseudo.selectorItems) {
|
|
const match = selectorItem.match(element);
|
|
if (match && priorityWeightForIs < match.priorityWeight) {
|
|
priorityWeightForIs = match.priorityWeight;
|
|
}
|
|
}
|
|
return priorityWeightForIs ? { priorityWeight: priorityWeightForIs } : null;
|
|
case 'where':
|
|
for (const selectorItem of pseudo.selectorItems) {
|
|
if (selectorItem.match(element)) {
|
|
return { priorityWeight: 0 };
|
|
}
|
|
}
|
|
return null;
|
|
case 'has':
|
|
let priorityWeightForHas = 0;
|
|
if (pseudo.arguments[0] === '+') {
|
|
const nextSibling = element.nextElementSibling;
|
|
if (!nextSibling) {
|
|
return null;
|
|
}
|
|
for (const selectorItem of pseudo.selectorItems) {
|
|
const match = selectorItem.match(nextSibling);
|
|
if (match && priorityWeightForHas < match.priorityWeight) {
|
|
priorityWeightForHas = match.priorityWeight;
|
|
}
|
|
}
|
|
}
|
|
else if (pseudo.arguments[0] === '>') {
|
|
for (const selectorItem of pseudo.selectorItems) {
|
|
for (const child of element[PropertySymbol.elementArray]) {
|
|
const match = selectorItem.match(child);
|
|
if (match && priorityWeightForHas < match.priorityWeight) {
|
|
priorityWeightForHas = match.priorityWeight;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
for (const selectorItem of pseudo.selectorItems) {
|
|
const match = this.matchChildOfElement(selectorItem, element);
|
|
if (match && priorityWeightForHas < match.priorityWeight) {
|
|
priorityWeightForHas = match.priorityWeight;
|
|
}
|
|
}
|
|
}
|
|
return priorityWeightForHas ? { priorityWeight: priorityWeightForHas } : null;
|
|
case 'focus':
|
|
case 'focus-visible':
|
|
return element[PropertySymbol.ownerDocument].activeElement === element
|
|
? { priorityWeight: 10 }
|
|
: null;
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
/**
|
|
* Matches attribute.
|
|
*
|
|
* @param element Element.
|
|
* @returns Result.
|
|
*/
|
|
matchAttributes(element) {
|
|
if (!this.attributes) {
|
|
return null;
|
|
}
|
|
let priorityWeight = 0;
|
|
for (const attribute of this.attributes) {
|
|
const elementAttribute = element[PropertySymbol.attributes].getNamedItem(attribute.name);
|
|
if (!elementAttribute) {
|
|
return null;
|
|
}
|
|
priorityWeight += 10;
|
|
if (attribute.value !== null &&
|
|
(elementAttribute[PropertySymbol.value] === null ||
|
|
(attribute.regExp && !attribute.regExp.test(elementAttribute[PropertySymbol.value])) ||
|
|
(!attribute.regExp && attribute.value !== elementAttribute[PropertySymbol.value]))) {
|
|
return null;
|
|
}
|
|
}
|
|
return { priorityWeight };
|
|
}
|
|
/**
|
|
* Matches class.
|
|
*
|
|
* @param element Element.
|
|
* @returns Result.
|
|
*/
|
|
matchClass(element) {
|
|
if (!this.classNames) {
|
|
return null;
|
|
}
|
|
const classList = element.className.split(SPACE_REGEXP);
|
|
let priorityWeight = 0;
|
|
for (const className of this.classNames) {
|
|
if (!classList.includes(className)) {
|
|
return null;
|
|
}
|
|
priorityWeight += 10;
|
|
}
|
|
return { priorityWeight };
|
|
}
|
|
/**
|
|
* Matches a selector item against children of an element.
|
|
*
|
|
* @param selectorItem Selector item.
|
|
* @param element Element.
|
|
* @returns Result.
|
|
*/
|
|
matchChildOfElement(selectorItem, element) {
|
|
for (const child of element[PropertySymbol.elementArray]) {
|
|
const match = selectorItem.match(child);
|
|
if (match) {
|
|
return match;
|
|
}
|
|
const childMatch = this.matchChildOfElement(selectorItem, child);
|
|
if (childMatch) {
|
|
return childMatch;
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* Returns the selector string.
|
|
*
|
|
* @returns Selector string.
|
|
*/
|
|
getSelectorString() {
|
|
return `${this.tagName ? this.tagName.toLowerCase() : ''}${this.id ? `#${this.id}` : ''}${this.classNames ? `.${this.classNames.join('.')}` : ''}${this.attributes
|
|
? this.attributes
|
|
.map((attribute) => `[${attribute.name}${attribute.value ? `${attribute.operator || ''}="${attribute.value}"` : ''}]`)
|
|
.join('')
|
|
: ''}${this.pseudos
|
|
? this.pseudos
|
|
.map((pseudo) => `:${pseudo.name}${pseudo.arguments ? `(${pseudo.arguments})` : ''}`)
|
|
.join('')
|
|
: ''}`;
|
|
}
|
|
}
|
|
exports.default = SelectorItem;
|
|
//# sourceMappingURL=SelectorItem.cjs.map
|