vsn 0.1.124 → 0.1.126
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.
- package/demo/demo.html +4 -8
- package/demo/examples/cascading-function-sheets.html +28 -0
- package/demo/examples/component-slots.html +16 -22
- package/demo/examples/loop.html +21 -0
- package/demo/resources/xhr-cfs.html +1 -0
- package/demo/resources/xhr-test-component.html +4 -0
- package/demo/service.html +3 -3
- package/demo/vsn.js +2 -2
- package/dist/AST/ClassNode.js +6 -3
- package/dist/AST/ClassNode.js.map +1 -1
- package/dist/AST/DispatchEventNode.d.ts +3 -1
- package/dist/AST/DispatchEventNode.js +48 -14
- package/dist/AST/DispatchEventNode.js.map +1 -1
- package/dist/AST/ElementQueryNode.d.ts +2 -1
- package/dist/AST/ElementQueryNode.js +5 -5
- package/dist/AST/ElementQueryNode.js.map +1 -1
- package/dist/AST/FunctionNode.d.ts +1 -1
- package/dist/AST/LoopNode.d.ts +12 -0
- package/dist/AST/LoopNode.js +121 -0
- package/dist/AST/LoopNode.js.map +1 -0
- package/dist/AST/OnNode.d.ts +1 -1
- package/dist/AST/OnNode.js.map +1 -1
- package/dist/AST/ScopeMemberNode.js +4 -0
- package/dist/AST/ScopeMemberNode.js.map +1 -1
- package/dist/AST.d.ts +64 -62
- package/dist/AST.js +82 -69
- package/dist/AST.js.map +1 -1
- package/dist/Component.d.ts +2 -0
- package/dist/Component.js +12 -27
- package/dist/Component.js.map +1 -1
- package/dist/DOM/AbstractDOM.d.ts +48 -0
- package/dist/DOM/AbstractDOM.js +937 -0
- package/dist/DOM/AbstractDOM.js.map +1 -0
- package/dist/DOM/ShadowDOM.d.ts +5 -0
- package/dist/DOM/ShadowDOM.js +32 -0
- package/dist/DOM/ShadowDOM.js.map +1 -0
- package/dist/DOM.d.ts +3 -48
- package/dist/DOM.js +6 -913
- package/dist/DOM.js.map +1 -1
- package/dist/Scope/ObjectAccessor.d.ts +7 -0
- package/dist/Scope/ObjectAccessor.js +40 -0
- package/dist/Scope/ObjectAccessor.js.map +1 -0
- package/dist/Scope/ScopeAbstract.d.ts +9 -0
- package/dist/Scope/ScopeAbstract.js +28 -0
- package/dist/Scope/ScopeAbstract.js.map +1 -0
- package/dist/Scope.d.ts +3 -2
- package/dist/Scope.js +37 -10
- package/dist/Scope.js.map +1 -1
- package/dist/Tag.d.ts +2 -0
- package/dist/Tag.js +52 -9
- package/dist/Tag.js.map +1 -1
- package/dist/helpers/VisionHelper.d.ts +1 -0
- package/dist/helpers/VisionHelper.js +45 -0
- package/dist/helpers/VisionHelper.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/vsn.js +3 -9
- package/dist/vsn.js.map +1 -1
- package/package.json +1 -1
- package/src/AST/ClassNode.ts +6 -3
- package/src/AST/DispatchEventNode.ts +19 -11
- package/src/AST/ElementQueryNode.ts +2 -1
- package/src/AST/FunctionNode.ts +1 -1
- package/src/AST/LoopNode.ts +35 -0
- package/src/AST/OnNode.ts +2 -2
- package/src/AST/ScopeMemberNode.ts +4 -0
- package/src/AST.ts +19 -7
- package/src/Component.ts +15 -13
- package/src/DOM/AbstractDOM.ts +389 -0
- package/src/DOM/ShadowDOM.ts +15 -0
- package/src/DOM.ts +4 -385
- package/src/Scope/ObjectAccessor.ts +21 -0
- package/src/Scope/ScopeAbstract.ts +11 -0
- package/src/Scope.ts +14 -1
- package/src/Tag.ts +25 -0
- package/src/helpers/VisionHelper.ts +6 -0
- package/src/version.ts +1 -1
- package/src/vsn.ts +3 -7
- package/test/AST/ClassNode.spec.ts +26 -4
- package/test/AST.spec.ts +21 -21
- package/test/Controller.spec.ts +1 -1
- package/test/DOM.spec.ts +3 -3
- package/test/Tag/TagList.spec.ts +1 -1
- package/test/attributes/Bind.spec.ts +12 -12
- package/test/attributes/JSONAttribute.spec.ts +5 -5
- package/test/attributes/ListItem.spec.ts +6 -6
- package/test/attributes/ScopeAttribute.spec.ts +2 -2
- package/test/attributes/ServiceAttribute.spec.ts +1 -1
- package/test/attributes/SetAttribute.spec.ts +3 -3
- package/test/attributes/Styles.spec.ts +2 -2
package/src/DOM.ts
CHANGED
|
@@ -1,394 +1,13 @@
|
|
|
1
|
-
import {Tag} from "./Tag";
|
|
2
|
-
import {ElementHelper} from "./helpers/ElementHelper";
|
|
3
|
-
import {Configuration} from "./Configuration";
|
|
4
|
-
import {Tree} from "./AST";
|
|
5
|
-
import {TagList} from "./Tag/TagList";
|
|
6
|
-
import {WrappedWindow} from "./DOM/WrappedWindow";
|
|
7
|
-
import {WrappedDocument} from "./DOM/WrappedDocument";
|
|
8
|
-
import {Scope} from "./Scope";
|
|
9
|
-
import {EventDispatcher} from "./EventDispatcher";
|
|
10
|
-
import {ClassNode} from "./AST/ClassNode";
|
|
11
|
-
import {Registry} from "./Registry";
|
|
12
|
-
import {SlotTag} from "./Tag/SlotTag";
|
|
13
|
-
import {SlottedTag} from "./Tag/SlottedTag";
|
|
14
1
|
|
|
15
|
-
|
|
16
|
-
ALL,
|
|
17
|
-
UP,
|
|
18
|
-
DOWN
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export class DOM extends EventDispatcher {
|
|
22
|
-
protected static _instance: DOM;
|
|
23
|
-
protected _root: Tag;
|
|
24
|
-
protected _ready: Promise<boolean>;
|
|
25
|
-
protected tags: Tag[];
|
|
26
|
-
protected observer: MutationObserver;
|
|
27
|
-
protected evaluateTimeout: any;
|
|
28
|
-
protected queued: HTMLElement[] = [];
|
|
29
|
-
protected window: WrappedWindow;
|
|
30
|
-
protected document: WrappedDocument;
|
|
31
|
-
protected _built: boolean = false;
|
|
32
|
-
|
|
33
|
-
constructor(
|
|
34
|
-
protected rootElement: Document,
|
|
35
|
-
build: boolean = true,
|
|
36
|
-
protected debug: boolean = false
|
|
37
|
-
) {
|
|
38
|
-
super();
|
|
39
|
-
this._ready = new Promise((resolve) => {
|
|
40
|
-
this.once('built', () => {
|
|
41
|
-
resolve(true);
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
this.observer = new MutationObserver(this.mutation.bind(this));
|
|
46
|
-
this.tags = [];
|
|
47
|
-
|
|
48
|
-
this.window = new WrappedWindow(window);
|
|
49
|
-
this.document = new WrappedDocument(window.document);
|
|
50
|
-
|
|
51
|
-
if (build) {
|
|
52
|
-
this.buildFrom(rootElement, true);
|
|
53
|
-
}
|
|
54
|
-
this.evaluate();
|
|
55
|
-
Configuration.instance.on('change', this.evaluate.bind(this));
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
public get built(): boolean {
|
|
59
|
-
return this._built;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
public get root(): Tag {
|
|
63
|
-
return this._root;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
public get ready(): Promise<boolean> {
|
|
67
|
-
return this.promise('builtRoot');
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
public async get(selector: string, create: boolean = false, tag: Tag = null, direction: EQuerySelectDirection = EQuerySelectDirection.DOWN): Promise<TagList> {
|
|
71
|
-
switch (selector) {
|
|
72
|
-
case 'window':
|
|
73
|
-
return new TagList(this.window);
|
|
74
|
-
case 'document':
|
|
75
|
-
return new TagList(this.document);
|
|
76
|
-
case 'body':
|
|
77
|
-
return new TagList(this.root);
|
|
78
|
-
default:
|
|
79
|
-
let nodes;
|
|
80
|
-
if (direction === EQuerySelectDirection.DOWN) {
|
|
81
|
-
nodes = this.querySelectorAll(selector, tag);
|
|
82
|
-
} else if (direction === EQuerySelectDirection.UP) {
|
|
83
|
-
nodes = [this.querySelectorClosest(selector, tag)];
|
|
84
|
-
} else {
|
|
85
|
-
nodes = this.querySelectorAll(selector);
|
|
86
|
-
}
|
|
87
|
-
return await this.getTagsForElements(Array.from(nodes) as Element[], create);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
public async getFromTag(tag: Tag, selector: string, create: boolean = false) {
|
|
92
|
-
const nodes = this.querySelectorElement(tag.element, selector);
|
|
93
|
-
return await this.getTagsForElements(Array.from(nodes) as Element[], create);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
public registerElementInRoot(tag: Tag): void {
|
|
97
|
-
const id: string = tag.element.getAttribute('id');
|
|
98
|
-
if (!!id)
|
|
99
|
-
this.root.scope.set(`#${id}`, tag.scope);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
public querySelectorClosest(q: string, tag: Tag = null): HTMLElement {
|
|
103
|
-
return tag.element.closest(q);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
public querySelectPath(path: string[], element: HTMLElement = null): HTMLElement[] {
|
|
107
|
-
const current = path.shift();
|
|
108
|
-
if (!current) return [];
|
|
109
|
-
const elements = Array.from(element ? this.querySelectorElement(element, current) : this.querySelectorAll(current));
|
|
110
|
-
if (path.length > 0) {
|
|
111
|
-
const result = [];
|
|
112
|
-
for (const _element of elements) {
|
|
113
|
-
result.push(...this.querySelectPath([...path], _element as HTMLElement));
|
|
114
|
-
}
|
|
115
|
-
return result;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
return elements as HTMLElement[];
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
public querySelectorAll(q: string, tag: Tag = null): NodeList | HTMLElement[] {
|
|
122
|
-
const element: HTMLElement | Document = tag && !q.startsWith('#') ? tag.element : this.rootElement;
|
|
123
|
-
return this.querySelectorElement(element, q);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
public querySelectorElement(element: HTMLElement | Document, q: string): NodeList | HTMLElement[] {
|
|
127
|
-
const parentIndex: number = q.indexOf(':parent');
|
|
128
|
-
if (parentIndex > -1) {
|
|
129
|
-
const _q = q.substring(0, parentIndex);
|
|
130
|
-
const rest = q.substring(parentIndex + 7);
|
|
131
|
-
if (_q.length > 0) {
|
|
132
|
-
const nodeList = [];
|
|
133
|
-
for (const _element of Array.from(this.querySelectorElement(element, _q))) {
|
|
134
|
-
if (rest.length > 0) {
|
|
135
|
-
nodeList.push(...Array.from(this.querySelectorElement(DOM.getParentElement(_element as HTMLElement), rest)));
|
|
136
|
-
} else {
|
|
137
|
-
nodeList.push(DOM.getParentElement(_element as HTMLElement));
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
return nodeList;
|
|
141
|
-
} else if (rest.length === 0) {
|
|
142
|
-
return [DOM.getParentElement(element as HTMLElement)];
|
|
143
|
-
} else {
|
|
144
|
-
return this.querySelectorElement(DOM.getParentElement(element as HTMLElement), rest);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
let matches = element.querySelectorAll(q);
|
|
148
|
-
|
|
149
|
-
if (matches.length === 0 && (element as HTMLElement).shadowRoot) {
|
|
150
|
-
matches = (element as HTMLElement).shadowRoot.querySelectorAll(q);
|
|
151
|
-
}
|
|
152
|
-
return matches;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
public querySelector(q: string): Element {
|
|
156
|
-
return this.rootElement.querySelector(q);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
public async exec(code: string) {
|
|
160
|
-
const tree = new Tree(code);
|
|
161
|
-
await tree.prepare(this.root.scope, this);
|
|
162
|
-
return await tree.evaluate(this.root.scope, this);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
public async evaluate() {
|
|
166
|
-
clearTimeout(this.evaluateTimeout);
|
|
167
|
-
for (const tag of this.tags) {
|
|
168
|
-
await tag.evaluate();
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
public async mutation(mutations: MutationRecord[]) {
|
|
173
|
-
for (const mutation of mutations) {
|
|
174
|
-
let tag: Tag = await this.getTagForElement(mutation.target as HTMLElement);
|
|
175
|
-
if (tag) {
|
|
176
|
-
tag.mutate(mutation);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// Check for class changes
|
|
180
|
-
if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
|
|
181
|
-
await ClassNode.checkForClassChanges(mutation.target as HTMLElement, this, tag);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
for (const ele of Array.from(mutation.removedNodes)) {
|
|
185
|
-
const toRemove: Tag = await this.getTagForElement(ele as HTMLElement);
|
|
186
|
-
if (toRemove) {
|
|
187
|
-
toRemove.deconstruct();
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
async discover(ele: HTMLElement, forComponent: boolean = false): Promise<HTMLElement[]> {
|
|
194
|
-
const discovered: HTMLElement[] = [];
|
|
195
|
-
const checkElement = (e: HTMLElement): boolean => {
|
|
196
|
-
if (Registry.instance.tags.has(e?.tagName?.toLowerCase())) {
|
|
197
|
-
return false;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
if (ElementHelper.hasVisionAttribute(e)) {
|
|
201
|
-
if (
|
|
202
|
-
(!forComponent && e.hasAttribute('slot'))
|
|
203
|
-
) return false;
|
|
204
|
-
if (this.queued.indexOf(e) > -1) return false;
|
|
205
|
-
this.queued.push(e);
|
|
206
|
-
discovered.push(e);
|
|
207
|
-
}
|
|
2
|
+
import {AbstractDOM} from "./DOM/AbstractDOM";
|
|
208
3
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
const scanChildren = (e: HTMLElement) => {
|
|
212
|
-
for (const element of Array.from(e.children) as HTMLElement[]) {
|
|
213
|
-
if (!checkElement(element)) continue;
|
|
214
|
-
if (element.tagName.toLowerCase() !== 'template')
|
|
215
|
-
scanChildren(element);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
checkElement(ele);
|
|
219
|
-
scanChildren(ele);
|
|
220
|
-
|
|
221
|
-
return discovered;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
async buildTag<T = Tag>(element: HTMLElement, returnExisting: boolean = false, cls: any = Tag): Promise<T> {
|
|
225
|
-
if (element[Tag.TaggedVariable]) return returnExisting ? element[Tag.TaggedVariable] : null;
|
|
226
|
-
if (element.tagName.toLowerCase() === 'slot')
|
|
227
|
-
cls = SlotTag;
|
|
228
|
-
else if (element.hasAttribute('slot'))
|
|
229
|
-
cls = SlottedTag;
|
|
230
|
-
|
|
231
|
-
const tag: T = new cls(element, this);
|
|
232
|
-
this.tags.push(tag as any);
|
|
233
|
-
return tag;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
async setupTags(tags: Tag[]) {
|
|
237
|
-
// Configure, setup & execute attributes
|
|
238
|
-
for (const tag of tags)
|
|
239
|
-
await tag.buildAttributes();
|
|
240
|
-
|
|
241
|
-
for (const tag of tags)
|
|
242
|
-
await tag.compileAttributes();
|
|
243
|
-
|
|
244
|
-
for (const tag of tags)
|
|
245
|
-
await tag.setupAttributes();
|
|
246
|
-
|
|
247
|
-
for (const tag of tags)
|
|
248
|
-
await tag.extractAttributes();
|
|
249
|
-
|
|
250
|
-
for (const tag of tags)
|
|
251
|
-
await tag.connectAttributes();
|
|
252
|
-
|
|
253
|
-
for (const tag of tags) {
|
|
254
|
-
await tag.finalize();
|
|
255
|
-
this.queued.splice(this.queued.indexOf(tag.element), 1);
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
for (const tag of tags) {
|
|
259
|
-
this.observer.observe(tag.element, {
|
|
260
|
-
attributes: true,
|
|
261
|
-
characterData: true,
|
|
262
|
-
childList: true,
|
|
263
|
-
subtree: true
|
|
264
|
-
});
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
async buildFrom(ele: any, isRoot: boolean = false, forComponent: boolean = false): Promise<Tag[]> {
|
|
269
|
-
if (isRoot) {
|
|
270
|
-
document.body.setAttribute('vsn-root', '');
|
|
271
|
-
document.ondragover = (e) => e.cancelable && e.preventDefault(); // Allow dragging over document
|
|
272
|
-
this._root = await this.buildTag(document.body, true);
|
|
273
|
-
this._root.createScope(true);
|
|
274
|
-
await this.setupTags([this._root]);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
// Setup components first
|
|
278
|
-
const templateNodes = this.querySelectorElement(ele, 'template');
|
|
279
|
-
const components: Tag[] = [];
|
|
280
|
-
for (const n of Array.from(templateNodes) as HTMLElement[]) {
|
|
281
|
-
if (!ElementHelper.hasVisionAttribute(n))
|
|
282
|
-
continue;
|
|
283
|
-
|
|
284
|
-
const tag = await this.buildTag(n);
|
|
285
|
-
if (tag)
|
|
286
|
-
components.push(tag);
|
|
287
|
-
}
|
|
288
|
-
if (components.length)
|
|
289
|
-
await this.setupTags(components);
|
|
290
|
-
|
|
291
|
-
// Create tags for each html element with a vsn-attribute
|
|
292
|
-
const newTags: Tag[] = [];
|
|
293
|
-
const toBuild: HTMLElement[] = await this.discover(ele, forComponent);
|
|
294
|
-
|
|
295
|
-
for (const element of toBuild) {
|
|
296
|
-
const tag = await this.buildTag(element);
|
|
297
|
-
if (tag)
|
|
298
|
-
newTags.push(tag);
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
await this.setupTags(newTags);
|
|
302
|
-
|
|
303
|
-
if (isRoot) {
|
|
304
|
-
this._built = true;
|
|
305
|
-
this.dispatch('builtRoot')
|
|
306
|
-
}
|
|
307
|
-
this.dispatch('built', newTags);
|
|
308
|
-
return newTags;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
async getTagsForElements(elements: Element[], create: boolean = false) {
|
|
312
|
-
const tags: TagList = new TagList();
|
|
313
|
-
const found: Element[] = [];
|
|
314
|
-
|
|
315
|
-
for (const element of elements) {
|
|
316
|
-
if (element && element[Tag.TaggedVariable]) {
|
|
317
|
-
tags.push(element[Tag.TaggedVariable]);
|
|
318
|
-
found.push(element);
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
if (create) {
|
|
323
|
-
const notFound: Element[] = [...elements];
|
|
324
|
-
for (let i = notFound.length; i >= 0; i--) {
|
|
325
|
-
const element: Element = notFound[i];
|
|
326
|
-
if (found.indexOf(element) > -1) {
|
|
327
|
-
notFound.splice(i, 1);
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
for (const element of notFound) {
|
|
332
|
-
tags.push(await this.getTagForElement(element, create));
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
return tags;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
async getTagForElement(element: Element, create: boolean = false, forComponent: boolean = false) {
|
|
340
|
-
if (element && element[Tag.TaggedVariable])
|
|
341
|
-
return element[Tag.TaggedVariable];
|
|
342
|
-
|
|
343
|
-
if (element && create) {
|
|
344
|
-
if (element instanceof HTMLElement)
|
|
345
|
-
element.setAttribute('vsn-ref', '');
|
|
346
|
-
await this.buildFrom(element.parentElement || element, false, forComponent);
|
|
347
|
-
return await this.getTagForElement(element, false);
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
return null;
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
async getTagForScope(scope: Scope) {
|
|
354
|
-
for (const tag of this.tags) {
|
|
355
|
-
if (tag.uniqueScope && tag.scope === scope)
|
|
356
|
-
return tag;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
return null;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
public async resetBranch(e: Tag | HTMLElement) {
|
|
363
|
-
if (e instanceof Tag)
|
|
364
|
-
e = e.element;
|
|
365
|
-
|
|
366
|
-
const tag: Tag = e[Tag.TaggedVariable];
|
|
367
|
-
if (tag) {
|
|
368
|
-
tag.findParentTag();
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
const children = Array.from(e.children) as HTMLElement[]
|
|
372
|
-
for (const t of children) {
|
|
373
|
-
await this.resetBranch(t);
|
|
374
|
-
}
|
|
375
|
-
}
|
|
4
|
+
export class DOM extends AbstractDOM {
|
|
5
|
+
protected static _instance: AbstractDOM;
|
|
376
6
|
|
|
377
7
|
public static get instance(): DOM {
|
|
378
8
|
if (!DOM._instance)
|
|
379
|
-
DOM._instance = new DOM(document ,false, false);
|
|
9
|
+
DOM._instance = new DOM(document.body ,false, false);
|
|
380
10
|
|
|
381
11
|
return DOM._instance;
|
|
382
12
|
}
|
|
383
|
-
|
|
384
|
-
public static getParentElement(element: HTMLElement): HTMLElement {
|
|
385
|
-
if (element.parentElement) {
|
|
386
|
-
return element.parentElement as HTMLElement;
|
|
387
|
-
} else if (element.assignedSlot) {
|
|
388
|
-
return element.assignedSlot.parentElement as HTMLElement;
|
|
389
|
-
} else if (element['shadowParent']) {
|
|
390
|
-
return element['shadowParent'];
|
|
391
|
-
}
|
|
392
|
-
return null;
|
|
393
|
-
}
|
|
394
13
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {ScopeAbstract} from "./ScopeAbstract";
|
|
2
|
+
|
|
3
|
+
export class ObjectAccessor extends ScopeAbstract {
|
|
4
|
+
public readonly data: any;
|
|
5
|
+
|
|
6
|
+
constructor(data: any) {
|
|
7
|
+
super();
|
|
8
|
+
this.data = data;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
public get(field: string, fallback = null): any {
|
|
12
|
+
const value = this.data[field];
|
|
13
|
+
if (typeof value === 'object')
|
|
14
|
+
return new ObjectAccessor(value);
|
|
15
|
+
return value === undefined ? fallback : value;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
public set(field: string, value: any): void {
|
|
19
|
+
this.data[field] = value;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import {EventDispatcher} from "../EventDispatcher";
|
|
2
|
+
|
|
3
|
+
export interface IScope {
|
|
4
|
+
get(field: string, fallback?: any): any;
|
|
5
|
+
set(field: string, value: any): void;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export abstract class ScopeAbstract extends EventDispatcher implements IScope {
|
|
9
|
+
public abstract get(field: string, fallback?: any): any;
|
|
10
|
+
public abstract set(field: string, value: any): void;
|
|
11
|
+
}
|
package/src/Scope.ts
CHANGED
|
@@ -5,9 +5,10 @@ import {WrappedArray} from "./Scope/WrappedArray";
|
|
|
5
5
|
import {ScopeData} from "./Scope/ScopeData";
|
|
6
6
|
import {DynamicScopeData} from "./Scope/DynamicScopeData";
|
|
7
7
|
import {DOM} from "./DOM";
|
|
8
|
+
import {ScopeAbstract} from "./Scope/ScopeAbstract";
|
|
8
9
|
|
|
9
10
|
|
|
10
|
-
export class Scope extends
|
|
11
|
+
export class Scope extends ScopeAbstract {
|
|
11
12
|
public wrapped: any;
|
|
12
13
|
protected _data: ScopeData;
|
|
13
14
|
protected children: Scope[];
|
|
@@ -29,6 +30,18 @@ export class Scope extends EventDispatcher {
|
|
|
29
30
|
return this._data;
|
|
30
31
|
}
|
|
31
32
|
|
|
33
|
+
public get objectify(): any {
|
|
34
|
+
const obj = {};
|
|
35
|
+
for (const key of this.keys) {
|
|
36
|
+
const value = this.get(key);
|
|
37
|
+
if (value instanceof Scope)
|
|
38
|
+
obj[key] = value.objectify;
|
|
39
|
+
else
|
|
40
|
+
obj[key] = value;
|
|
41
|
+
}
|
|
42
|
+
return obj;
|
|
43
|
+
}
|
|
44
|
+
|
|
32
45
|
public get parentScope(): Scope {
|
|
33
46
|
if (this._parentScope)
|
|
34
47
|
return this._parentScope;
|
package/src/Tag.ts
CHANGED
|
@@ -656,6 +656,22 @@ export class Tag extends DOMObject {
|
|
|
656
656
|
return attribute.replace(`|${modifier}`, '');
|
|
657
657
|
}
|
|
658
658
|
|
|
659
|
+
public getElementPath(element: HTMLElement): string {
|
|
660
|
+
const path = [];
|
|
661
|
+
let currentElement = element;
|
|
662
|
+
while (currentElement) {
|
|
663
|
+
let tag = currentElement.tagName
|
|
664
|
+
if (currentElement.hasAttribute('id'))
|
|
665
|
+
tag += `#${currentElement.getAttribute('id')}`;
|
|
666
|
+
else if (currentElement.hasAttribute('class'))
|
|
667
|
+
tag += `.${currentElement.getAttribute('class').split(' ').join('.')}`;
|
|
668
|
+
|
|
669
|
+
path.push(tag);
|
|
670
|
+
currentElement = currentElement.parentElement;
|
|
671
|
+
}
|
|
672
|
+
return path.reverse().join('>');
|
|
673
|
+
}
|
|
674
|
+
|
|
659
675
|
public addEventHandler(eventType: string, modifiers: Modifiers, handler, context: any = null) {
|
|
660
676
|
let passiveValue: boolean = null;
|
|
661
677
|
modifiers = modifiers || new Modifiers();
|
|
@@ -698,6 +714,14 @@ export class Tag extends DOMObject {
|
|
|
698
714
|
}
|
|
699
715
|
}
|
|
700
716
|
|
|
717
|
+
public removeAllEventHandlers() {
|
|
718
|
+
for (const eventType of Object.keys(this.onEventHandlers)) {
|
|
719
|
+
for (const handler of this.onEventHandlers[eventType]) {
|
|
720
|
+
this.removeEventHandler(eventType, handler.handler, handler.context);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
701
725
|
public removeContextEventHandlers(context: any) {
|
|
702
726
|
for (const eventType of Object.keys(this.onEventHandlers)) {
|
|
703
727
|
for (const handler of this.onEventHandlers[eventType]) {
|
|
@@ -763,6 +787,7 @@ export class Tag extends DOMObject {
|
|
|
763
787
|
}
|
|
764
788
|
|
|
765
789
|
deconstruct() {
|
|
790
|
+
this.removeAllEventHandlers();
|
|
766
791
|
this.attributes.forEach(attr => attr.deconstruct());
|
|
767
792
|
this.attributes.clear();
|
|
768
793
|
this._children.forEach(child => child.deconstruct());
|
|
@@ -59,6 +59,12 @@ export class VisionHelper {
|
|
|
59
59
|
return _url.toString();
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
public static async wait (seconds) {
|
|
63
|
+
return new Promise((resolve) => {
|
|
64
|
+
setTimeout(resolve, seconds * 1000);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
62
68
|
public static nice(callback, timeout: number = 100) {
|
|
63
69
|
if (VisionHelper.window && window['requestIdleCallback']) {
|
|
64
70
|
window['requestIdleCallback'](callback);
|
package/src/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const VERSION = '0.1.
|
|
1
|
+
export const VERSION = '0.1.126';
|
|
2
2
|
|
package/src/vsn.ts
CHANGED
|
@@ -4,7 +4,6 @@ import {Registry} from "./Registry";
|
|
|
4
4
|
import {Configuration} from "./Configuration";
|
|
5
5
|
import {VisionHelper} from "./helpers/VisionHelper";
|
|
6
6
|
import {Tree} from "./AST";
|
|
7
|
-
import {Query} from "./Query";
|
|
8
7
|
import {EventDispatcher} from "./EventDispatcher";
|
|
9
8
|
import {DynamicScopeData} from "./Scope/DynamicScopeData";
|
|
10
9
|
import {Controller} from "./Controller";
|
|
@@ -19,6 +18,7 @@ export class Vision extends EventDispatcher {
|
|
|
19
18
|
|
|
20
19
|
constructor() {
|
|
21
20
|
super();
|
|
21
|
+
document.ondragover = (e) => e.cancelable && e.preventDefault(); // Allow dragging over document
|
|
22
22
|
Registry.instance.tags.on('register', this.defineComponent, this);
|
|
23
23
|
if (VisionHelper.document) {
|
|
24
24
|
document.addEventListener(
|
|
@@ -32,11 +32,7 @@ export class Vision extends EventDispatcher {
|
|
|
32
32
|
this.registry.functions.register('warn', console.warn);
|
|
33
33
|
this.registry.functions.register('error', console.error);
|
|
34
34
|
this.registry.functions.register('info', console.info);
|
|
35
|
-
this.registry.functions.register('wait',
|
|
36
|
-
return new Promise((resolve) => {
|
|
37
|
-
setTimeout(resolve, seconds * 1000);
|
|
38
|
-
});
|
|
39
|
-
});
|
|
35
|
+
this.registry.functions.register('wait', VisionHelper.wait);
|
|
40
36
|
this.registry.models.register('Object', Object);
|
|
41
37
|
this.registry.controllers.register('WrappedArray', WrappedArray);
|
|
42
38
|
this.registry.controllers.register('Data', DynamicScopeData);
|
|
@@ -46,7 +42,7 @@ export class Vision extends EventDispatcher {
|
|
|
46
42
|
window['Registry'] = Registry;
|
|
47
43
|
window['vision'] = window['vsn'] = this;
|
|
48
44
|
window['Tree'] = Tree;
|
|
49
|
-
window['$'] =
|
|
45
|
+
window['$'] = this.exec.bind(this);
|
|
50
46
|
VisionHelper.window.dispatchEvent(new Event('vsn'));
|
|
51
47
|
}
|
|
52
48
|
}
|
|
@@ -2,6 +2,7 @@ import {DOM} from "../../src/DOM";
|
|
|
2
2
|
import {ClassNode} from "../../src/AST/ClassNode";
|
|
3
3
|
import {Registry} from "../../src/Registry";
|
|
4
4
|
import {TagList} from "../../src/Tag/TagList";
|
|
5
|
+
import {Tag} from "../../src/vsn";
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
describe('ClassNode', () => {
|
|
@@ -16,8 +17,9 @@ describe('ClassNode', () => {
|
|
|
16
17
|
}
|
|
17
18
|
</script>
|
|
18
19
|
<div class="simple"><input /></div>
|
|
20
|
+
<input id="not-nested" />
|
|
19
21
|
`;
|
|
20
|
-
const dom = new DOM(document);
|
|
22
|
+
const dom = new DOM(document.body);
|
|
21
23
|
await dom.ready;
|
|
22
24
|
await Registry.instance.classes.get('.simple input');
|
|
23
25
|
expect(ClassNode.classParents['input']).toBeInstanceOf(Array);
|
|
@@ -25,6 +27,26 @@ describe('ClassNode', () => {
|
|
|
25
27
|
expect(ClassNode.classes['.simple input']).toBeInstanceOf(ClassNode);
|
|
26
28
|
});
|
|
27
29
|
|
|
30
|
+
it("properly build classes on newly added elements in the dom", async () => {
|
|
31
|
+
document.body.innerHTML = `
|
|
32
|
+
<script type="text/vsn" vsn-script>
|
|
33
|
+
class .added-html {
|
|
34
|
+
func construct() {}
|
|
35
|
+
}
|
|
36
|
+
</script>
|
|
37
|
+
<div id="container"></div>
|
|
38
|
+
`;
|
|
39
|
+
const dom = new DOM(document.body);
|
|
40
|
+
await dom.ready;
|
|
41
|
+
await Registry.instance.classes.get('.added-html');
|
|
42
|
+
const newHtml: string = `<div class="added-html" id="added"></div>`;
|
|
43
|
+
dom.root.scope.set('newHtml', newHtml);
|
|
44
|
+
await dom.exec('#container.@html = newHtml');
|
|
45
|
+
const addedTag = await dom.exec('#added');
|
|
46
|
+
expect(addedTag).toBeInstanceOf(Tag);
|
|
47
|
+
expect(ClassNode.preppedTags['.added-html'].indexOf(addedTag)).toBeGreaterThan(-1);
|
|
48
|
+
});
|
|
49
|
+
|
|
28
50
|
it("properly define a simple class", async () => {
|
|
29
51
|
document.body.innerHTML = `
|
|
30
52
|
<script type="text/vsn" vsn-script>
|
|
@@ -43,7 +65,7 @@ describe('ClassNode', () => {
|
|
|
43
65
|
<div class="simple-construct" id="ele-1"></div>
|
|
44
66
|
<div class="simple-construct" id="ele-2"></div>
|
|
45
67
|
`;
|
|
46
|
-
const dom = new DOM(document);
|
|
68
|
+
const dom = new DOM(document.body);
|
|
47
69
|
await dom.ready;
|
|
48
70
|
await Registry.instance.classes.get('.simple-construct');
|
|
49
71
|
const t: TagList = await dom.exec('?(.simple-construct)');
|
|
@@ -72,7 +94,7 @@ describe('ClassNode', () => {
|
|
|
72
94
|
</script>
|
|
73
95
|
<div class="testing"><span class="test"></span></div>
|
|
74
96
|
`;
|
|
75
|
-
const dom = new DOM(document);
|
|
97
|
+
const dom = new DOM(document.body);
|
|
76
98
|
await dom.ready;
|
|
77
99
|
await Registry.instance.classes.get('.testing .test');
|
|
78
100
|
const t = await dom.exec('?(.testing .test)[0]');
|
|
@@ -147,7 +169,7 @@ class .option {
|
|
|
147
169
|
</ul>
|
|
148
170
|
</div>
|
|
149
171
|
`;
|
|
150
|
-
const dom = new DOM(document);
|
|
172
|
+
const dom = new DOM(document.body);
|
|
151
173
|
await dom.ready;
|
|
152
174
|
});
|
|
153
175
|
});
|