@angular-wave/angular.ts 0.0.48 → 0.0.50
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/Makefile +3 -0
- package/README.md +1 -1
- package/css/angular.css +0 -6
- package/dist/angular-ts.esm.js +2 -2
- package/dist/angular-ts.umd.js +2 -2
- package/jsdoc.json +24 -0
- package/package.json +6 -2
- package/src/core/compile/compile.md +1 -1
- package/src/core/compile/compile.spec.js +50 -48
- package/src/core/interpolate/interpolate.js +1 -18
- package/src/core/on.spec.js +7 -12
- package/src/core/parser/ast.js +234 -69
- package/src/core/parser/interpreter.js +246 -50
- package/src/core/parser/lexer.js +144 -57
- package/src/core/parser/parse.js +38 -278
- package/src/core/parser/parse.md +44 -0
- package/src/core/parser/parser.js +32 -6
- package/src/core/parser/shared.js +7 -1
- package/src/core/prop.spec.js +4 -4
- package/src/core/scope/scope.js +14 -10
- package/src/directive/csp.md +0 -26
- package/src/directive/form/form.spec.js +18 -18
- package/src/directive/include/include.spec.js +18 -18
- package/src/directive/switch/switch.spec.js +4 -4
- package/src/shared/constants.js +3 -2
- package/src/shared/jqlite/jqlite.js +2 -2
- package/src/types.js +4 -1
- package/types/core/parser/ast.d.ts +269 -67
- package/types/core/parser/interpreter.d.ts +202 -53
- package/types/core/parser/lexer.d.ts +153 -0
- package/types/core/parser/parse.d.ts +23 -34
- package/types/core/parser/parser.d.ts +43 -14
- package/types/core/parser/shared.d.ts +7 -1
- package/types/core/scope/scope.d.ts +11 -11
- package/types/shared/jqlite/jqlite.d.ts +4 -4
- package/types/types.d.ts +1 -1
- package/src/core/parser/compiler.js +0 -561
- package/types/core/parser/compiler.d.ts +0 -49
package/src/core/parser/parse.js
CHANGED
|
@@ -1,62 +1,27 @@
|
|
|
1
|
-
import {
|
|
2
|
-
csp,
|
|
3
|
-
forEach,
|
|
4
|
-
isDefined,
|
|
5
|
-
isFunction,
|
|
6
|
-
minErr,
|
|
7
|
-
} from "../../shared/utils";
|
|
1
|
+
import { forEach, isDefined, isFunction, minErr } from "../../shared/utils";
|
|
8
2
|
import { getValueOf, PURITY_RELATIVE } from "./shared";
|
|
9
3
|
import { Lexer } from "./lexer";
|
|
10
4
|
import { Parser } from "./parser";
|
|
11
5
|
|
|
12
|
-
export const $parseMinErr = minErr("$parse");
|
|
13
|
-
|
|
14
|
-
/// ////////////////////////////////
|
|
15
|
-
|
|
16
6
|
/**
|
|
17
|
-
* @typedef {function
|
|
7
|
+
* @typedef {function} CompiledExpression
|
|
8
|
+
* @param {import('../scope/scope').Scope} context - An object against which any expressions embedded in the strings are evaluated against (typically a scope object).
|
|
9
|
+
* @param {object} [locals] - local variables context object, useful for overriding values in `context`.
|
|
10
|
+
* @returns {any}
|
|
11
|
+
* @property {boolean} literal - Indicates if the expression is a literal.
|
|
12
|
+
* @property {boolean} constant - Indicates if the expression is constant.
|
|
13
|
+
* @property {function(any, any): any} assign - Assigns a value to a context. If value is not provided,
|
|
14
|
+
* undefined is gonna be used since the implementation
|
|
15
|
+
* does not check the parameter. Let's force a value for consistency. If consumer
|
|
16
|
+
* wants to undefine it, pass the undefined value explicitly.
|
|
18
17
|
*/
|
|
19
18
|
|
|
20
19
|
/**
|
|
21
|
-
* @
|
|
22
|
-
* @name $parse
|
|
23
|
-
* @kind function
|
|
24
|
-
*
|
|
25
|
-
* @description
|
|
26
|
-
*
|
|
27
|
-
* Converts AngularJS {@link guide/expression expression} into a function.
|
|
28
|
-
*
|
|
29
|
-
* ```js
|
|
30
|
-
* let getter = $parse('user.name');
|
|
31
|
-
* let setter = getter.assign;
|
|
32
|
-
* let context = {user:{name:'AngularJS'}};
|
|
33
|
-
* let locals = {user:{name:'local'}};
|
|
34
|
-
*
|
|
35
|
-
* expect(getter(context)).toEqual('AngularJS');
|
|
36
|
-
* setter(context, 'newValue');
|
|
37
|
-
* expect(context.user.name).toEqual('newValue');
|
|
38
|
-
* expect(getter(context, locals)).toEqual('local');
|
|
39
|
-
* ```
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* @param {string} expression String expression to compile.
|
|
43
|
-
* @returns {function(context, locals)} a function which represents the compiled expression:
|
|
44
|
-
*
|
|
45
|
-
* * `context` – `{object}` – an object against which any expressions embedded in the strings
|
|
46
|
-
* are evaluated against (typically a scope object).
|
|
47
|
-
* * `locals` – `{object=}` – local variables context object, useful for overriding values in
|
|
48
|
-
* `context`.
|
|
49
|
-
*
|
|
50
|
-
* The returned function also has the following properties:
|
|
51
|
-
* * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript
|
|
52
|
-
* literal.
|
|
53
|
-
* * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
|
|
54
|
-
* constant literals.
|
|
55
|
-
* * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
|
|
56
|
-
* set to a function to change its value on the given context.
|
|
57
|
-
*
|
|
20
|
+
* @typedef {function(string|function(import('../scope/scope').Scope):any, function(any, import('../scope/scope').Scope, any):any=, boolean=): CompiledExpression} ParseService
|
|
58
21
|
*/
|
|
59
22
|
|
|
23
|
+
export const $parseMinErr = minErr("$parse");
|
|
24
|
+
|
|
60
25
|
export const literals = {
|
|
61
26
|
true: true,
|
|
62
27
|
false: false,
|
|
@@ -64,15 +29,6 @@ export const literals = {
|
|
|
64
29
|
undefined,
|
|
65
30
|
};
|
|
66
31
|
|
|
67
|
-
/**
|
|
68
|
-
* @ngdoc provider
|
|
69
|
-
* @name $parseProvider
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
* @description
|
|
73
|
-
* `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
|
|
74
|
-
* service.
|
|
75
|
-
*/
|
|
76
32
|
export function $ParseProvider() {
|
|
77
33
|
const cache = Object.create(null);
|
|
78
34
|
const literals = {
|
|
@@ -81,7 +37,11 @@ export function $ParseProvider() {
|
|
|
81
37
|
null: null,
|
|
82
38
|
undefined: undefined,
|
|
83
39
|
};
|
|
84
|
-
|
|
40
|
+
/** @type {function(any):boolean?} */
|
|
41
|
+
var identStart;
|
|
42
|
+
|
|
43
|
+
/** @type {function(any):boolean?} */
|
|
44
|
+
var identContinue;
|
|
85
45
|
|
|
86
46
|
/**
|
|
87
47
|
* @ngdoc method
|
|
@@ -99,11 +59,6 @@ export function $ParseProvider() {
|
|
|
99
59
|
};
|
|
100
60
|
|
|
101
61
|
/**
|
|
102
|
-
* @ngdoc method
|
|
103
|
-
* @name $parseProvider#setIdentifierFns
|
|
104
|
-
*
|
|
105
|
-
* @description
|
|
106
|
-
*
|
|
107
62
|
* Allows defining the set of characters that are allowed in AngularJS expressions. The function
|
|
108
63
|
* `identifierStart` will get called to know if a given character is a valid character to be the
|
|
109
64
|
* first character for an identifier. The function `identifierContinue` will get called to know if
|
|
@@ -117,10 +72,11 @@ export function $ParseProvider() {
|
|
|
117
72
|
* Since this function will be called extensively, keep the implementation of these functions fast,
|
|
118
73
|
* as the performance of these functions have a direct impact on the expressions parsing speed.
|
|
119
74
|
*
|
|
120
|
-
* @param {function
|
|
75
|
+
* @param {function(any):boolean} [identifierStart] The function that will decide whether the given character is
|
|
121
76
|
* a valid identifier start character.
|
|
122
|
-
* @param {function
|
|
77
|
+
* @param {function(any):boolean} [identifierContinue] The function that will decide whether the given character is
|
|
123
78
|
* a valid identifier continue character.
|
|
79
|
+
* @returns {$ParseProvider}
|
|
124
80
|
*/
|
|
125
81
|
this.setIdentifierFns = function (identifierStart, identifierContinue) {
|
|
126
82
|
identStart = identifierStart;
|
|
@@ -131,9 +87,7 @@ export function $ParseProvider() {
|
|
|
131
87
|
this.$get = [
|
|
132
88
|
"$filter",
|
|
133
89
|
function ($filter) {
|
|
134
|
-
var noUnsafeEval = csp().noUnsafeEval;
|
|
135
90
|
var $parseOptions = {
|
|
136
|
-
csp: noUnsafeEval,
|
|
137
91
|
literals: structuredClone(literals),
|
|
138
92
|
isIdentifierStart: isFunction(identStart) && identStart,
|
|
139
93
|
isIdentifierContinue: isFunction(identContinue) && identContinue,
|
|
@@ -152,7 +106,10 @@ export function $ParseProvider() {
|
|
|
152
106
|
parsedExpression = cache[cacheKey];
|
|
153
107
|
|
|
154
108
|
if (!parsedExpression) {
|
|
155
|
-
var lexer = new Lexer(
|
|
109
|
+
var lexer = new Lexer({
|
|
110
|
+
isIdentifierContinue: $parseOptions.isIdentifierContinue,
|
|
111
|
+
isIdentifierStart: $parseOptions.isIdentifierStart,
|
|
112
|
+
});
|
|
156
113
|
var parser = new Parser(lexer, $filter, $parseOptions);
|
|
157
114
|
parsedExpression = parser.parse(exp);
|
|
158
115
|
|
|
@@ -168,213 +125,16 @@ export function $ParseProvider() {
|
|
|
168
125
|
}
|
|
169
126
|
}
|
|
170
127
|
|
|
128
|
+
/**
|
|
129
|
+
* @param {string} exp
|
|
130
|
+
* @returns {import("./ast").ASTNode}
|
|
131
|
+
*/
|
|
171
132
|
function $$getAst(exp) {
|
|
172
133
|
var lexer = new Lexer($parseOptions);
|
|
173
134
|
var parser = new Parser(lexer, $filter, $parseOptions);
|
|
174
135
|
return parser.getAst(exp).ast;
|
|
175
136
|
}
|
|
176
137
|
|
|
177
|
-
function expressionInputDirtyCheck(
|
|
178
|
-
newValue,
|
|
179
|
-
oldValueOfValue,
|
|
180
|
-
compareObjectIdentity,
|
|
181
|
-
) {
|
|
182
|
-
if (newValue == null || oldValueOfValue == null) {
|
|
183
|
-
// null/undefined
|
|
184
|
-
return newValue === oldValueOfValue;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
if (typeof newValue === "object") {
|
|
188
|
-
// attempt to convert the value to a primitive type
|
|
189
|
-
// TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
|
|
190
|
-
// be cheaply dirty-checked
|
|
191
|
-
newValue = getValueOf(newValue);
|
|
192
|
-
|
|
193
|
-
if (typeof newValue === "object" && !compareObjectIdentity) {
|
|
194
|
-
// objects/arrays are not supported - deep-watching them would be too expensive
|
|
195
|
-
return false;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// fall-through to the primitive equality check
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
//Primitive or NaN
|
|
202
|
-
|
|
203
|
-
return (
|
|
204
|
-
newValue === oldValueOfValue ||
|
|
205
|
-
(newValue !== newValue && oldValueOfValue !== oldValueOfValue)
|
|
206
|
-
);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
function inputsWatchDelegate(
|
|
210
|
-
scope,
|
|
211
|
-
listener,
|
|
212
|
-
objectEquality,
|
|
213
|
-
parsedExpression,
|
|
214
|
-
) {
|
|
215
|
-
var inputExpressions = parsedExpression.inputs;
|
|
216
|
-
var lastResult;
|
|
217
|
-
|
|
218
|
-
if (inputExpressions.length === 1) {
|
|
219
|
-
var oldInputValueOf = expressionInputDirtyCheck; // init to something unique so that equals check fails
|
|
220
|
-
inputExpressions = inputExpressions[0];
|
|
221
|
-
return scope.$watch(
|
|
222
|
-
function expressionInputWatch(scope) {
|
|
223
|
-
var newInputValue = inputExpressions(scope);
|
|
224
|
-
if (
|
|
225
|
-
!expressionInputDirtyCheck(
|
|
226
|
-
newInputValue,
|
|
227
|
-
oldInputValueOf,
|
|
228
|
-
inputExpressions.isPure,
|
|
229
|
-
)
|
|
230
|
-
) {
|
|
231
|
-
lastResult = parsedExpression(scope, undefined, undefined, [
|
|
232
|
-
newInputValue,
|
|
233
|
-
]);
|
|
234
|
-
oldInputValueOf = newInputValue && getValueOf(newInputValue);
|
|
235
|
-
}
|
|
236
|
-
return lastResult;
|
|
237
|
-
},
|
|
238
|
-
listener,
|
|
239
|
-
objectEquality,
|
|
240
|
-
);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
var oldInputValueOfValues = [];
|
|
244
|
-
var oldInputValues = [];
|
|
245
|
-
for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
|
|
246
|
-
oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails
|
|
247
|
-
oldInputValues[i] = null;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
return scope.$watch(
|
|
251
|
-
function expressionInputsWatch(scope) {
|
|
252
|
-
var changed = false;
|
|
253
|
-
|
|
254
|
-
for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
|
|
255
|
-
var newInputValue = inputExpressions[i](scope);
|
|
256
|
-
if (
|
|
257
|
-
changed ||
|
|
258
|
-
(changed = !expressionInputDirtyCheck(
|
|
259
|
-
newInputValue,
|
|
260
|
-
oldInputValueOfValues[i],
|
|
261
|
-
inputExpressions[i].isPure,
|
|
262
|
-
))
|
|
263
|
-
) {
|
|
264
|
-
oldInputValues[i] = newInputValue;
|
|
265
|
-
oldInputValueOfValues[i] =
|
|
266
|
-
newInputValue && getValueOf(newInputValue);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
if (changed) {
|
|
271
|
-
lastResult = parsedExpression(
|
|
272
|
-
scope,
|
|
273
|
-
undefined,
|
|
274
|
-
undefined,
|
|
275
|
-
oldInputValues,
|
|
276
|
-
);
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
return lastResult;
|
|
280
|
-
},
|
|
281
|
-
listener,
|
|
282
|
-
objectEquality,
|
|
283
|
-
);
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
function oneTimeWatchDelegate(
|
|
287
|
-
scope,
|
|
288
|
-
listener,
|
|
289
|
-
objectEquality,
|
|
290
|
-
parsedExpression,
|
|
291
|
-
) {
|
|
292
|
-
var isDone = parsedExpression.literal ? isAllDefined : isDefined;
|
|
293
|
-
var unwatch, lastValue;
|
|
294
|
-
|
|
295
|
-
var exp = parsedExpression.$$intercepted || parsedExpression;
|
|
296
|
-
var post = parsedExpression.$$interceptor || ((x) => x);
|
|
297
|
-
|
|
298
|
-
var useInputs = parsedExpression.inputs && !exp.inputs;
|
|
299
|
-
|
|
300
|
-
// Propagate the literal/inputs/constant attributes
|
|
301
|
-
// ... but not oneTime since we are handling it
|
|
302
|
-
oneTimeWatch.literal = parsedExpression.literal;
|
|
303
|
-
oneTimeWatch.constant = parsedExpression.constant;
|
|
304
|
-
oneTimeWatch.inputs = parsedExpression.inputs;
|
|
305
|
-
|
|
306
|
-
// Allow other delegates to run on this wrapped expression
|
|
307
|
-
addWatchDelegate(oneTimeWatch);
|
|
308
|
-
|
|
309
|
-
unwatch = scope.$watch(oneTimeWatch, listener, objectEquality);
|
|
310
|
-
|
|
311
|
-
return unwatch;
|
|
312
|
-
|
|
313
|
-
function unwatchIfDone() {
|
|
314
|
-
if (isDone(lastValue)) {
|
|
315
|
-
unwatch();
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
function oneTimeWatch(scope, locals, assign, inputs) {
|
|
320
|
-
lastValue =
|
|
321
|
-
useInputs && inputs
|
|
322
|
-
? inputs[0]
|
|
323
|
-
: exp(scope, locals, assign, inputs);
|
|
324
|
-
if (isDone(lastValue)) {
|
|
325
|
-
scope.$$postDigest(unwatchIfDone);
|
|
326
|
-
}
|
|
327
|
-
return post(lastValue);
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
function isAllDefined(value) {
|
|
332
|
-
var allDefined = true;
|
|
333
|
-
forEach(value, function (val) {
|
|
334
|
-
if (!isDefined(val)) allDefined = false;
|
|
335
|
-
});
|
|
336
|
-
return allDefined;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
function constantWatchDelegate(
|
|
340
|
-
scope,
|
|
341
|
-
listener,
|
|
342
|
-
objectEquality,
|
|
343
|
-
parsedExpression,
|
|
344
|
-
) {
|
|
345
|
-
var unwatch = scope.$watch(
|
|
346
|
-
function constantWatch(scope) {
|
|
347
|
-
unwatch();
|
|
348
|
-
return parsedExpression(scope);
|
|
349
|
-
},
|
|
350
|
-
listener,
|
|
351
|
-
objectEquality,
|
|
352
|
-
);
|
|
353
|
-
return unwatch;
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
function addWatchDelegate(parsedExpression) {
|
|
357
|
-
if (parsedExpression.constant) {
|
|
358
|
-
parsedExpression.$$watchDelegate = constantWatchDelegate;
|
|
359
|
-
} else if (parsedExpression.oneTime) {
|
|
360
|
-
parsedExpression.$$watchDelegate = oneTimeWatchDelegate;
|
|
361
|
-
} else if (parsedExpression.inputs) {
|
|
362
|
-
parsedExpression.$$watchDelegate = inputsWatchDelegate;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
return parsedExpression;
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
function chainInterceptors(first, second) {
|
|
369
|
-
function chainedInterceptor(value) {
|
|
370
|
-
return second(first(value));
|
|
371
|
-
}
|
|
372
|
-
chainedInterceptor.$stateful = first.$stateful || second.$stateful;
|
|
373
|
-
chainedInterceptor.$$pure = first.$$pure && second.$$pure;
|
|
374
|
-
|
|
375
|
-
return chainedInterceptor;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
138
|
function addInterceptor(parsedExpression, interceptorFn) {
|
|
379
139
|
if (!interceptorFn) return parsedExpression;
|
|
380
140
|
|
|
@@ -436,16 +196,16 @@ export function $ParseProvider() {
|
|
|
436
196
|
];
|
|
437
197
|
}
|
|
438
198
|
|
|
439
|
-
function constantWatchDelegate(
|
|
199
|
+
export function constantWatchDelegate(
|
|
440
200
|
scope,
|
|
441
201
|
listener,
|
|
442
202
|
objectEquality,
|
|
443
203
|
parsedExpression,
|
|
444
204
|
) {
|
|
445
205
|
const unwatch = scope.$watch(
|
|
446
|
-
(
|
|
206
|
+
() => {
|
|
447
207
|
unwatch();
|
|
448
|
-
return parsedExpression(
|
|
208
|
+
return parsedExpression(scope);
|
|
449
209
|
},
|
|
450
210
|
listener,
|
|
451
211
|
objectEquality,
|
|
@@ -465,7 +225,7 @@ function addWatchDelegate(parsedExpression) {
|
|
|
465
225
|
return parsedExpression;
|
|
466
226
|
}
|
|
467
227
|
|
|
468
|
-
|
|
228
|
+
function inputsWatchDelegate(
|
|
469
229
|
scope,
|
|
470
230
|
listener,
|
|
471
231
|
objectEquality,
|
|
@@ -542,7 +302,7 @@ export function inputsWatchDelegate(
|
|
|
542
302
|
);
|
|
543
303
|
}
|
|
544
304
|
|
|
545
|
-
|
|
305
|
+
function oneTimeWatchDelegate(
|
|
546
306
|
scope,
|
|
547
307
|
listener,
|
|
548
308
|
objectEquality,
|
|
@@ -587,7 +347,7 @@ export function oneTimeWatchDelegate(
|
|
|
587
347
|
return unwatch;
|
|
588
348
|
}
|
|
589
349
|
|
|
590
|
-
|
|
350
|
+
function chainInterceptors(first, second) {
|
|
591
351
|
function chainedInterceptor(value) {
|
|
592
352
|
return second(first(value));
|
|
593
353
|
}
|
|
@@ -597,7 +357,7 @@ export function chainInterceptors(first, second) {
|
|
|
597
357
|
return chainedInterceptor;
|
|
598
358
|
}
|
|
599
359
|
|
|
600
|
-
|
|
360
|
+
function expressionInputDirtyCheck(
|
|
601
361
|
newValue,
|
|
602
362
|
oldValueOfValue,
|
|
603
363
|
compareObjectIdentity,
|
|
@@ -629,7 +389,7 @@ export function expressionInputDirtyCheck(
|
|
|
629
389
|
);
|
|
630
390
|
}
|
|
631
391
|
|
|
632
|
-
|
|
392
|
+
function isAllDefined(value) {
|
|
633
393
|
let allDefined = true;
|
|
634
394
|
forEach(value, (val) => {
|
|
635
395
|
if (!isDefined(val)) allDefined = false;
|
package/src/core/parser/parse.md
CHANGED
|
@@ -11,3 +11,47 @@
|
|
|
11
11
|
// content then it is possible that your application contains a security vulnerability to an XSS style attack.
|
|
12
12
|
//
|
|
13
13
|
// See https://docs.angularjs.org/guide/security
|
|
14
|
+
|
|
15
|
+
/\*\*
|
|
16
|
+
|
|
17
|
+
- @ngdoc service
|
|
18
|
+
- @name $parse
|
|
19
|
+
- @kind function
|
|
20
|
+
-
|
|
21
|
+
- @description
|
|
22
|
+
-
|
|
23
|
+
- Converts AngularJS {@link guide/expression expression} into a function.
|
|
24
|
+
-
|
|
25
|
+
- let getter = $parse('user.name');
|
|
26
|
+
- let setter = getter.assign;
|
|
27
|
+
- let context = {user:{name:'AngularJS'}};
|
|
28
|
+
- let locals = {user:{name:'local'}};
|
|
29
|
+
-
|
|
30
|
+
- expect(getter(context)).toEqual('AngularJS');
|
|
31
|
+
- setter(context, 'newValue');
|
|
32
|
+
- expect(context.user.name).toEqual('newValue');
|
|
33
|
+
- expect(getter(context, locals)).toEqual('local');
|
|
34
|
+
-
|
|
35
|
+
- @param {string} expression String expression to compile.
|
|
36
|
+
- @returns {function(context, locals)} a function which represents the compiled expression:
|
|
37
|
+
-
|
|
38
|
+
- - `context` – `{object}` – an object against which any expressions embedded in the strings
|
|
39
|
+
- are evaluated against (typically a scope object).
|
|
40
|
+
- - `locals` – `{object=}` – local variables context object, useful for overriding values in
|
|
41
|
+
- `context`.
|
|
42
|
+
-
|
|
43
|
+
- The returned function also has the following properties:
|
|
44
|
+
- * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript
|
|
45
|
+
- literal.
|
|
46
|
+
- * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
|
|
47
|
+
- constant literals.
|
|
48
|
+
- * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
|
|
49
|
+
- set to a function to change its value on the given context.
|
|
50
|
+
- @ngdoc provider
|
|
51
|
+
- @name $parseProvider
|
|
52
|
+
-
|
|
53
|
+
-
|
|
54
|
+
- @description
|
|
55
|
+
- `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
|
|
56
|
+
- service.
|
|
57
|
+
\*/
|
|
@@ -1,21 +1,43 @@
|
|
|
1
1
|
import { AST } from "./ast";
|
|
2
2
|
import { isLiteral, isConstant } from "./shared";
|
|
3
3
|
import { ASTInterpreter } from "./interpreter";
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @typedef {Object} ParsedAST
|
|
7
|
+
* @property {import("./ast").ASTNode} ast - AST representation of expression
|
|
8
|
+
* @property {boolean} oneTime - True if expression should be evaluated only once
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @typedef {Object} ParserOptions
|
|
13
|
+
* @property {function(string):any} literals
|
|
14
|
+
*/
|
|
5
15
|
|
|
6
16
|
/**
|
|
7
17
|
* @constructor
|
|
8
18
|
*/
|
|
9
19
|
export class Parser {
|
|
20
|
+
/**
|
|
21
|
+
*
|
|
22
|
+
* @param {import('./lexer').Lexer} lexer
|
|
23
|
+
* @param {function(any):any} $filter
|
|
24
|
+
* @param {ParserOptions} options
|
|
25
|
+
*/
|
|
10
26
|
constructor(lexer, $filter, options) {
|
|
27
|
+
/** @type {AST} */
|
|
11
28
|
this.ast = new AST(lexer, options);
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
29
|
+
|
|
30
|
+
/** @type {ASTInterpreter} */
|
|
31
|
+
this.astCompiler = new ASTInterpreter($filter);
|
|
15
32
|
}
|
|
16
33
|
|
|
17
|
-
|
|
18
|
-
|
|
34
|
+
/**
|
|
35
|
+
*
|
|
36
|
+
* @param {string} exp - Expression to be parsed
|
|
37
|
+
* @returns
|
|
38
|
+
*/
|
|
39
|
+
parse(exp) {
|
|
40
|
+
const { ast, oneTime } = this.getAst(exp);
|
|
19
41
|
const fn = this.astCompiler.compile(ast);
|
|
20
42
|
fn.literal = isLiteral(ast);
|
|
21
43
|
fn.constant = isConstant(ast);
|
|
@@ -23,6 +45,10 @@ export class Parser {
|
|
|
23
45
|
return fn;
|
|
24
46
|
}
|
|
25
47
|
|
|
48
|
+
/**
|
|
49
|
+
* @param {string} exp - Expression to be parsed
|
|
50
|
+
* @returns {ParsedAST}
|
|
51
|
+
*/
|
|
26
52
|
getAst(exp) {
|
|
27
53
|
let oneTime = false;
|
|
28
54
|
exp = exp.trim();
|
|
@@ -61,6 +61,12 @@ export function isPure(node, parentIsPure) {
|
|
|
61
61
|
return undefined === parentIsPure ? PURITY_RELATIVE : parentIsPure;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Decorates ast with constant, toWatch, and isPure properties
|
|
66
|
+
* @param {import("./ast").ASTNode} ast
|
|
67
|
+
* @param {function(any):any} $filter
|
|
68
|
+
* @param {*} parentIsPure
|
|
69
|
+
*/
|
|
64
70
|
export function findConstantAndWatchExpressions(ast, $filter, parentIsPure) {
|
|
65
71
|
let allConstants;
|
|
66
72
|
let argsToWatch;
|
|
@@ -71,7 +77,7 @@ export function findConstantAndWatchExpressions(ast, $filter, parentIsPure) {
|
|
|
71
77
|
switch (ast.type) {
|
|
72
78
|
case ASTType.Program:
|
|
73
79
|
allConstants = true;
|
|
74
|
-
/** @type {[
|
|
80
|
+
/** @type {[import("./ast").ASTNode]} */ (ast.body).forEach((expr) => {
|
|
75
81
|
findConstantAndWatchExpressions(expr.expression, $filter, astIsPure);
|
|
76
82
|
allConstants = allConstants && expr.expression.constant;
|
|
77
83
|
});
|
package/src/core/prop.spec.js
CHANGED
|
@@ -119,7 +119,7 @@ describe("ngProp*", () => {
|
|
|
119
119
|
it("should work with different prefixes", () => {
|
|
120
120
|
$rootScope.name = "Misko";
|
|
121
121
|
const element = $compile(
|
|
122
|
-
'<span ng
|
|
122
|
+
'<span ng-prop-test="name" ng-Prop-test2="name" ng-Prop-test3="name"></span>',
|
|
123
123
|
)($rootScope);
|
|
124
124
|
expect(element[0].test).toBe("Misko");
|
|
125
125
|
expect(element[0].test2).toBe("Misko");
|
|
@@ -138,7 +138,7 @@ describe("ngProp*", () => {
|
|
|
138
138
|
it("should work if they are prefixed with x- or data- and different prefixes", () => {
|
|
139
139
|
$rootScope.name = "Misko";
|
|
140
140
|
const element = $compile(
|
|
141
|
-
'<span data-ng-prop-test2="name" ng-prop-test3="name" data-ng
|
|
141
|
+
'<span data-ng-prop-test2="name" ng-prop-test3="name" data-ng-prop-test4="name" ' +
|
|
142
142
|
'ng-prop-test5="name" ng-prop-test6="name"></span>',
|
|
143
143
|
)($rootScope);
|
|
144
144
|
expect(element[0].test2).toBe("Misko");
|
|
@@ -179,7 +179,7 @@ describe("ngProp*", () => {
|
|
|
179
179
|
}),
|
|
180
180
|
);
|
|
181
181
|
$compile(
|
|
182
|
-
'<div attr-exposer ng-prop-title="12" ng-prop-super-title="34" ng-prop-my-
|
|
182
|
+
'<div attr-exposer ng-prop-title="12" ng-prop-super-title="34" ng-prop-my-camel-title="56">',
|
|
183
183
|
)($rootScope);
|
|
184
184
|
|
|
185
185
|
expect(attrs.title).toBeUndefined();
|
|
@@ -195,7 +195,7 @@ describe("ngProp*", () => {
|
|
|
195
195
|
expect(attrs.myCamelTitle).toBeUndefined();
|
|
196
196
|
expect(attrs.$attr.myCamelTitle).toBeUndefined();
|
|
197
197
|
expect(attrs.ngPropMyCamelTitle).toBe("56");
|
|
198
|
-
expect(attrs.$attr.ngPropMyCamelTitle).toBe("ng-prop-my-
|
|
198
|
+
expect(attrs.$attr.ngPropMyCamelTitle).toBe("ng-prop-my-camel-title");
|
|
199
199
|
});
|
|
200
200
|
|
|
201
201
|
it("should not conflict with (ng-attr-)attribute mappings of the same name", () => {
|