@joist/element 4.0.0-next.4 → 4.0.0-next.6

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 (56) hide show
  1. package/README.md +2 -2
  2. package/package.json +4 -3
  3. package/src/lib/attr.test.ts +107 -98
  4. package/src/lib/attr.ts +2 -2
  5. package/src/lib/element.test.ts +84 -80
  6. package/src/lib/element.ts +5 -13
  7. package/src/lib/lifecycle.test.ts +31 -0
  8. package/src/lib/lifecycle.ts +9 -0
  9. package/src/lib/listen.ts +1 -1
  10. package/src/lib/metadata.ts +1 -0
  11. package/src/lib/query.test.ts +43 -44
  12. package/src/lib/query.ts +1 -1
  13. package/src/lib/result.ts +2 -22
  14. package/src/lib/tags.ts +28 -13
  15. package/src/lib/template.test.ts +65 -0
  16. package/src/lib/template.ts +132 -0
  17. package/src/lib.ts +3 -2
  18. package/target/lib/attr.d.ts +1 -1
  19. package/target/lib/attr.js +1 -1
  20. package/target/lib/attr.js.map +1 -1
  21. package/target/lib/attr.test.js +258 -252
  22. package/target/lib/attr.test.js.map +1 -1
  23. package/target/lib/element.d.ts +2 -5
  24. package/target/lib/element.js +3 -9
  25. package/target/lib/element.js.map +1 -1
  26. package/target/lib/element.test.js +181 -179
  27. package/target/lib/element.test.js.map +1 -1
  28. package/target/lib/lifecycle.d.ts +1 -0
  29. package/target/lib/lifecycle.js +8 -0
  30. package/target/lib/lifecycle.js.map +1 -0
  31. package/target/lib/lifecycle.test.d.ts +1 -0
  32. package/target/lib/lifecycle.test.js +48 -0
  33. package/target/lib/lifecycle.test.js.map +1 -0
  34. package/target/lib/listen.d.ts +1 -1
  35. package/target/lib/listen.js.map +1 -1
  36. package/target/lib/metadata.d.ts +1 -0
  37. package/target/lib/metadata.js +1 -0
  38. package/target/lib/metadata.js.map +1 -1
  39. package/target/lib/query.d.ts +1 -1
  40. package/target/lib/query.test.js +72 -74
  41. package/target/lib/query.test.js.map +1 -1
  42. package/target/lib/result.d.ts +2 -9
  43. package/target/lib/result.js +1 -14
  44. package/target/lib/result.js.map +1 -1
  45. package/target/lib/tags.d.ts +11 -7
  46. package/target/lib/tags.js +20 -11
  47. package/target/lib/tags.js.map +1 -1
  48. package/target/lib/template.d.ts +6 -0
  49. package/target/lib/template.js +91 -0
  50. package/target/lib/template.js.map +1 -0
  51. package/target/lib/template.test.d.ts +1 -0
  52. package/target/lib/template.test.js +47 -0
  53. package/target/lib/template.test.js.map +1 -0
  54. package/target/lib.d.ts +3 -2
  55. package/target/lib.js +3 -2
  56. package/target/lib.js.map +1 -1
@@ -1,54 +1,53 @@
1
- import { expect } from '@open-wc/testing';
1
+ import { expect } from 'chai';
2
+
2
3
  import { element } from './element.js';
3
4
  import { query } from './query.js';
4
5
  import { html } from './tags.js';
5
6
 
