@pacem/pacem-core 1.0.6-ricci → 2.0.0-abel

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 (40) hide show
  1. package/dist/browser/pacem-core.js +289 -161
  2. package/dist/browser/pacem-core.js.map +1 -1
  3. package/dist/browser/pacem-core.min.js +2 -2
  4. package/dist/bundle/pacem-core.min.mjs +6 -6
  5. package/dist/bundle/pacem-core.mjs +276 -104
  6. package/dist/bundle/pacem-core.mjs.map +4 -4
  7. package/dist/docs/pacem-core.json +1 -1
  8. package/dist/esm/adapter.js +1 -1
  9. package/dist/esm/binding-parser.js +259 -0
  10. package/dist/esm/command.js +1 -1
  11. package/dist/esm/comparers.js +1 -1
  12. package/dist/esm/converters.js +1 -1
  13. package/dist/esm/decorators-next.js +1 -1
  14. package/dist/esm/decorators.js +1 -1
  15. package/dist/esm/defaults.js +1 -1
  16. package/dist/esm/events.js +1 -1
  17. package/dist/esm/eventtarget.js +1 -1
  18. package/dist/esm/expression.js +36 -161
  19. package/dist/esm/index-animations.js +1 -1
  20. package/dist/esm/index-behaviors.js +1 -1
  21. package/dist/esm/index-components.js +1 -1
  22. package/dist/esm/index-iife.js +1 -1
  23. package/dist/esm/index-logging.js +1 -1
  24. package/dist/esm/index-net.js +1 -1
  25. package/dist/esm/index-root.js +1 -1
  26. package/dist/esm/index-ui.js +1 -1
  27. package/dist/esm/index.js +1 -1
  28. package/dist/esm/logger.js +1 -1
  29. package/dist/esm/prefix.js +1 -1
  30. package/dist/esm/promise.js +1 -1
  31. package/dist/esm/regex.js +1 -1
  32. package/dist/esm/storage.js +1 -1
  33. package/dist/esm/template.js +1 -1
  34. package/dist/esm/transforms.js +1 -1
  35. package/dist/esm/types.js +1 -1
  36. package/dist/esm/utils-customelement.js +1 -1
  37. package/dist/esm/utils-customevent.js +1 -1
  38. package/dist/esm/utils.js +1 -1
  39. package/package.json +1 -1
  40. package/typings/index.d.ts +6 -2
