@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.
- package/dist/browser/pacem-core.js +289 -161
- package/dist/browser/pacem-core.js.map +1 -1
- package/dist/browser/pacem-core.min.js +2 -2
- package/dist/bundle/pacem-core.min.mjs +6 -6
- package/dist/bundle/pacem-core.mjs +276 -104
- package/dist/bundle/pacem-core.mjs.map +4 -4
- package/dist/docs/pacem-core.json +1 -1
- package/dist/esm/adapter.js +1 -1
- package/dist/esm/binding-parser.js +259 -0
- package/dist/esm/command.js +1 -1
- package/dist/esm/comparers.js +1 -1
- package/dist/esm/converters.js +1 -1
- package/dist/esm/decorators-next.js +1 -1
- package/dist/esm/decorators.js +1 -1
- package/dist/esm/defaults.js +1 -1
- package/dist/esm/events.js +1 -1
- package/dist/esm/eventtarget.js +1 -1
- package/dist/esm/expression.js +36 -161
- package/dist/esm/index-animations.js +1 -1
- package/dist/esm/index-behaviors.js +1 -1
- package/dist/esm/index-components.js +1 -1
- package/dist/esm/index-iife.js +1 -1
- package/dist/esm/index-logging.js +1 -1
- package/dist/esm/index-net.js +1 -1
- package/dist/esm/index-root.js +1 -1
- package/dist/esm/index-ui.js +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/logger.js +1 -1
- package/dist/esm/prefix.js +1 -1
- package/dist/esm/promise.js +1 -1
- package/dist/esm/regex.js +1 -1
- package/dist/esm/storage.js +1 -1
- package/dist/esm/template.js +1 -1
- package/dist/esm/transforms.js +1 -1
- package/dist/esm/types.js +1 -1
- package/dist/esm/utils-customelement.js +1 -1
- package/dist/esm/utils-customevent.js +1 -1
- package/dist/esm/utils.js +1 -1
- package/package.json +1 -1
- 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
|
|
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",
|
package/dist/esm/adapter.js
CHANGED
|
@@ -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
|
+
}
|
package/dist/esm/command.js
CHANGED
package/dist/esm/comparers.js
CHANGED
package/dist/esm/converters.js
CHANGED
package/dist/esm/decorators.js
CHANGED
package/dist/esm/defaults.js
CHANGED
package/dist/esm/events.js
CHANGED
package/dist/esm/eventtarget.js
CHANGED
package/dist/esm/expression.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @pacem/pacem-core
|
|
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
|
-
|
|
13
|
-
|
|
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
|
-
*
|
|
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
|
|
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
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
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
|
-
|
|
262
|
-
|
|
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
|
// }
|
package/dist/esm/index-iife.js
CHANGED
package/dist/esm/index-net.js
CHANGED
package/dist/esm/index-root.js
CHANGED
package/dist/esm/index-ui.js
CHANGED
package/dist/esm/index.js
CHANGED
package/dist/esm/logger.js
CHANGED