6
- describe('query', () => {
7
- it('should work', () => {
8
- @element({
9
- tagName: 'query-test-1',
10
- shadow: [
11
- html`
12
- <form>
13
- <input id="fname" name="fname" />
14
- <input id="lname" name="lname" />
15
- </form>
16
- `
17
- ]
18
- })
19
- class MyElement extends HTMLElement {
20
- fname = query<HTMLInputElement>('#fname');
21
- lname = query<HTMLInputElement>('#lname');
22
- }
7
+ it('should work', () => {
8
+ @element({
9
+ tagName: 'query-test-1',
10
+ shadow: [
11
+ html`
12
+ <form>
13
+ <input id="fname" name="fname" />
14
+ <input id="lname" name="lname" />
15
+ </form>
16
+ `
17
+ ]
18
+ })
19
+ class MyElement extends HTMLElement {
20
+ fname = query<HTMLInputElement>('#fname');
21
+ lname = query<HTMLInputElement>('#lname');
22
+ }
23
23
 
24
- const el = new MyElement();
24
+ const el = new MyElement();
25
25
 
26
- expect(el.fname()).to.equal(el.shadowRoot?.querySelector('#fname'));
27
- expect(el.lname()).to.equal(el.shadowRoot?.querySelector('#lname'));
28
- });
26
+ expect(el.fname()).to.equal(el.shadowRoot?.querySelector('#fname'));
27
+ expect(el.lname()).to.equal(el.shadowRoot?.querySelector('#lname'));
28
+ });
29
29
 
30
- it('should patch the selected item', () => {
31
- @element({
32
- tagName: 'query-test-2',
33
- shadow: [
34
- html`
35
- <form>
36
- <input id="fname" name="fname" />
37
- <input id="lname" name="lname" />
38
- </form>
39
- `
40
- ]
41
- })
42
- class MyElement extends HTMLElement {
43
- fname = query<HTMLInputElement>('#fname');
44
- lname = query<HTMLInputElement>('#lname');
45
- }
30
+ it('should patch the selected item', () => {
31
+ @element({
32
+ tagName: 'query-test-2',
33
+ shadow: [
34
+ html`
35
+ <form>
36
+ <input id="fname" name="fname" />
37
+ <input id="lname" name="lname" />
38
+ </form>
39
+ `
40
+ ]
41
+ })
42
+ class MyElement extends HTMLElement {
43
+ fname = query<HTMLInputElement>('#fname');
44
+ lname = query<HTMLInputElement>('#lname');
45
+ }
46
46
 
47
- const el = new MyElement();
48
- el.fname({ value: 'Foo' });
49
- el.lname({ value: 'Bar' });
47
+ const el = new MyElement();
48
+ el.fname({ value: 'Foo' });
49
+ el.lname({ value: 'Bar' });
50
50
 
51
- expect(el.shadowRoot?.querySelector<HTMLInputElement>('#fname')?.value).to.equal('Foo');
52
- expect(el.shadowRoot?.querySelector<HTMLInputElement>('#lname')?.value).to.equal('Bar');
53
- });
51
+ expect(el.shadowRoot?.querySelector<HTMLInputElement>('#fname')?.value).to.equal('Foo');
52
+ expect(el.shadowRoot?.querySelector<HTMLInputElement>('#lname')?.value).to.equal('Bar');
54
53
  });
package/src/lib/query.ts CHANGED
@@ -7,7 +7,7 @@ type QueryResult<T> = (updates?: Partial<T>) => T;
7
7
  export function query<K extends Tags>(selectors: K): QueryResult<HTMLElementTagNameMap[K]>;
8
8
  export function query<K extends SVGTags>(selectors: K): QueryResult<SVGElementTagNameMap[K]>;
9
9
  export function query<K extends MathTags>(selectors: K): QueryResult<MathMLElementTagNameMap[K]>;
10
- export function query<E extends Element = Element>(selectors: string): QueryResult<E>;
10
+ export function query<E extends HTMLElement = HTMLElement>(selectors: string): QueryResult<E>;
11
11
  export function query<K extends Tags>(query: K): QueryResult<HTMLElementTagNameMap[K]> {
12
12
  let res: HTMLElementTagNameMap[K] | null = null;
13
13
 
package/src/lib/result.ts CHANGED
@@ -1,23 +1,3 @@
1
- export interface ShadowResult {
2
- run(el: HTMLElement): void;
3
- }
4
-
5
- export abstract class JoistShadowResult implements ShadowResult {
6
- strings;
7
- values;
8
-
9
- constructor(raw: TemplateStringsArray, ...values: any[]) {
10
- this.strings = raw;
11
- this.values = values;
12
- }
13
-
14
- run(el: HTMLElement) {
15
- if (!el.shadowRoot) {
16
- throw new Error('ShadowResult has not been applied');
17
- }
18
-
19
- this.setup(el.shadowRoot);
20
- }
21
-
22
- abstract setup(root: ShadowRoot): void;
1
+ export interface ShadowResult<T extends HTMLElement> {
2
+ apply(el: T): void;
23
3
  }
package/src/lib/tags.ts CHANGED
@@ -1,28 +1,43 @@
1
- import { JoistShadowResult } from './result.js';
1
+ import { ShadowResult } from './result.js';
2
2
 
3
- export class HTMLResult extends JoistShadowResult {
4
- setup(root: ShadowRoot): void {
5
- let template = document.createElement('template');
6
- template.innerHTML = concat(this.strings);
3
+ export class HTMLResult<T extends HTMLElement> implements ShadowResult<T> {
4
+ #template;
7
5
 
8
- root.append(template.content.cloneNode(true));
6
+ constructor(raw: TemplateStringsArray, ..._values: any[]) {
7
+ this.#template = document.createElement('template');
8
+ this.#template.innerHTML = concat(raw);
9
+ }
10
+
11
+ apply(el: T): void {
12
+ if (el.shadowRoot) {
13
+ el.shadowRoot.append(this.#template.content.cloneNode(true));
14
+ }
9
15
  }
10
16
  }
11
17
 
12
- export function html(strings: TemplateStringsArray, ...values: any[]): HTMLResult {
18
+ export function html<T extends HTMLElement>(
19
+ strings: TemplateStringsArray,
20
+ ...values: any[]
21
+ ): HTMLResult<T> {
13
22
  return new HTMLResult(strings, ...values);
14
23
  }
15
24
 
16
- export class CSSResult extends JoistShadowResult {
17
- setup(root: ShadowRoot): void {
18
- let sheet = new CSSStyleSheet();
19
- sheet.replaceSync(concat(this.strings));
25
+ export class CSSResult<T extends HTMLElement> implements ShadowResult<T> {
26
+ #sheet;
27
+
28
+ constructor(raw: TemplateStringsArray, ..._values: any[]) {
29
+ this.#sheet = new CSSStyleSheet();
30
+ this.#sheet.replaceSync(concat(raw));
31
+ }
20
32
 
21
- root.adoptedStyleSheets = [...root.adoptedStyleSheets, sheet];
33
+ apply(el: HTMLElement): void {
34
+ if (el.shadowRoot) {
35
+ el.shadowRoot.adoptedStyleSheets = [...el.shadowRoot.adoptedStyleSheets, this.#sheet];
36
+ }
22
37
  }
23
38
  }
24
39
 
25
- export function css(strings: TemplateStringsArray): CSSResult {
40
+ export function css<T extends HTMLElement>(strings: TemplateStringsArray): CSSResult<T> {
26
41
  return new CSSResult(strings);
27
42
  }
28
43
 
@@ -0,0 +1,65 @@
1
+ import { assert } from 'chai';
2
+
3
+ import { template } from './template.js';
4
+
5
+ // Run all tests with both shadow and light dom
6
+ const TESTS = [
7
+ function comments(el: HTMLElement, root: HTMLElement | ShadowRoot) {
8
+ it(`should intialize template comments ${root instanceof ShadowRoot ? '(ShadowDOM)' : '(LightDOM)'}`, () => {
9
+ el.title = 'Hello World';
10
+ el.ariaLabel = 'This is the label';
11
+ el.ariaDescription = 'This is the description';
12
+
13
+ root.innerHTML = /*html*/ `
14
+ <!--#:title-->
15
+
16
+ <ul>
17
+ <li><!--#:ariaLabel--></li>
18
+ <li><!--#:ariaDescription--></li>
19
+ </ul>
20
+ `;
21
+
22
+ const render = template();
23
+
24
+ render.call(el);
25
+
26
+ assert.equal(
27
+ root.innerHTML
28
+ .split('\n')
29
+ .map((res) => res.trim())
30
+ .join(''),
31
+ 'Hello World<ul><li>This is the label</li><li>This is the description</li></ul>'
32
+ );
33
+ });
34
+ },
35
+ function attributes(el: HTMLElement, root: HTMLElement | ShadowRoot) {
36
+ it(`should intialize template attributes ${root instanceof ShadowRoot ? '(ShadowDOM)' : '(LightDOM)'}`, () => {
37
+ el.ariaLabel = 'This is the label';
38
+ el.ariaDescription = 'This is the description';
39
+
40
+ root.innerHTML = /*html*/ `
41
+ <ul aria-label="#:ariaLabel" aria-description="#:ariaDescription"></ul>
42
+ `;
43
+
44
+ const render = template();
45
+
46
+ render.call(el);
47
+
48
+ assert.equal(
49
+ root.innerHTML
50
+ .split('\n')
51
+ .map((res) => res.trim())
52
+ .join(''),
53
+ '<ul aria-label="This is the label" aria-description="This is the description"></ul>'
54
+ );
55
+ });
56
+ }
57
+ ];
58
+
59
+ for (let test of TESTS) {
60
+ const lightEl = document.createElement('div');
61
+ test(lightEl, lightEl);
62
+
63
+ const shadowEl = document.createElement('div');
64
+ test(shadowEl, shadowEl.attachShadow({ mode: 'open' }));
65
+ }
@@ -0,0 +1,132 @@
1
+ const TOKEN_PREFIX = '#:';
2
+
3
+ class NodeMap extends Map<Node, string> {}
4
+
5
+ export interface TemplateOpts {}
6
+
7
+ export interface RenderOpts {
8
+ refresh?: boolean;
9
+ }
10
+
11
+ export function template(_templateOpts?: TemplateOpts) {
12
+ // make sure we only initialize once
13
+ let initialized = false;
14
+
15
+ // Track all nodes that can be updated and their associated property
16
+ const nodes = new NodeMap();
17
+
18
+ return function render<T extends HTMLElement>(this: T, renderOpts?: RenderOpts) {
19
+ if (renderOpts?.refresh) {
20
+ initialized = false;
21
+ nodes.clear();
22
+ }
23
+
24
+ if (initialized) {
25
+ return updateNodes(this, nodes);
26
+ }
27
+
28
+ // find and track nodes
29
+ initializeNodes(this, nodes);
30
+
31
+ initialized = true;
32
+ };
33
+ }
34
+
35
+ function initializeNodes(el: HTMLElement, nodes: NodeMap) {
36
+ const iterator = document.createTreeWalker(
37
+ el.shadowRoot || el,
38
+ NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT
39
+ );
40
+
41
+ while (iterator.nextNode()) {
42
+ const res = trackNode(el, iterator.currentNode, nodes);
43
+
44
+ if (res !== null) {
45
+ iterator.currentNode = res;
46
+ }
47
+ }
48
+ }
49
+
50
+ function updateNodes(el: HTMLElement, nodes: NodeMap) {
51
+ for (let [node, prop] of nodes) {
52
+ const value = Reflect.get(el, prop);
53
+
54
+ const isAttribute = node.nodeType === Node.ATTRIBUTE_NODE;
55
+
56
+ if (isAttribute && isBooleanAttributeNode(node as Attr)) {
57
+ manageBooleanAttribute(el, node as Attr);
58
+ } else if (value !== node.nodeValue) {
59
+ node.nodeValue = value;
60
+ }
61
+ }
62
+ }
63
+
64
+ /**
65
+ * configures and tracks a given Node so that it can be updated in place later.
66
+ */
67
+ function trackNode(el: HTMLElement, node: Node, nodes: NodeMap): Node | null {
68
+ switch (node.nodeType) {
69
+ case Node.COMMENT_NODE: {
70
+ const nodeValue = node.nodeValue?.trim();
71
+
72
+ if (nodeValue?.startsWith(TOKEN_PREFIX)) {
73
+ if (node.parentNode) {
74
+ const propertyKey = nodeValue.replace(TOKEN_PREFIX, '');
75
+ const textNode = document.createTextNode(Reflect.get(el, propertyKey));
76
+
77
+ node.parentNode.replaceChild(textNode, node);
78
+
79
+ nodes.set(textNode, propertyKey);
80
+
81
+ return textNode;
82
+ }
83
+ }
84
+
85
+ break;
86
+ }
87
+
88
+ case Node.ELEMENT_NODE: {
89
+ const element = node as Element;
90
+
91
+ for (let attr of element.attributes) {
92
+ const nodeValue = attr.value.trim();
93
+
94
+ if (isBooleanAttributeNode(attr)) {
95
+ manageBooleanAttribute(el, attr);
96
+
97
+ nodes.set(attr, attr.value);
98
+ } else if (nodeValue.startsWith(TOKEN_PREFIX)) {
99
+ const propertyKey = nodeValue.replace(TOKEN_PREFIX, '');
100
+
101
+ attr.value = Reflect.get(el, propertyKey);
102
+
103
+ nodes.set(attr, propertyKey);
104
+ }
105
+ }
106
+
107
+ break;
108
+ }
109
+ }
110
+
111
+ return null;
112
+ }
113
+
114
+ function isBooleanAttributeNode(attr: Attr) {
115
+ return attr.name.startsWith(TOKEN_PREFIX);
116
+ }
117
+
118
+ function manageBooleanAttribute(el: HTMLElement, attr: Attr) {
119
+ const realAttributeName = attr.name.replace(TOKEN_PREFIX, '');
120
+
121
+ if (attr.ownerElement) {
122
+ let value = attr.value.startsWith('!')
123
+ ? !Reflect.get(el, attr.value.replace('!', ''))
124
+ : Reflect.get(el, attr.value);
125
+
126
+ if (value) {
127
+ attr.ownerElement.setAttribute(realAttributeName, '');
128
+ } else {
129
+ attr.ownerElement.removeAttribute(realAttributeName);
130
+ }
131
+ }
132
+ }
package/src/lib.ts CHANGED
@@ -1,6 +1,7 @@
1
- export { JoistShadowResult as TemplateResult } from './lib/result.js';
2
1
  export { css, html, HTMLResult, CSSResult } from './lib/tags.js';
3
2
  export { attr } from './lib/attr.js';
4
3
  export { listen } from './lib/listen.js';
5
- export { element, LifeCycle } from './lib/element.js';
4
+ export { element } from './lib/element.js';
6
5
  export { query } from './lib/query.js';
6
+ export { template } from './lib/template.js';
7
+ export { ready } from './lib/lifecycle.js';
@@ -1,4 +1,4 @@
1
1
  export interface AttrOpts {
2
- observe?: boolean;
2
+ observed?: boolean;
3
3
  }
4
4
  export declare function attr(opts?: AttrOpts): <This extends HTMLElement>({ get, set }: ClassAccessorDecoratorTarget<This, unknown>, ctx: ClassAccessorDecoratorContext<This>) => ClassAccessorDecoratorResult<This, any>;
@@ -6,7 +6,7 @@ export function attr(opts) {
6
6
  meta.attrs.push({
7
7
  propName: ctx.name,
8
8
  attrName,
9
- observe: opts?.observe ?? true
9
+ observe: opts?.observed ?? true
10
10
  });
11
11
  return {
12
12
  set(value) {
@@ -1 +1 @@
1
- {"version":3,"file":"attr.js","sourceRoot":"","sources":["../../src/lib/attr.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAM9C,MAAM,UAAU,IAAI,CAAC,IAAe;IAClC,OAAO,SAAS,aAAa,CAC3B,EAAE,GAAG,EAAE,GAAG,EAA+C,EACzD,GAAwC;QAExC,MAAM,QAAQ,GAAG,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACzC,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAE9C,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;YACd,QAAQ,EAAE,GAAG,CAAC,IAAI;YAClB,QAAQ;YACR,OAAO,EAAE,IAAI,EAAE,OAAO,IAAI,IAAI;SAC/B,CAAC,CAAC;QAEH,OAAO;YACL,GAAG,CAAC,KAAc;gBAChB,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;oBACnB,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;gBAClC,CAAC;qBAAM,IAAI,KAAK,KAAK,KAAK,EAAE,CAAC;oBAC3B,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;gBACjC,CAAC;qBAAM,CAAC;oBACN,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;gBAC7C,CAAC;gBAED,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YACxB,CAAC;YACD,GAAG;gBACD,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;gBAEzC,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;oBAElB,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;wBAChB,OAAO,IAAI,CAAC;oBACd,CAAC;oBAGD,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;wBAChC,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC;oBACtB,CAAC;oBAGD,OAAO,IAAI,CAAC;gBACd,CAAC;gBAGD,OAAO,OAAO,CAAC;YACjB,CAAC;SACF,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,GAAoB;IACzC,IAAI,KAAa,CAAC;IAElB,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,IAAI,GAAG,CAAC,WAAW,EAAE,CAAC;YACpB,KAAK,GAAG,GAAG,CAAC,WAAW,CAAC;QAC1B,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;QACvE,CAAC;IACH,CAAC;SAAM,CAAC;QACN,KAAK,GAAG,GAAG,CAAC;IACd,CAAC;IAED,OAAO,KAAK,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;AAClD,CAAC"}
1
+ {"version":3,"file":"attr.js","sourceRoot":"","sources":["../../src/lib/attr.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAM9C,MAAM,UAAU,IAAI,CAAC,IAAe;IAClC,OAAO,SAAS,aAAa,CAC3B,EAAE,GAAG,EAAE,GAAG,EAA+C,EACzD,GAAwC;QAExC,MAAM,QAAQ,GAAG,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACzC,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAE9C,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;YACd,QAAQ,EAAE,GAAG,CAAC,IAAI;YAClB,QAAQ;YACR,OAAO,EAAE,IAAI,EAAE,QAAQ,IAAI,IAAI;SAChC,CAAC,CAAC;QAEH,OAAO;YACL,GAAG,CAAC,KAAc;gBAChB,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;oBACnB,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;gBAClC,CAAC;qBAAM,IAAI,KAAK,KAAK,KAAK,EAAE,CAAC;oBAC3B,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;gBACjC,CAAC;qBAAM,CAAC;oBACN,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;gBAC7C,CAAC;gBAED,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YACxB,CAAC;YACD,GAAG;gBACD,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAC/B,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;gBAEzC,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;oBAElB,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;wBAChB,OAAO,IAAI,CAAC;oBACd,CAAC;oBAGD,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;wBAChC,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC;oBACtB,CAAC;oBAGD,OAAO,IAAI,CAAC;gBACd,CAAC;gBAGD,OAAO,OAAO,CAAC;YACjB,CAAC;SACF,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,GAAoB;IACzC,IAAI,KAAa,CAAC;IAElB,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,IAAI,GAAG,CAAC,WAAW,EAAE,CAAC;YACpB,KAAK,GAAG,GAAG,CAAC,WAAW,CAAC;QAC1B,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;QACvE,CAAC;IACH,CAAC;SAAM,CAAC;QACN,KAAK,GAAG,GAAG,CAAC;IACd,CAAC;IAED,OAAO,KAAK,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;AAClD,CAAC"}