@@ -24556,7 +24556,7 @@
24556
24556
  {
24557
24557
  "kind": "Method",
24558
24558
  "canonicalReference": "@pacem/pacem-core!Expression.parse:member(1)",
24559
- "docComment": "/**\n * Parses a binding expression's source text (the bare content of a `{{ ... }}` attribute, without the delimiters - see {@link CustomElementUtils.extractBindingAttributeExpression}) into an {@link Expression}, resolving its special tokens against `element`'s scope (see {@link CustomElementUtils.findScopeContext}). Throws if the expression matches the unsafe-code guard (`new`, `window.`, `document.`, `eval(`).\n *\n * @param expression - Bare expression source text.\n *\n * @param element - Element the expression is bound to; provides the scope special tokens (`:host.`, `#id`, `^item`, ...) resolve against.\n *\n * @returns A constant expression if it has no dependencies; a shared pending placeholder if `element` isn't attached to a scope yet; otherwise a fully resolved expression.\n */\n",
24559
+ "docComment": "/**\n * Parses a binding expression's source text (the bare content of a `{{ ... }}` attribute, without the delimiters - see {@link CustomElementUtils.extractBindingAttributeExpression}) into an {@link Expression}, resolving its special tokens against `element`'s scope (see {@link CustomElementUtils.findScopeContext}). Tokenization and rewriting are delegated to {@link BindingParser} (cached per expression text). Throws if the expression uses an unsafe construct (`new`, members of `window`/`document`, `eval(`).\n *\n * @param expression - Bare expression source text.\n *\n * @param element - Element the expression is bound to; provides the scope special tokens (`:host.`, `#id`, `^item`, ...) resolve against.\n *\n * @returns A constant expression if it has no special tokens; a shared pending placeholder if `element` isn't attached to a scope yet; otherwise a fully resolved expression.\n */\n",
24560
24560
  "excerptTokens": [
24561
24561
  {
24562
24562
  "kind": "Content",
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @pacem/pacem-core v1.0.6-ricci (https://js.pacem.it)
2
+ * @pacem/pacem-core v2.0.0-abel (https://js.pacem.it)
3
3
  * Pacem (https://pacem.it)
4
4
  * Licensed under Apache-2.0
5
5
  */
@@ -0,0 +1,259 @@
1
+ /*!
2
+ * @pacem/pacem-core v2.0.0-abel (https://js.pacem.it)
3
+ * Pacem (https://pacem.it)
4
+ * Licensed under Apache-2.0
5
+ */
6
+ import { Compile } from '@pacem/pacem-foundation';
7
+ //namespace Pacem {
8
+ /** Name of the argument object binding expressions are compiled against (see {@link Expression}). */
9
+ export const BINDING_CONTEXT_PREFIX = '__context_pacem';
10
+ const HOST_REFERENCE = '__host_pacem';
11
+ /** The foundation vocabulary binding expressions are tokenized with. */
12
+ const BINDING_SYNTAX = 'pacem-binding';
13
+ /** Tokenizations are cached by expression text, up to this many entries. */
14
+ const MAX_CACHED_ANALYSES = 4096;
15
+ const INSIGNIFICANT = new Set(['space', 'newline', 'comment', 'doc-comment']);
16
+ const MEMBER_ACCESS = new Set(['.', '?.']);
17
+ /** Globals whose members are off limits. */
18
+ const UNSAFE_OWNERS = new Set(['window', 'document']);
19
+ function isReference(token) {
20
+ return token.type.startsWith('binding-');
21
+ }
22
+ function isMemberName(token) {
23
+ return token?.type === 'identifier' || token?.type === 'keyword';
24
+ }
25
+ /** Whether `raw` (and the significant token after it) call what precedes them: `(` or `?.(`. */
26
+ function isInvocation(raw, next) {
27
+ if (raw === '(') {
28
+ return true;
29
+ }
30
+ return raw === '?.' && next === '(';
31
+ }
32
+ /**
33
+ * Pure parser of Pacem binding expressions (the content of `{{ … }}` attributes and `on-*` handlers), built on the
34
+ * `pacem-binding` vocabulary of foundation's language-agnostic `Compile.Scanner`: Pacem references (`:host.`, `::name`,
35
+ * `^item`, `^index`, `#id`, `$this`) are recognized wherever the JavaScript grammar allows an expression, and never
36
+ * inside strings or template literal text.
37
+ */
38
+ export class BindingParser {
39
+ static { this._scanner = new Compile.Scanner(); }
40
+ static { this._cache = new Map(); }
41
+ /** Tokenizes `expression` (cached) and detects its references and unsafe constructs. */
42
+ static analyze(expression) {
43
+ const text = expression ?? '';
44
+ const cache = BindingParser._cache;
45
+ const cached = cache.get(text);
46
+ if (cached != null) {
47
+ return cached;
48
+ }
49
+ if (cache.size >= MAX_CACHED_ANALYSES) {
50
+ cache.clear();
51
+ }
52
+ const tokens = BindingParser._scanner.tokenize(text, BINDING_SYNTAX);
53
+ const significant = [];
54
+ tokens.forEach((t, i) => {
55
+ if (!INSIGNIFICANT.has(t.type)) {
56
+ significant.push(i);
57
+ }
58
+ });
59
+ const hasReferences = tokens.some(isReference);
60
+ const unsafe = BindingParser._findUnsafe(tokens, significant);
61
+ const analysis = { tokens, significant, hasReferences, unsafe };
62
+ cache.set(text, analysis);
63
+ return analysis;
64
+ }
65
+ /**
66
+ * Finds the first unsafe construct: `new`, members of `window`/`document`, `eval(…)`. Member names (`x.window`),
67
+ * object keys (`{ new: 1 }`) and string contents never count.
68
+ */
69
+ static _findUnsafe(tokens, significant) {
70
+ for (let k = 0; k < significant.length; k++) {
71
+ const token = tokens[significant[k]];
72
+ const previous = tokens[significant[k - 1]];
73
+ const next = tokens[significant[k + 1]];
74
+ if (MEMBER_ACCESS.has(previous?.raw)) {
75
+ continue;
76
+ }
77
+ // (`{ new: … }` is an object key, not an instantiation)
78
+ const instantiation = token.type === 'keyword' && token.raw === 'new' && next?.raw !== ':';
79
+ if (instantiation) {
80
+ return 'new';
81
+ }
82
+ if (token.type !== 'identifier') {
83
+ continue;
84
+ }
85
+ const accessed = next?.raw === '[' || MEMBER_ACCESS.has(next?.raw);
86
+ if (UNSAFE_OWNERS.has(token.raw) && accessed) {
87
+ return token.raw + next.raw;
88
+ }
89
+ if (token.raw === 'eval' && next?.raw === '(') {
90
+ return 'eval(';
91
+ }
92
+ }
93
+ return null;
94
+ }
95
+ /**
96
+ * Parses a binding expression against a scope.
97
+ * @param expression The bare expression text (no `{{ }}` delimiters, no `, twoway`/`, once` modifier).
98
+ * @param scope What the references resolve to.
99
+ * @throws When the expression is unsafe (see {@link analyze}).
100
+ */
101
+ static parse(expression, scope) {
102
+ const analysis = BindingParser.analyze(expression);
103
+ if (analysis.unsafe != null) {
104
+ throw `Unsafe expression detected: "${analysis.unsafe}" in "${expression}".`;
105
+ }
106
+ if (!analysis.hasReferences) {
107
+ return { kind: 'constant', functionBody: `return ${expression};`, voidBody: expression, dependencies: [] };
108
+ }
109
+ if (!scope.ready) {
110
+ return { kind: 'pending', dependencies: [] };
111
+ }
112
+ const rewriter = new BindingRewriter(analysis, scope);
113
+ return rewriter.run();
114
+ }
115
+ }
116
+ /** One rewrite pass: replaces the Pacem references with `__context_pacem` lookups, collecting args and dependencies. */
117
+ class BindingRewriter {
118
+ constructor(_analysis, _scope) {
119
+ this._analysis = _analysis;
120
+ this._scope = _scope;
121
+ this._dependencies = [];
122
+ this._args = { '$this': _scope.self };
123
+ }
124
+ run() {
125
+ const tokens = this._analysis.tokens;
126
+ let expression = '';
127
+ for (let i = 0; i < tokens.length; i++) {
128
+ const token = tokens[i];
129
+ expression += isReference(token) ? this._rewrite(token, i) : token.raw;
130
+ }
131
+ // some referenced element/item/host isn't available (yet)
132
+ for (let key in this._args) {
133
+ if (this._args[key] == null) {
134
+ return { kind: 'pending', dependencies: [] };
135
+ }
136
+ }
137
+ const dependencies = this._dependencies;
138
+ // a single dependency on a plain property path can be bound two-way
139
+ const trimmed = expression.trim();
140
+ if (dependencies.length === 1 && !/[^\w\.\$]+/.test(trimmed)) {
141
+ dependencies[0].twowayAllowed = true;
142
+ }
143
+ return {
144
+ kind: 'resolved',
145
+ functionBody: `try{ var ___$$$r = ${expression}; return ___$$$r; }catch(e){ }`,
146
+ voidBody: `try{ ${expression} }catch(e){ }`,
147
+ dependencies,
148
+ args: this._args
149
+ };
150
+ }
151
+ /**
152
+ * The member values read directly after the token at `index` (`.a.b`, `?.a`), e.g. `['a', 'b']`.
153
+ * As in javascript, `?.` is a member access like `.`; a member that gets invoked (`.map(`, `?.map(`, `.fn?.(`)
154
+ * is a method, not a value, so the chain stops before it. Spaces and comments in between are skipped, as in javascript.
155
+ */
156
+ _memberChain(index) {
157
+ const tokens = this._analysis.tokens;
158
+ const names = [];
159
+ let k = this._significantAfter(index);
160
+ while (MEMBER_ACCESS.has(this._significantRaw(k)) && isMemberName(tokens[this._significant(k + 1)])) {
161
+ const following = this._significantRaw(k + 2);
162
+ const afterFollowing = this._significantRaw(k + 3);
163
+ if (isInvocation(following, afterFollowing)) {
164
+ return names;
165
+ }
166
+ const name = tokens[this._significant(k + 1)].raw;
167
+ names.push(name);
168
+ k += 2;
169
+ }
170
+ return names;
171
+ }
172
+ /** The position, in the significant tokens, of the first one after the token at `index`. */
173
+ _significantAfter(index) {
174
+ const significant = this._analysis.significant;
175
+ let k = 0;
176
+ while (k < significant.length && significant[k] <= index) {
177
+ k++;
178
+ }
179
+ return k;
180
+ }
181
+ /** The token index of the `k`-th significant token, if any. */
182
+ _significant(k) {
183
+ return this._analysis.significant[k] ?? -1;
184
+ }
185
+ _significantRaw(k) {
186
+ const index = this._significant(k);
187
+ return this._analysis.tokens[index]?.raw;
188
+ }
189
+ _ensureArg(key, factory) {
190
+ const args = this._args;
191
+ if (args[key] == null) {
192
+ args[key] = factory();
193
+ }
194
+ return args[key];
195
+ }
196
+ _host() {
197
+ return this._ensureArg(HOST_REFERENCE, () => this._scope.host());
198
+ }
199
+ _rewrite(token, index) {
200
+ const chain = this._memberChain(index);
201
+ switch (token.type) {
202
+ case 'binding-host': {
203
+ const host = this._host();
204
+ this._addDependency(host, chain);
205
+ return `${BINDING_CONTEXT_PREFIX}.${HOST_REFERENCE}`;
206
+ }
207
+ case 'binding-host-property': {
208
+ const name = token.raw.slice(2);
209
+ const host = this._host();
210
+ const element = host == null ? null : host[name];
211
+ this._addDependency(element, chain);
212
+ return `${BINDING_CONTEXT_PREFIX}.${HOST_REFERENCE}.${name}`;
213
+ }
214
+ case 'binding-item':
215
+ case 'binding-index': {
216
+ // `^item` is 0 levels up, `^^item` 1, …
217
+ const upLevels = token.raw.lastIndexOf('^');
218
+ const member = token.type === 'binding-item' ? 'item' : 'index';
219
+ const reference = `__item_${upLevels}_up_pacem`;
220
+ const placeholder = this._ensureArg(reference, () => this._scope.item(upLevels));
221
+ // the dependency is the `item`/`index` property itself, the path goes on with the item members
222
+ const memberChain = member === 'item' ? [member].concat(chain) : [member];
223
+ this._addDependency(placeholder, memberChain);
224
+ return `${BINDING_CONTEXT_PREFIX}.${reference}.${member}`;
225
+ }
226
+ case 'binding-element': {
227
+ const id = token.raw.slice(1);
228
+ const element = this._ensureArg(id, () => this._scope.element(id));
229
+ this._addDependency(element, chain);
230
+ return `${BINDING_CONTEXT_PREFIX}.${id}`;
231
+ }
232
+ default: {
233
+ // `$this`/`this`
234
+ this._addDependency(this._scope.self, chain);
235
+ return `${BINDING_CONTEXT_PREFIX}.$this`;
236
+ }
237
+ }
238
+ }
239
+ /**
240
+ * Registers a dependency on the first member of `chain` of `element`, unless it's a method, it doesn't exist,
241
+ * or it's already registered.
242
+ */
243
+ _addDependency(element, chain) {
244
+ const property = chain[0];
245
+ if (property == null || element == null || typeof element !== 'object' && typeof element !== 'function') {
246
+ return;
247
+ }
248
+ if (!(property in element) || typeof element[property] === 'function') {
249
+ return;
250
+ }
251
+ const dependencies = this._dependencies;
252
+ const duplicate = dependencies.some(d => d.element === element && d.property === property);
253
+ if (duplicate) {
254
+ return;
255
+ }
256
+ const path = chain.join('.');
257
+ dependencies.push({ element, property, path, twowayAllowed: false });
258
+ }
259
+ }
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @pacem/pacem-core v1.0.6-ricci (https://js.pacem.it)
2
+ * @pacem/pacem-core v2.0.0-abel (https://js.pacem.it)
3
3
  * Pacem (https://pacem.it)
4
4
  * Licensed under Apache-2.0
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @pacem/pacem-core v1.0.6-ricci (https://js.pacem.it)
2
+ * @pacem/pacem-core v2.0.0-abel (https://js.pacem.it)
3
3
  * Pacem (https://pacem.it)
4
4
  * Licensed under Apache-2.0
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @pacem/pacem-core v1.0.6-ricci (https://js.pacem.it)
2
+ * @pacem/pacem-core v2.0.0-abel (https://js.pacem.it)
3
3
  * Pacem (https://pacem.it)
4
4
  * Licensed under Apache-2.0
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @pacem/pacem-core v1.0.6-ricci (https://js.pacem.it)
2
+ * @pacem/pacem-core v2.0.0-abel (https://js.pacem.it)
3
3
  * Pacem (https://pacem.it)
4
4
  * Licensed under Apache-2.0
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @pacem/pacem-core v1.0.6-ricci (https://js.pacem.it)
2
+ * @pacem/pacem-core v2.0.0-abel (https://js.pacem.it)
3
3
  * Pacem (https://pacem.it)
4
4
  * Licensed under Apache-2.0
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @pacem/pacem-core v1.0.6-ricci (https://js.pacem.it)
2
+ * @pacem/pacem-core v2.0.0-abel (https://js.pacem.it)
3
3
  * Pacem (https://pacem.it)
4
4
  * Licensed under Apache-2.0
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @pacem/pacem-core v1.0.6-ricci (https://js.pacem.it)
2
+ * @pacem/pacem-core v2.0.0-abel (https://js.pacem.it)
3
3
  * Pacem (https://pacem.it)
4
4
  * Licensed under Apache-2.0
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @pacem/pacem-core v1.0.6-ricci (https://js.pacem.it)
2
+ * @pacem/pacem-core v2.0.0-abel (https://js.pacem.it)
3
3
  * Pacem (https://pacem.it)
4
4
  * Licensed under Apache-2.0
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @pacem/pacem-core v1.0.6-ricci (https://js.pacem.it)
2
+ * @pacem/pacem-core v2.0.0-abel (https://js.pacem.it)
3
3
  * Pacem (https://pacem.it)
4
4
  * Licensed under Apache-2.0
5
5
  */
@@ -9,42 +9,8 @@
9
9
  import { DeferPromise } from './promise';
10
10
  import { Utils } from './utils';
11
11
  import { CustomElementUtils, Repeater } from './utils-customelement';
12
- const CONTEXT_PREFIX = '__context_pacem';
13
- /** the following pattern matches:
14
- * -------------------------------
15
- * ciao "a tutti \"quanti\" voi" -> "a tutti \"quanti\" voi"
16
- * ciao 'a tutti "quanti" voi'+ mysel.value + 'e a nessun\' altra' -> 'a tutti "quanti" voi'
17
- * 'e a nessun\' altra'
18
- */
19
- const stringPattern = /'(\\'|[^'])*[^\\]?'|"(\\"|[^"])*[^\\]?"/g;
20
- /** the following pattern matches:
21
- * -------------------------------
22
- * var l = new Date(); -> new
23
- * window.eval( -> window.
24
- * eval( -> eval(
25
- * eval ( -> eval (
26
- */
27
- const safetyPattern = /[^\w]new\s|[^\w]window\s*[\.\[]|[^\w]document\s*\.|[^\w]eval\s*\(/;
28
- /** the following pattern matches:
29
- * -------------------------------
30
- * ^^^item.ciao -> ^^^item
31
- * !ciao.value -> !ciao.
32
- * :host.value -> :host.
33
- * ::child.prop -> ::child.
34
- * _connectTo(me.value) -> (me.
35
- * 009.ciao ->
36
- * this[that.value] -> [that.
37
- */
38
- const variablePattern = /(^|[\s\(\[\+\-\*\/!])(:host\.|\^+item[\s\.,;\)\]]|\^+item$|\^+index[^\w\.]|\^+index$|\$?this[\.\s$]?|#[a-zA-Z_][\w]*|::[a-zA-Z_][\w]*)/g;
39
- ///** the following pattern tests:
40
- // * -------------------------------
41
- // * __context.$this.myself.value -> true
42
- // * !__context.myelem.value -> false
43
- // * fn(myelem.value) -> false
44
- // * $this == that -> false
45
- // * ...
46
- // */
47
- //const purePropertyPathPattern = /^[\w\.\$]+/;
12
+ import { BINDING_CONTEXT_PREFIX, BindingParser } from './binding-parser';
13
+ const CONTEXT_PREFIX = BINDING_CONTEXT_PREFIX;
48
14
  /**
49
15
  * A parsed, compiled binding expression (the content of a `{{ ... }}` attribute value). Parsing
50
16
  * (see {@link parse}) rewrites special tokens found in the source text - `:host.`, `^item`/`^^item`/...,
@@ -134,136 +100,45 @@ export class Expression {
134
100
  * Parses a binding expression's source text (the bare content of a `{{ ... }}` attribute, without the
135
101
  * delimiters - see {@link CustomElementUtils.extractBindingAttributeExpression}) into an {@link Expression},
136
102
  * resolving its special tokens against `element`'s scope (see {@link CustomElementUtils.findScopeContext}).
137
- * Throws if the expression matches the unsafe-code guard (`new`, `window.`, `document.`, `eval(`).
103
+ * Tokenization and rewriting are delegated to {@link BindingParser} (cached per expression text).
104
+ * Throws if the expression uses an unsafe construct (`new`, members of `window`/`document`, `eval(`).
138
105
  * @param expression Bare expression source text.
139
106
  * @param element Element the expression is bound to; provides the scope special tokens (`:host.`, `#id`, `^item`, ...) resolve against.
140
- * @returns A constant expression if it has no dependencies; a shared pending placeholder if `element` isn't attached to a scope yet; otherwise a fully resolved expression.
107
+ * @returns A constant expression if it has no special tokens; a shared pending placeholder if `element` isn't attached to a scope yet; otherwise a fully resolved expression.
141
108
  */
142
109
  static parse(expression, element) {
143
- var context = CustomElementUtils.findScopeContext(element);
144
- var args = { '$this': element };
145
- var dependencies = [];
146
- var expr = '';
147
- var independent = true;
148
- //1. remove strings
149
- var strings = [], stringMatches, index = 0, trunks = [];
150
- while ((stringMatches = stringPattern.exec(expression)) != null) {
151
- trunks.push(expression.substr(index, stringMatches.index - index));
152
- let length = stringMatches[0].length;
153
- strings.push({ start: stringMatches.index, length: length });
154
- index = stringMatches.index + length;
155
- }
156
- if (index < expression.length)
157
- trunks.push(expression.substr(index, expression.length - index));
158
- //2. loop non string trunks
159
- // trunks.length >= strings.length (more precisely, the following statemente is always `true`:
160
- // trunks.length === strings.length || trunks.length === strings.length+1;
161
- for (var j = 0; j < trunks.length; j++) {
162
- let trunk = trunks[j];
163
- if (safetyPattern.test(trunk))
164
- throw `Unsafe expression detected: "${trunk}".`;
165
- if (context == null) {
166
- // context is null, means viewActivated event still to be fired.
167
- // check whether this is a constant expression (then no need to instantiate actual binding listeners)
168
- // or not.
169
- if (new RegExp(variablePattern).test(trunk) === true) {
170
- independent = false;
171
- break;
172
- }
173
- }
174
- else {
175
- let processedTrunk = trunk.replace(variablePattern, (mwhole, mstart, melem, index, input) => {
176
- independent = false;
177
- const cmpRef = `__host_pacem`;
178
- // default initialization := assumes `this`
179
- let retval = CONTEXT_PREFIX + '.$this' + (melem.endsWith('.') ? '.' : ''), el = element, dotIndex = input.indexOf('.', index), furtherParts = dotIndex > index ? input.substr(dotIndex + 1).trim() : '', pathRegexArray = /^[\w\.\$]+/.exec(furtherParts), path = pathRegexArray && pathRegexArray.length > 0 && pathRegexArray[0], propArray, prop = path && (propArray = path.split('.')).length > 0 && propArray[0];
180
- if (melem === ':host.') {
181
- el = args[cmpRef] = args[cmpRef] || CustomElementUtils.findHostContext(element);
182
- retval = `${CONTEXT_PREFIX}.${cmpRef}.`;
183
- }
184
- else if (melem.endsWith('^item') || melem.substr(0, melem.length - 1).endsWith('^item')) {
185
- let lastChar = melem.endsWith('^item') ? '' : melem.substr(melem.length - 1, 1);
186
- let upLevels = melem.length - (lastChar.length > 0 ? 6 : 5); // 5 = 'item'.length + 1 (zero-based)
187
- let itemRef = `__item_${upLevels}_up_pacem`;
188
- let item = Repeater.findItemContext(element, upLevels);
189
- el = args[itemRef] = args[itemRef] || (item && item.placeholder);
190
- path = 'item' + (path ? ('.' + path) : '');
191
- prop = 'item';
192
- retval = `${CONTEXT_PREFIX}.${itemRef}.item${lastChar}`;
193
- }
194
- else if (melem.endsWith('^index') || melem.substr(0, melem.length - 1).endsWith('^index')) {
195
- let lastChar = melem.endsWith('^index') ? '' : melem.substr(melem.length - 1, 1);
196
- let upLevels = melem.length - (lastChar.length > 0 ? 7 : 6); // 6 = 'index'.length + 1 (zero-based)
197
- let itemRef = `__item_${upLevels}_up_pacem`;
198
- let item = Repeater.findItemContext(element, upLevels);
199
- el = args[itemRef] = args[itemRef] || (item && item.placeholder);
200
- path = 'index';
201
- prop = 'index';
202
- retval = `${CONTEXT_PREFIX}.${itemRef}.index${lastChar}`;
203
- }
204
- else if (melem.startsWith('::')) {
205
- let melem0 = melem.substr(2, melem.length - 2);
206
- let host = args[cmpRef] = args[cmpRef] || CustomElementUtils.findHostContext(element);
207
- if (!Utils.isNull(host)) {
208
- el = host[melem0];
209
- }
210
- else {
211
- el = null;
212
- }
213
- retval = `${CONTEXT_PREFIX}.${cmpRef}.${melem0}`;
214
- }
215
- else if (melem.startsWith('#')) {
216
- let melem0 = melem.substr(1, melem.length - 1);
217
- el = args[melem0] = args[melem0] || context.querySelector('#' + melem0);
218
- retval = `${CONTEXT_PREFIX}.${melem0}`;
219
- }
220
- // merge dependencies
221
- if (prop && el && prop in el
222
- // && it is not a direct method call (which means that the 'prop' isn't in fact a 'func')
223
- && typeof el[prop] !== 'function' //!isMethodCall
224
- // && does not already exist as a dependency
225
- && dependencies.find(d => d.element === el && d.property == prop) == null) {
226
- dependencies.push({ element: el, property: prop, path: path, twowayAllowed: false });
227
- }
228
- // return value
229
- return mstart + retval;
230
- });
231
- expr += processedTrunk;
232
- if (j < strings.length) {
233
- const s = strings[j];
234
- expr += expression.substr(s.start, s.length);
235
- }
236
- }
237
- }
238
- if (independent) {
239
- // constant expression
240
- var constexpr = new Expression();
241
- constexpr._independent = true;
242
- constexpr._fnBody = `return ${expression};`;
243
- constexpr._voidBody = expression;
244
- constexpr._dependencies = [];
245
- return constexpr;
246
- }
247
- if (context == null) {
248
- return this._getPendingExpressionSingleton();
249
- }
250
- // if some dependency elements aren't available yet, then postpone...
251
- for (var elem in args) {
252
- if (args[elem] == null)
253
- return Expression._getPendingExpressionSingleton();
110
+ const scope = Expression._scopeOf(element);
111
+ const parsed = BindingParser.parse(expression, scope);
112
+ return Expression._fromParsed(parsed);
113
+ }
114
+ /** The DOM surroundings of `element` the special tokens resolve against (lazily, except for the scope context). */
115
+ static _scopeOf(element) {
116
+ const context = CustomElementUtils.findScopeContext(element);
117
+ return {
118
+ ready: context != null,
119
+ self: element,
120
+ host: () => CustomElementUtils.findHostContext(element),
121
+ item: (upLevels) => {
122
+ const item = Repeater.findItemContext(element, upLevels);
123
+ return item?.placeholder;
124
+ },
125
+ element: (id) => context.querySelector('#' + id)
126
+ };
127
+ }
128
+ static _fromParsed(parsed) {
129
+ if (parsed.kind === 'pending') {
130
+ return Expression._getPendingExpressionSingleton();
254
131
  }
255
- if (dependencies.length == 1
256
- // := && trimmed expression contains only letters, figures, underscores, dots, and '$'...
257
- && /[^\w\.\$]+/.test(expr.trim()) !== true) {
258
- // ...then 'twoway' binding might be applied/accepted
259
- dependencies[0].twowayAllowed = true;
132
+ const retval = new Expression();
133
+ retval._fnBody = parsed.functionBody;
134
+ retval._voidBody = parsed.voidBody;
135
+ retval._dependencies = parsed.dependencies;
136
+ if (parsed.kind === 'constant') {
137
+ retval._independent = true;
138
+ return retval;
260
139
  }
261
- var retexpr = new Expression();
262
- retexpr._args = args;
263
- retexpr._fnBody = `try{ var ___$$$r = ${expr}; return ___$$$r; }catch(e){ }`;
264
- retexpr._voidBody = `try{ ${expr} }catch(e){ }`;
265
- retexpr._dependencies = dependencies;
266
- return retexpr;
140
+ retval._args = parsed.args;
141
+ return retval;
267
142
  }
268
143
  }
269
144
  // }
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @pacem/pacem-core v1.0.6-ricci (https://js.pacem.it)
2
+ * @pacem/pacem-core v2.0.0-abel (https://js.pacem.it)
3
3
  * Pacem (https://pacem.it)
4
4
  * Licensed under Apache-2.0
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @pacem/pacem-core v1.0.6-ricci (https://js.pacem.it)
2
+ * @pacem/pacem-core v2.0.0-abel (https://js.pacem.it)
3
3
  * Pacem (https://pacem.it)
4
4
  * Licensed under Apache-2.0
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @pacem/pacem-core v1.0.6-ricci (https://js.pacem.it)
2
+ * @pacem/pacem-core v2.0.0-abel (https://js.pacem.it)
3
3
  * Pacem (https://pacem.it)
4
4
  * Licensed under Apache-2.0
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @pacem/pacem-core v1.0.6-ricci (https://js.pacem.it)
2
+ * @pacem/pacem-core v2.0.0-abel (https://js.pacem.it)
3
3
  * Pacem (https://pacem.it)
4
4
  * Licensed under Apache-2.0
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @pacem/pacem-core v1.0.6-ricci (https://js.pacem.it)
2
+ * @pacem/pacem-core v2.0.0-abel (https://js.pacem.it)
3
3
  * Pacem (https://pacem.it)
4
4
  * Licensed under Apache-2.0
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @pacem/pacem-core v1.0.6-ricci (https://js.pacem.it)
2
+ * @pacem/pacem-core v2.0.0-abel (https://js.pacem.it)
3
3
  * Pacem (https://pacem.it)
4
4
  * Licensed under Apache-2.0
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @pacem/pacem-core v1.0.6-ricci (https://js.pacem.it)
2
+ * @pacem/pacem-core v2.0.0-abel (https://js.pacem.it)
3
3
  * Pacem (https://pacem.it)
4
4
  * Licensed under Apache-2.0
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @pacem/pacem-core v1.0.6-ricci (https://js.pacem.it)
2
+ * @pacem/pacem-core v2.0.0-abel (https://js.pacem.it)
3
3
  * Pacem (https://pacem.it)
4
4
  * Licensed under Apache-2.0
5
5
  */
package/dist/esm/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @pacem/pacem-core v1.0.6-ricci (https://js.pacem.it)
2
+ * @pacem/pacem-core v2.0.0-abel (https://js.pacem.it)
3
3
  * Pacem (https://pacem.it)
4
4
  * Licensed under Apache-2.0
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @pacem/pacem-core v1.0.6-ricci (https://js.pacem.it)
2
+ * @pacem/pacem-core v2.0.0-abel (https://js.pacem.it)
3
3
  * Pacem (https://pacem.it)
4
4
  * Licensed under Apache-2.0
5
5
  */