@dooboostore/dom-parser 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/cjs/DomParser.js +33 -12
  2. package/dist/cjs/DomParser.js.map +2 -2
  3. package/dist/cjs/node/DocumentBase.js +4 -0
  4. package/dist/cjs/node/DocumentBase.js.map +2 -2
  5. package/dist/cjs/node/elements/Element.js.map +1 -1
  6. package/dist/cjs/node/elements/ElementBase.js +12 -2
  7. package/dist/cjs/node/elements/ElementBase.js.map +2 -2
  8. package/dist/cjs/node/elements/HTMLElement.js.map +1 -1
  9. package/dist/cjs/node/elements/HTMLElementBase.js +154 -2
  10. package/dist/cjs/node/elements/HTMLElementBase.js.map +2 -2
  11. package/dist/cjs/window/WindowBase.js +128 -7
  12. package/dist/cjs/window/WindowBase.js.map +2 -2
  13. package/dist/esm/DomParser.js +33 -12
  14. package/dist/esm/DomParser.js.map +2 -2
  15. package/dist/esm/node/DocumentBase.js +4 -0
  16. package/dist/esm/node/DocumentBase.js.map +2 -2
  17. package/dist/esm/node/elements/ElementBase.js +12 -2
  18. package/dist/esm/node/elements/ElementBase.js.map +2 -2
  19. package/dist/esm/node/elements/HTMLElementBase.js +154 -2
  20. package/dist/esm/node/elements/HTMLElementBase.js.map +2 -2
  21. package/dist/esm/window/WindowBase.js +128 -7
  22. package/dist/esm/window/WindowBase.js.map +2 -2
  23. package/dist/esm-bundle/dooboostore-dom-parser.esm.js +504 -195
  24. package/dist/esm-bundle/dooboostore-dom-parser.esm.js.map +3 -3
  25. package/dist/types/DomParser.d.ts +4 -0
  26. package/dist/types/DomParser.d.ts.map +1 -1
  27. package/dist/types/node/DocumentBase.d.ts +2 -0
  28. package/dist/types/node/DocumentBase.d.ts.map +1 -1
  29. package/dist/types/node/elements/Element.d.ts +1 -0
  30. package/dist/types/node/elements/Element.d.ts.map +1 -1
  31. package/dist/types/node/elements/ElementBase.d.ts +2 -2
  32. package/dist/types/node/elements/ElementBase.d.ts.map +1 -1
  33. package/dist/types/node/elements/HTMLElement.d.ts +32 -1
  34. package/dist/types/node/elements/HTMLElement.d.ts.map +1 -1
  35. package/dist/types/node/elements/HTMLElementBase.d.ts +11 -2
  36. package/dist/types/node/elements/HTMLElementBase.d.ts.map +1 -1
  37. package/dist/types/window/WindowBase.d.ts +12 -2
  38. package/dist/types/window/WindowBase.d.ts.map +1 -1
  39. package/dist/umd-bundle/dooboostore-dom-parser.umd.js +504 -195
  40. package/dist/umd-bundle/dooboostore-dom-parser.umd.js.map +3 -3
  41. package/package.json +1 -1
  42. package/src/DomParser.ts +457 -436
  43. package/src/node/DocumentBase.ts +7 -2
  44. package/src/node/elements/Element.ts +24 -23
  45. package/src/node/elements/ElementBase.ts +50 -41
  46. package/src/node/elements/HTMLElement.ts +36 -1
  47. package/src/node/elements/HTMLElementBase.ts +191 -5
  48. package/src/window/WindowBase.ts +1128 -919
@@ -1,6 +1,7 @@
1
1
  import {ParentNodeBase} from './ParentNodeBase';
2
2
  import {DOCUMENT_NODE, Node} from './Node';
3
3
  import {Document, ElementCreationOptions, DocumentReadyState, DocumentVisibilityState, ImportNodeOptions, CaretPositionFromPointOptions, StartViewTransitionOptions, ViewTransitionUpdateCallback} from './Document';
4
+ import {Location} from '../window/Window';
4
5
  import {Comment} from './Comment';
5
6
  import {Text} from './Text';
6
7
  import {DocumentFragment} from './DocumentFragment';
@@ -65,7 +66,7 @@ export class DocumentBase extends ParentNodeBase implements Document {
65
66
  readonly links: HTMLCollectionOf<any> = new HTMLCollectionOf([]);
66
67
 
67
68
  // Location property - simple implementation for Document
68
- private _location: any = {
69
+ private _location: Location = {
69
70
  href: 'about:blank',
70
71
  protocol: 'about:',
71
72
  host: '',
@@ -78,9 +79,13 @@ export class DocumentBase extends ParentNodeBase implements Document {
78
79
  assign: (url: string) => { this._location.href = url; },
79
80
  replace: (url: string) => { this._location.href = url; },
80
81
  reload: () => {},
82
+ // @ts-ignore
81
83
  toString: () => this._location.href
82
84
  };
83
-
85
+
86
+ setLocation(location: Location): void {
87
+ this._location = location;
88
+ }
84
89
  get location(): any {
85
90
  return this._location;
86
91
  }
@@ -1,11 +1,11 @@
1
1
  import { Node } from '../Node';
2
2
  import { ChildNode } from '../ChildNode';
3
3
  import { ParentNode } from '../ParentNode';
4
- import {HTMLCollectionOf} from '../collection/HTMLCollectionOf';
5
- import {HTMLElement} from './HTMLElement';
6
- import {SVGElement} from './SVGElement';
7
- import {MathMLElement} from './MathMLElement';
8
- import {HTMLElementTagNameMap, SVGElementTagNameMap, MathMLElementTagNameMap} from '../index';
4
+ import { HTMLCollectionOf } from '../collection/HTMLCollectionOf';
5
+ import { HTMLElement } from './HTMLElement';
6
+ import { SVGElement } from './SVGElement';
7
+ import { MathMLElement } from './MathMLElement';
8
+ import { HTMLElementTagNameMap, SVGElementTagNameMap, MathMLElementTagNameMap } from '../index';
9
9
  // Forward declarations for types that will be implemented later
10
10
  export interface NamedNodeMap {
11
11
  readonly length: number;
@@ -38,6 +38,7 @@ export interface DOMRect {
38
38
  readonly width: number;
39
39
  readonly x: number;
40
40
  readonly y: number;
41
+ toJSON(): any;
41
42
  }
42
43
 
43
44
  export interface DOMRectList {
@@ -235,24 +236,24 @@ export interface Event {
235
236
  stopImmediatePropagation(): void;
236
237
  }
237
238
 
238
- export interface UIEvent extends Event {}
239
- export interface MouseEvent extends UIEvent {}
240
- export interface KeyboardEvent extends UIEvent {}
241
- export interface FocusEvent extends UIEvent {}
242
- export interface InputEvent extends UIEvent {}
243
- export interface WheelEvent extends MouseEvent {}
244
- export interface PointerEvent extends MouseEvent {}
245
- export interface TouchEvent extends UIEvent {}
246
- export interface DragEvent extends MouseEvent {}
247
- export interface ClipboardEvent extends Event {}
248
- export interface AnimationEvent extends Event {}
249
- export interface TransitionEvent extends Event {}
250
- export interface CompositionEvent extends UIEvent {}
251
- export interface FormDataEvent extends Event {}
252
- export interface ProgressEvent extends Event {}
253
- export interface SecurityPolicyViolationEvent extends Event {}
254
- export interface SubmitEvent extends Event {}
255
- export interface ErrorEvent extends Event {}
239
+ export interface UIEvent extends Event { }
240
+ export interface MouseEvent extends UIEvent { }
241
+ export interface KeyboardEvent extends UIEvent { }
242
+ export interface FocusEvent extends UIEvent { }
243
+ export interface InputEvent extends UIEvent { }
244
+ export interface WheelEvent extends MouseEvent { }
245
+ export interface PointerEvent extends MouseEvent { }
246
+ export interface TouchEvent extends UIEvent { }
247
+ export interface DragEvent extends MouseEvent { }
248
+ export interface ClipboardEvent extends Event { }
249
+ export interface AnimationEvent extends Event { }
250
+ export interface TransitionEvent extends Event { }
251
+ export interface CompositionEvent extends UIEvent { }
252
+ export interface FormDataEvent extends Event { }
253
+ export interface ProgressEvent extends Event { }
254
+ export interface SecurityPolicyViolationEvent extends Event { }
255
+ export interface SubmitEvent extends Event { }
256
+ export interface ErrorEvent extends Event { }
256
257
 
257
258
  export interface EventTarget {
258
259
  addEventListener(type: string, listener: EventListenerOrEventListenerObject | null, options?: boolean | AddEventListenerOptions): void;
@@ -3,7 +3,7 @@ import { ChildNodeBase } from '../ChildNodeBase';
3
3
  import { HTMLCollection } from "../collection";
4
4
  import { Text } from "../Text";
5
5
  import { ElementFactory } from "../../factory/ElementFactory";
6
- import { Element, DOMTokenList, Attr, NamedNodeMap } from './Element';
6
+ import { Element, DOMTokenList, Attr, NamedNodeMap, DOMRect } from './Element';
7
7
  import { ELEMENT_NODE, TEXT_NODE, ATTRIBUTE_NODE } from '../Node';
8
8
  import { CSSSelector } from '../../utils/CSSSelector';
9
9
  import { HTMLElementTagNameMap } from "./index";
@@ -121,10 +121,9 @@ export abstract class ElementBase extends ParentNodeBase implements Element {
121
121
  */
122
122
  private generateChildElementHTML(element: any): string {
123
123
  const tagName = element.tagName.toLowerCase();
124
-
125
124
  // Get attributes
126
125
  const attrs = Array.from(element._attributes?.entries() || [])
127
- .map(([name, value]: [string, string]) => value === '' ? ` ${name}` : ` ${name}="${value.replace(/"/g, '&quot;')}"`)
126
+ .map(([name, value]: [string, string]) => value === '' ? ` ${name}` : ` ${name}="${String(value).replace(/"/g, '&quot;')}"`)
128
127
  .join('');
129
128
 
130
129
  // Check if it's a self-closing tag
@@ -154,13 +153,13 @@ export abstract class ElementBase extends ParentNodeBase implements Element {
154
153
  */
155
154
  private parseAndAppendHTML(html: string): void {
156
155
  const ElementFactory = require('../../factory/ElementFactory').ElementFactory;
157
-
156
+
158
157
  let i = 0;
159
158
  const length = html.length;
160
-
159
+
161
160
  while (i < length) {
162
161
  const nextTagStart = html.indexOf('<', i);
163
-
162
+
164
163
  // Handle text content before next tag
165
164
  if (nextTagStart === -1) {
166
165
  // No more tags, rest is text
@@ -184,15 +183,15 @@ export abstract class ElementBase extends ParentNodeBase implements Element {
184
183
  this.appendChild(textNode);
185
184
  }
186
185
  }
187
-
186
+
188
187
  i = nextTagStart;
189
-
188
+
190
189
  // Parse the tag - find the real tag end, considering quoted attributes
191
190
  const tagEnd = this.findTagEnd(html, i);
192
191
  if (tagEnd === -1) break;
193
-
192
+
194
193
  const tagContent = html.substring(i + 1, tagEnd);
195
-
194
+
196
195
  // Handle comments
197
196
  if (tagContent.startsWith('!--')) {
198
197
  const commentEnd = html.indexOf('-->', i);
@@ -205,20 +204,20 @@ export abstract class ElementBase extends ParentNodeBase implements Element {
205
204
  continue;
206
205
  }
207
206
  }
208
-
207
+
209
208
  // Handle self-closing tags
210
209
  if (tagContent.endsWith('/')) {
211
210
  const parts = tagContent.slice(0, -1).trim().split(/\s+/);
212
211
  const tagName = parts[0];
213
212
  const attributeString = tagContent.slice(tagName.length, -1).trim();
214
-
213
+
215
214
  const element = ElementFactory.createElement(tagName, this._ownerDocument);
216
215
  this.parseAttributes(element, attributeString);
217
216
  this.appendChild(element);
218
217
  i = tagEnd + 1;
219
218
  continue;
220
219
  }
221
-
220
+
222
221
  // Handle closing tags - if they don't have matching opening tags, treat as text
223
222
  if (tagContent.startsWith('/')) {
224
223
  // For now, treat unmatched closing tags as text content
@@ -229,47 +228,47 @@ export abstract class ElementBase extends ParentNodeBase implements Element {
229
228
  i = tagEnd + 1;
230
229
  continue;
231
230
  }
232
-
231
+
233
232
  // Handle opening tags
234
233
  const parts = tagContent.split(/\s+/);
235
234
  const tagName = parts[0];
236
235
  const attributeString = tagContent.slice(tagName.length).trim();
237
-
236
+
238
237
  // Special handling for style and script tags
239
238
  if (tagName === 'style' || tagName === 'script') {
240
239
  const closingTag = `</${tagName}>`;
241
240
  const closingTagIndex = html.indexOf(closingTag, tagEnd + 1);
242
-
241
+
243
242
  if (closingTagIndex !== -1) {
244
243
  const element = ElementFactory.createElement(tagName, this._ownerDocument);
245
244
  this.parseAttributes(element, attributeString);
246
-
245
+
247
246
  const content = html.substring(tagEnd + 1, closingTagIndex);
248
247
  if (content) {
249
248
  const { TextBase } = require('../TextBase');
250
249
  const textNode = new TextBase(content, this._ownerDocument);
251
250
  element.appendChild(textNode);
252
251
  }
253
-
252
+
254
253
  this.appendChild(element);
255
254
  i = closingTagIndex + closingTag.length;
256
255
  continue;
257
256
  }
258
257
  }
259
-
258
+
260
259
  // Handle regular opening tags with content
261
260
  const closingTag = `</${tagName}>`;
262
261
  const closingTagIndex = this.findMatchingClosingTag(html, tagName, tagEnd + 1);
263
-
262
+
264
263
  if (closingTagIndex !== -1) {
265
264
  const element = ElementFactory.createElement(tagName, this._ownerDocument);
266
265
  this.parseAttributes(element, attributeString);
267
-
266
+
268
267
  const content = html.substring(tagEnd + 1, closingTagIndex);
269
268
  if (content.trim()) {
270
269
  element.innerHTML = content;
271
270
  }
272
-
271
+
273
272
  this.appendChild(element);
274
273
  i = closingTagIndex + closingTag.length;
275
274
  } else {
@@ -281,7 +280,7 @@ export abstract class ElementBase extends ParentNodeBase implements Element {
281
280
  }
282
281
  }
283
282
  }
284
-
283
+
285
284
  /**
286
285
  * Find the real end of a tag, considering quoted attributes
287
286
  */
@@ -289,10 +288,10 @@ export abstract class ElementBase extends ParentNodeBase implements Element {
289
288
  let i = startIndex + 1; // Skip the '<'
290
289
  let inQuotes = false;
291
290
  let quoteChar = '';
292
-
291
+
293
292
  while (i < html.length) {
294
293
  const char = html[i];
295
-
294
+
296
295
  if (!inQuotes) {
297
296
  if (char === '"' || char === "'") {
298
297
  inQuotes = true;
@@ -306,10 +305,10 @@ export abstract class ElementBase extends ParentNodeBase implements Element {
306
305
  quoteChar = '';
307
306
  }
308
307
  }
309
-
308
+
310
309
  i++;
311
310
  }
312
-
311
+
313
312
  return -1; // No closing '>' found
314
313
  }
315
314
 
@@ -330,15 +329,15 @@ export abstract class ElementBase extends ParentNodeBase implements Element {
330
329
  const closeTag = `</${tagName}>`;
331
330
  let depth = 1;
332
331
  let i = startIndex;
333
-
332
+
334
333
  while (i < html.length && depth > 0) {
335
334
  const nextOpen = html.indexOf(openTag, i);
336
335
  const nextClose = html.indexOf(closeTag, i);
337
-
336
+
338
337
  if (nextClose === -1) {
339
338
  return -1; // No closing tag found
340
339
  }
341
-
340
+
342
341
  if (nextOpen !== -1 && nextOpen < nextClose) {
343
342
  // Found another opening tag before the closing tag
344
343
  // Make sure it's a complete tag (not just a substring)
@@ -356,7 +355,7 @@ export abstract class ElementBase extends ParentNodeBase implements Element {
356
355
  i = nextClose + closeTag.length;
357
356
  }
358
357
  }
359
-
358
+
360
359
  return -1;
361
360
  }
362
361
 
@@ -408,18 +407,18 @@ export abstract class ElementBase extends ParentNodeBase implements Element {
408
407
 
409
408
  if (position < length) {
410
409
  const quote = attributeString[position];
411
-
410
+
412
411
  if (quote === '"' || quote === "'") {
413
412
  // Quoted value - find matching closing quote
414
413
  position++; // Skip opening quote
415
414
  const valueStart = position;
416
-
415
+
417
416
  while (position < length && attributeString[position] !== quote) {
418
417
  position++;
419
418
  }
420
-
419
+
421
420
  value = attributeString.substring(valueStart, position);
422
-
421
+
423
422
  if (position < length && attributeString[position] === quote) {
424
423
  position++; // Skip closing quote
425
424
  }
@@ -436,7 +435,7 @@ export abstract class ElementBase extends ParentNodeBase implements Element {
436
435
 
437
436
  // Decode HTML entities in attribute values
438
437
  value = this.decodeHTMLEntities(value);
439
-
438
+
440
439
  element.setAttribute(name, value);
441
440
  }
442
441
  }
@@ -488,7 +487,7 @@ export abstract class ElementBase extends ParentNodeBase implements Element {
488
487
  if (entityMap[entity]) {
489
488
  return entityMap[entity];
490
489
  }
491
-
490
+
492
491
  // Handle numeric entities like &#39; &#34;
493
492
  if (entity.startsWith('&#') && entity.endsWith(';')) {
494
493
  const numStr = entity.slice(2, -1);
@@ -497,7 +496,7 @@ export abstract class ElementBase extends ParentNodeBase implements Element {
497
496
  return String.fromCharCode(num);
498
497
  }
499
498
  }
500
-
499
+
501
500
  // Handle hex entities like &#x27;
502
501
  if (entity.startsWith('&#x') && entity.endsWith(';')) {
503
502
  const hexStr = entity.slice(3, -1);
@@ -506,7 +505,7 @@ export abstract class ElementBase extends ParentNodeBase implements Element {
506
505
  return String.fromCharCode(num);
507
506
  }
508
507
  }
509
-
508
+
510
509
  // Return original if not recognized
511
510
  return entity;
512
511
  });
@@ -708,8 +707,18 @@ export abstract class ElementBase extends ParentNodeBase implements Element {
708
707
  return this.getAttributeNode(localName);
709
708
  }
710
709
 
711
- getBoundingClientRect(): any {
712
- throw new Error('Element.getBoundingClientRect() is not implemented yet');
710
+ getBoundingClientRect(): DOMRect {
711
+ return {
712
+ bottom: 0,
713
+ height: 0,
714
+ left: 0,
715
+ right: 0,
716
+ top: 0,
717
+ width: 0,
718
+ x: 0,
719
+ y: 0,
720
+ toJSON: () => ({})
721
+ };
713
722
  }
714
723
 
715
724
  getClientRects(): any {
@@ -1,11 +1,29 @@
1
1
  import { Element } from './Element';
2
2
 
3
+ /**
4
+ * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ElementCSSInlineStyle)
5
+ */
6
+ export interface ElementCSSInlineStyle {
7
+ /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLElement/attributeStyleMap) */
8
+ readonly attributeStyleMap: StylePropertyMap;
9
+ /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLElement/style) */
10
+ style: CSSStyleDeclaration | string;
11
+ }
12
+
13
+ export interface StylePropertyMap {
14
+ append(property: string, ...values: (string | any)[]): void;
15
+ clear(): void;
16
+ delete(property: string): void;
17
+ set(property: string, ...values: (string | any)[]): void;
18
+ [key: string]: any;
19
+ }
20
+
3
21
  /**
4
22
  * The **`HTMLElement`** interface represents any HTML element.
5
23
  *
6
24
  * [MDN Reference](https://developer.mozilla.org/docs/Web/API/HTMLElement)
7
25
  */
8
- export interface HTMLElement extends Element {
26
+ export interface HTMLElement extends Element, ElementCSSInlineStyle {
9
27
  /**
10
28
  * Returns the value of element's title content attribute. Can be set to change it.
11
29
  *
@@ -102,4 +120,21 @@ export interface HTMLElement extends Element {
102
120
  // 기본 타입들
103
121
  export interface FocusOptions {
104
122
  preventScroll?: boolean;
123
+ }
124
+
125
+ export interface CSSStyleDeclaration {
126
+ cssText: string;
127
+ length: number;
128
+ parentRule: any;
129
+ getPropertyPriority(property: string): string;
130
+ getPropertyValue(property: string): string;
131
+ item(index: number): string;
132
+ removeProperty(property: string): string;
133
+ setProperty(property: string, value: string | null, priority?: string): void;
134
+ [index: number]: string;
135
+ [key: string]: any;
136
+ }
137
+
138
+ export interface DOMStringMap {
139
+ [key: string]: string | undefined;
105
140
  }
@@ -1,12 +1,12 @@
1
1
  import { ElementBase } from './ElementBase';
2
- import { HTMLElement, FocusOptions } from './HTMLElement';
2
+ import { HTMLElement, FocusOptions, CSSStyleDeclaration, DOMStringMap, ElementCSSInlineStyle, StylePropertyMap } from './HTMLElement';
3
3
  import { Text } from '../Text';
4
4
  import { ElementFactory } from '../../factory';
5
5
 
6
6
  /**
7
7
  * Base implementation for all HTML elements
8
8
  */
9
- export abstract class HTMLElementBase extends ElementBase implements HTMLElement {
9
+ export abstract class HTMLElementBase extends ElementBase implements HTMLElement, ElementCSSInlineStyle {
10
10
  private _title: string = '';
11
11
  private _lang: string = '';
12
12
  private _dir: string = '';
@@ -124,9 +124,6 @@ export abstract class HTMLElementBase extends ElementBase implements HTMLElement
124
124
  }
125
125
 
126
126
  focus(options?: FocusOptions): void {
127
- // Set focus state using data attribute
128
- this.setAttribute('data-focused', 'true');
129
- console.log(`Focused on ${this.tagName} element`);
130
127
  }
131
128
 
132
129
  blur(): void {
@@ -195,4 +192,193 @@ export abstract class HTMLElementBase extends ElementBase implements HTMLElement
195
192
  break;
196
193
  }
197
194
  }
195
+
196
+ // New implementations for style, dataset, nonce
197
+ private _style: CSSStyleDeclaration | null = null;
198
+ private _dataset: DOMStringMap | null = null;
199
+
200
+ get style(): CSSStyleDeclaration {
201
+ if (!this._style) {
202
+ this._style = new Proxy(new CSSStyleDeclarationImpl(this), {
203
+ get: (target, prop: string | symbol) => {
204
+ if (typeof prop === 'string' && !(prop in target)) {
205
+ // Convert camelCase to kebab-case for CSS properties
206
+ const cssProp = prop.replace(/[A-Z]/g, m => `-${m.toLowerCase()}`);
207
+ return target.getPropertyValue(cssProp);
208
+ }
209
+ return (target as any)[prop];
210
+ },
211
+ set: (target, prop: string | symbol, value: any) => {
212
+ if (typeof prop === 'string' && !(prop in target)) {
213
+ // Convert camelCase to kebab-case for CSS properties
214
+ const cssProp = prop.replace(/[A-Z]/g, m => `-${m.toLowerCase()}`);
215
+ target.setProperty(cssProp, String(value));
216
+ return true;
217
+ }
218
+ (target as any)[prop] = value;
219
+ return true;
220
+ }
221
+ }) as unknown as CSSStyleDeclaration;
222
+ }
223
+ return this._style;
224
+ }
225
+
226
+ set style(value: string | CSSStyleDeclaration) {
227
+ if (typeof value === 'string') {
228
+ this.style.cssText = value;
229
+ }
230
+ }
231
+
232
+ private _attributeStyleMap: StylePropertyMap | null = null;
233
+
234
+ get attributeStyleMap(): StylePropertyMap {
235
+ if (!this._attributeStyleMap) {
236
+ this._attributeStyleMap = new StylePropertyMapImpl(this);
237
+ }
238
+ return this._attributeStyleMap;
239
+ }
240
+
241
+ get dataset(): DOMStringMap {
242
+ if (!this._dataset) {
243
+ this._dataset = new Proxy({}, {
244
+ get: (target, prop: string | symbol) => {
245
+ if (typeof prop === 'string') {
246
+ // Convert camelCase to kebab-case: fooBar -> data-foo-bar
247
+ const attrName = 'data-' + prop.replace(/[A-Z]/g, m => `-${m.toLowerCase()}`);
248
+ return this.getAttribute(attrName);
249
+ }
250
+ return undefined;
251
+ },
252
+ set: (target, prop: string | symbol, value: any) => {
253
+ if (typeof prop === 'string') {
254
+ // Convert camelCase to kebab-case: fooBar -> data-foo-bar
255
+ const attrName = 'data-' + prop.replace(/[A-Z]/g, m => `-${m.toLowerCase()}`);
256
+ this.setAttribute(attrName, String(value));
257
+ return true;
258
+ }
259
+ return false;
260
+ },
261
+ deleteProperty: (target, prop: string | symbol) => {
262
+ if (typeof prop === 'string') {
263
+ const attrName = 'data-' + prop.replace(/[A-Z]/g, m => `-${m.toLowerCase()}`);
264
+ this.removeAttribute(attrName);
265
+ return true;
266
+ }
267
+ return false;
268
+ }
269
+ });
270
+ }
271
+ return this._dataset;
272
+ }
273
+
274
+ get nonce(): string {
275
+ return this.getAttribute('nonce') || '';
276
+ }
277
+
278
+ set nonce(value: string) {
279
+ this.setAttribute('nonce', value);
280
+ }
281
+ }
282
+
283
+ // CSSStyleDeclaration implementation
284
+ class CSSStyleDeclarationImpl {
285
+ [key: string]: any;
286
+
287
+ constructor(private element: HTMLElementBase) { }
288
+
289
+ parentRule: any = null;
290
+
291
+ getPropertyPriority(property: string): string {
292
+ return '';
293
+ }
294
+
295
+ get cssText(): string {
296
+ return this.element.getAttribute('style') || '';
297
+ }
298
+
299
+ set cssText(value: string) {
300
+ this.element.setAttribute('style', value);
301
+ }
302
+
303
+ get length(): number {
304
+ return this.parseStyle(this.cssText).size;
305
+ }
306
+
307
+ getPropertyValue(property: string): string {
308
+ const style = this.parseStyle(this.cssText);
309
+ return style.get(property) || '';
310
+ }
311
+
312
+ setProperty(property: string, value: string | null, priority: string = ''): void {
313
+ const style = this.parseStyle(this.cssText);
314
+ if (value === null || value === '') {
315
+ style.delete(property);
316
+ } else {
317
+ style.set(property, value); // TODO: handle priority
318
+ }
319
+ this.cssText = this.serializeStyle(style);
320
+ }
321
+
322
+ removeProperty(property: string): string {
323
+ const style = this.parseStyle(this.cssText);
324
+ const value = style.get(property) || '';
325
+ style.delete(property);
326
+ this.cssText = this.serializeStyle(style);
327
+ return value;
328
+ }
329
+
330
+ item(index: number): string {
331
+ const style = this.parseStyle(this.cssText);
332
+ return Array.from(style.keys())[index] || '';
333
+ }
334
+
335
+ private parseStyle(cssText: string): Map<string, string> {
336
+ const style = new Map<string, string>();
337
+ if (!cssText) return style;
338
+
339
+ cssText.split(';').forEach(declaration => {
340
+ const part = declaration.trim();
341
+ if (!part) return;
342
+
343
+ const colonIndex = part.indexOf(':');
344
+ if (colonIndex !== -1) {
345
+ const property = part.substring(0, colonIndex).trim();
346
+ const value = part.substring(colonIndex + 1).trim();
347
+ if (property && value) {
348
+ style.set(property, value);
349
+ }
350
+ }
351
+ });
352
+ return style;
353
+ }
354
+
355
+ private serializeStyle(style: Map<string, string>): string {
356
+ return Array.from(style.entries())
357
+ .map(([property, value]) => `${property}: ${value}`)
358
+ .join('; ');
359
+ }
360
+ }
361
+
362
+ // StylePropertyMap implementation
363
+ class StylePropertyMapImpl implements StylePropertyMap {
364
+ [key: string]: any;
365
+
366
+ constructor(private element: HTMLElementBase) { }
367
+
368
+ set(property: string, ...values: (string | any)[]): void {
369
+ const value = values[0];
370
+ this.element.style.setProperty(property, String(value));
371
+ }
372
+
373
+ append(property: string, ...values: (string | any)[]): void {
374
+ this.set(property, ...values);
375
+ }
376
+
377
+ delete(property: string): void {
378
+ this.element.style.removeProperty(property);
379
+ }
380
+
381
+ clear(): void {
382
+ this.element.style.cssText = '';
383
+ }
198
384
  }