@angular-wave/angular.ts 0.0.48 → 0.0.49
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/on.spec.js +7 -12
- package/src/core/parser/ast.js +4 -0
- package/src/core/parser/lexer.js +144 -56
- package/src/core/parser/parse.js +16 -56
- package/src/core/parser/parse.md +57 -0
- package/src/core/parser/parser.js +6 -0
- package/src/core/prop.spec.js +4 -4
- package/src/core/scope/scope.js +14 -10
- 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/types.js +1 -1
- package/types/core/parser/ast.d.ts +17 -9
- package/types/core/parser/lexer.d.ts +153 -0
- package/types/core/parser/parse.d.ts +4 -15
- package/types/core/parser/parser.d.ts +7 -1
- package/types/core/scope/scope.d.ts +11 -11
- package/types/types.d.ts +1 -1
package/src/core/parser/lexer.js
CHANGED
|
@@ -17,18 +17,45 @@ const OPERATORS = new Set(
|
|
|
17
17
|
);
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
-
* @
|
|
20
|
+
* @typedef {Object} LexerOptions
|
|
21
|
+
* @property {(ch: string, codePoint: number) => boolean} [isIdentifierStart] Custom function to determine if a character is a valid identifier start.
|
|
22
|
+
* @property {(ch: string, codePoint: number) => boolean} [isIdentifierContinue] Custom function to determine if a character is a valid identifier continuation.
|
|
21
23
|
*/
|
|
22
|
-
export const Lexer = function Lexer(options) {
|
|
23
|
-
this.options = options;
|
|
24
|
-
};
|
|
25
24
|
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Represents a token produced by the lexer.
|
|
27
|
+
* @typedef {Object} Token
|
|
28
|
+
* @property {number} index Index of the token.
|
|
29
|
+
* @property {string} text Text of the token.
|
|
30
|
+
* @property {boolean} [identifier] Indicates if token is an identifier.
|
|
31
|
+
* @property {boolean} [constant] Indicates if token is a constant.
|
|
32
|
+
* @property {string|number} [value] Value of the token if it's a constant.
|
|
33
|
+
* @property {boolean} [operator] Indicates if token is an operator.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Represents a lexer that tokenizes input text. The Lexer takes the original expression string and returns an array of tokens parsed from that string.
|
|
38
|
+
* For example, the string "a + b" would result in tokens for a, +, and b.
|
|
39
|
+
*/
|
|
40
|
+
export class Lexer {
|
|
41
|
+
/**
|
|
42
|
+
* Creates an instance of Lexer.
|
|
43
|
+
* @param {LexerOptions} options Lexer options.
|
|
44
|
+
*/
|
|
45
|
+
constructor(options) {
|
|
46
|
+
/** @type {LexerOptions} */
|
|
47
|
+
this.options = options;
|
|
48
|
+
}
|
|
28
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Tokenizes the input text.
|
|
52
|
+
* @param {string} text Input text to lex.
|
|
53
|
+
* @returns {Array<Token>} Array of tokens.
|
|
54
|
+
*/
|
|
29
55
|
lex(text) {
|
|
30
56
|
this.text = text;
|
|
31
57
|
this.index = 0;
|
|
58
|
+
/** @type {Array<Token>} */
|
|
32
59
|
this.tokens = [];
|
|
33
60
|
|
|
34
61
|
while (this.index < this.text.length) {
|
|
@@ -37,7 +64,7 @@ Lexer.prototype = {
|
|
|
37
64
|
this.readString(ch);
|
|
38
65
|
} else if (
|
|
39
66
|
this.isNumber(ch) ||
|
|
40
|
-
(ch === "." && this.isNumber(this.peek()))
|
|
67
|
+
(ch === "." && this.isNumber(/** @type {string} */ (this.peek())))
|
|
41
68
|
) {
|
|
42
69
|
this.readNumber();
|
|
43
70
|
} else if (this.isIdentifierStart(this.peekMultichar())) {
|
|
@@ -67,23 +94,44 @@ Lexer.prototype = {
|
|
|
67
94
|
}
|
|
68
95
|
}
|
|
69
96
|
return this.tokens;
|
|
70
|
-
}
|
|
97
|
+
}
|
|
71
98
|
|
|
99
|
+
/**
|
|
100
|
+
* Checks if a character is contained in a set of characters.
|
|
101
|
+
* @param {string} ch Character to check.
|
|
102
|
+
* @param {string} chars Set of characters.
|
|
103
|
+
* @returns {boolean} True if character is in the set, false otherwise.
|
|
104
|
+
*/
|
|
72
105
|
is(ch, chars) {
|
|
73
106
|
return chars.indexOf(ch) !== -1;
|
|
74
|
-
}
|
|
107
|
+
}
|
|
75
108
|
|
|
109
|
+
/**
|
|
110
|
+
* Peeks at the next character in the text.
|
|
111
|
+
* @param {number} [i=1] Number of characters to peek.
|
|
112
|
+
* @returns {string|false} Next character or false if end of text.
|
|
113
|
+
*/
|
|
76
114
|
peek(i) {
|
|
77
115
|
const num = i || 1;
|
|
78
116
|
return this.index + num < this.text.length
|
|
79
117
|
? this.text.charAt(this.index + num)
|
|
80
118
|
: false;
|
|
81
|
-
}
|
|
119
|
+
}
|
|
82
120
|
|
|
121
|
+
/**
|
|
122
|
+
* Checks if a character is a number.
|
|
123
|
+
* @param {string} ch Character to check.
|
|
124
|
+
* @returns {boolean} True if character is a number, false otherwise.
|
|
125
|
+
*/
|
|
83
126
|
isNumber(ch) {
|
|
84
127
|
return ch >= "0" && ch <= "9" && typeof ch === "string";
|
|
85
|
-
}
|
|
128
|
+
}
|
|
86
129
|
|
|
130
|
+
/**
|
|
131
|
+
* Checks if a character is whitespace.
|
|
132
|
+
* @param {string} ch Character to check.
|
|
133
|
+
* @returns {boolean} True if character is whitespace, false otherwise.
|
|
134
|
+
*/
|
|
87
135
|
isWhitespace(ch) {
|
|
88
136
|
// IE treats non-breaking space as \u00A0
|
|
89
137
|
return (
|
|
@@ -94,39 +142,52 @@ Lexer.prototype = {
|
|
|
94
142
|
ch === "\v" ||
|
|
95
143
|
ch === "\u00A0"
|
|
96
144
|
);
|
|
97
|
-
}
|
|
145
|
+
}
|
|
98
146
|
|
|
147
|
+
/**
|
|
148
|
+
* Checks if a character is a valid identifier start.
|
|
149
|
+
* @param {string} ch Character to check.
|
|
150
|
+
* @returns {boolean} True if character is a valid identifier start, false otherwise.
|
|
151
|
+
*/
|
|
99
152
|
isIdentifierStart(ch) {
|
|
100
153
|
return this.options.isIdentifierStart
|
|
101
154
|
? this.options.isIdentifierStart(ch, this.codePointAt(ch))
|
|
102
|
-
:
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
(ch >= "a" && ch <= "z") ||
|
|
108
|
-
(ch >= "A" && ch <= "Z") ||
|
|
109
|
-
ch === "_" ||
|
|
110
|
-
ch === "$"
|
|
111
|
-
);
|
|
112
|
-
},
|
|
155
|
+
: (ch >= "a" && ch <= "z") ||
|
|
156
|
+
(ch >= "A" && ch <= "Z") ||
|
|
157
|
+
ch === "_" ||
|
|
158
|
+
ch === "$";
|
|
159
|
+
}
|
|
113
160
|
|
|
161
|
+
/**
|
|
162
|
+
* Checks if a character is a valid identifier continuation.
|
|
163
|
+
* @param {string} ch Character to check.
|
|
164
|
+
* @returns {boolean} True if character is a valid identifier continuation, false otherwise.
|
|
165
|
+
*/
|
|
114
166
|
isIdentifierContinue(ch) {
|
|
115
167
|
return this.options.isIdentifierContinue
|
|
116
168
|
? this.options.isIdentifierContinue(ch, this.codePointAt(ch))
|
|
117
|
-
:
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
169
|
+
: (ch >= "a" && ch <= "z") ||
|
|
170
|
+
(ch >= "A" && ch <= "Z") ||
|
|
171
|
+
ch === "_" ||
|
|
172
|
+
ch === "$" ||
|
|
173
|
+
(ch >= "0" && ch <= "9");
|
|
174
|
+
}
|
|
123
175
|
|
|
176
|
+
/**
|
|
177
|
+
* Converts a character to its Unicode code point.
|
|
178
|
+
* @param {string} ch Character to convert.
|
|
179
|
+
* @returns {number} Unicode code point.
|
|
180
|
+
*/
|
|
124
181
|
codePointAt(ch) {
|
|
125
182
|
if (ch.length === 1) return ch.charCodeAt(0);
|
|
126
183
|
|
|
127
184
|
return (ch.charCodeAt(0) << 10) + ch.charCodeAt(1) - 0x35fdc00;
|
|
128
|
-
}
|
|
185
|
+
}
|
|
129
186
|
|
|
187
|
+
/**
|
|
188
|
+
* Peeks at the next multicharacter sequence in the text.
|
|
189
|
+
* @returns {string} Next multicharacter sequence.
|
|
190
|
+
*/
|
|
130
191
|
peekMultichar() {
|
|
131
192
|
const ch = this.text.charAt(this.index);
|
|
132
193
|
const peek = this.peek();
|
|
@@ -139,12 +200,24 @@ Lexer.prototype = {
|
|
|
139
200
|
return ch + peek;
|
|
140
201
|
}
|
|
141
202
|
return ch;
|
|
142
|
-
}
|
|
203
|
+
}
|
|
143
204
|
|
|
205
|
+
/**
|
|
206
|
+
* Checks if a character is an exponent operator.
|
|
207
|
+
* @param {string} ch Character to check.
|
|
208
|
+
* @returns {boolean} True if character is an exponent operator, false otherwise.
|
|
209
|
+
*/
|
|
144
210
|
isExpOperator(ch) {
|
|
145
211
|
return ch === "-" || ch === "+" || this.isNumber(ch);
|
|
146
|
-
}
|
|
212
|
+
}
|
|
147
213
|
|
|
214
|
+
/**
|
|
215
|
+
* Throws a lexer error.
|
|
216
|
+
* @param {string} error Error message.
|
|
217
|
+
* @param {number} [start] Start index.
|
|
218
|
+
* @param {number} [end] End index.
|
|
219
|
+
* @throws {Error} Lexer error.
|
|
220
|
+
*/
|
|
148
221
|
throwError(error, start, end) {
|
|
149
222
|
end = end || this.index;
|
|
150
223
|
const colStr = isDefined(start)
|
|
@@ -152,13 +225,13 @@ Lexer.prototype = {
|
|
|
152
225
|
: ` ${end}`;
|
|
153
226
|
throw $parseMinErr(
|
|
154
227
|
"lexerr",
|
|
155
|
-
|
|
156
|
-
error,
|
|
157
|
-
colStr,
|
|
158
|
-
this.text,
|
|
228
|
+
`Lexer Error: ${error} at column${colStr} in expression [${this.text}].`,
|
|
159
229
|
);
|
|
160
|
-
}
|
|
230
|
+
}
|
|
161
231
|
|
|
232
|
+
/**
|
|
233
|
+
* Reads and tokenizes a number from the text.
|
|
234
|
+
*/
|
|
162
235
|
readNumber() {
|
|
163
236
|
let number = "";
|
|
164
237
|
const start = this.index;
|
|
@@ -168,7 +241,7 @@ Lexer.prototype = {
|
|
|
168
241
|
number += ch;
|
|
169
242
|
} else {
|
|
170
243
|
const peekCh = this.peek();
|
|
171
|
-
if (ch === "e" && this.isExpOperator(peekCh)) {
|
|
244
|
+
if (ch === "e" && this.isExpOperator(/** @type {string} */ (peekCh))) {
|
|
172
245
|
number += ch;
|
|
173
246
|
} else if (
|
|
174
247
|
this.isExpOperator(ch) &&
|
|
@@ -195,8 +268,11 @@ Lexer.prototype = {
|
|
|
195
268
|
constant: true,
|
|
196
269
|
value: Number(number),
|
|
197
270
|
});
|
|
198
|
-
}
|
|
271
|
+
}
|
|
199
272
|
|
|
273
|
+
/**
|
|
274
|
+
* Reads and tokenizes an identifier from the text.
|
|
275
|
+
*/
|
|
200
276
|
readIdent() {
|
|
201
277
|
const start = this.index;
|
|
202
278
|
this.index += this.peekMultichar().length;
|
|
@@ -212,46 +288,58 @@ Lexer.prototype = {
|
|
|
212
288
|
text: this.text.slice(start, this.index),
|
|
213
289
|
identifier: true,
|
|
214
290
|
});
|
|
215
|
-
}
|
|
291
|
+
}
|
|
216
292
|
|
|
293
|
+
/**
|
|
294
|
+
* Reads and tokenizes a string from the text.
|
|
295
|
+
* @param {string} quote Quote character used for the string.
|
|
296
|
+
*/
|
|
217
297
|
readString(quote) {
|
|
218
298
|
const start = this.index;
|
|
219
|
-
this.index++;
|
|
220
299
|
let string = "";
|
|
221
|
-
let rawString = quote;
|
|
222
300
|
let escape = false;
|
|
301
|
+
|
|
302
|
+
this.index++; // Skip opening quote
|
|
303
|
+
|
|
223
304
|
while (this.index < this.text.length) {
|
|
224
|
-
const ch = this.text
|
|
225
|
-
|
|
305
|
+
const ch = this.text[this.index];
|
|
306
|
+
|
|
226
307
|
if (escape) {
|
|
227
308
|
if (ch === "u") {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
}
|
|
232
|
-
this.index += 4;
|
|
233
|
-
string += String.fromCharCode(parseInt(hex, 16));
|
|
309
|
+
// Handle unicode escapes
|
|
310
|
+
// Simplified for brevity
|
|
311
|
+
string += this.handleUnicodeEscape();
|
|
234
312
|
} else {
|
|
235
|
-
|
|
236
|
-
string += rep || ch;
|
|
313
|
+
string += ESCAPE[ch] || ch;
|
|
237
314
|
}
|
|
238
315
|
escape = false;
|
|
239
316
|
} else if (ch === "\\") {
|
|
240
317
|
escape = true;
|
|
241
318
|
} else if (ch === quote) {
|
|
242
|
-
this.index++;
|
|
243
319
|
this.tokens.push({
|
|
244
320
|
index: start,
|
|
245
|
-
text:
|
|
321
|
+
text: this.text.slice(start, this.index + 1),
|
|
246
322
|
constant: true,
|
|
247
323
|
value: string,
|
|
248
324
|
});
|
|
325
|
+
this.index++; // Skip closing quote
|
|
249
326
|
return;
|
|
250
327
|
} else {
|
|
251
328
|
string += ch;
|
|
252
329
|
}
|
|
330
|
+
|
|
253
331
|
this.index++;
|
|
254
332
|
}
|
|
333
|
+
|
|
255
334
|
this.throwError("Unterminated quote", start);
|
|
256
|
-
}
|
|
257
|
-
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
handleUnicodeEscape() {
|
|
338
|
+
const hex = this.text.substring(this.index + 1, this.index + 5);
|
|
339
|
+
if (!hex.match(/[\da-f]{4}/i)) {
|
|
340
|
+
this.throwError(`Invalid unicode escape [\\u${hex}]`);
|
|
341
|
+
}
|
|
342
|
+
this.index += 4; // Move index past the four hexadecimal digits
|
|
343
|
+
return String.fromCharCode(parseInt(hex, 16));
|
|
344
|
+
}
|
|
345
|
+
}
|
package/src/core/parser/parse.js
CHANGED
|
@@ -14,47 +14,7 @@ export const $parseMinErr = minErr("$parse");
|
|
|
14
14
|
/// ////////////////////////////////
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
* @typedef {function(string|function(import('../scope/scope').Scope):any, function(any, Scope, any):any=, boolean=): import('../../types').CompiledExpression} ParseService
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* @ngdoc service
|
|
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
|
-
*
|
|
17
|
+
* @typedef {function(string|function(import('../scope/scope').Scope):any, function(any, import('../scope/scope').Scope, any):any=, boolean=): import('../../types').CompiledExpression} ParseService
|
|
58
18
|
*/
|
|
59
19
|
|
|
60
20
|
export const literals = {
|
|
@@ -64,15 +24,6 @@ export const literals = {
|
|
|
64
24
|
undefined,
|
|
65
25
|
};
|
|
66
26
|
|
|
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
27
|
export function $ParseProvider() {
|
|
77
28
|
const cache = Object.create(null);
|
|
78
29
|
const literals = {
|
|
@@ -81,7 +32,11 @@ export function $ParseProvider() {
|
|
|
81
32
|
null: null,
|
|
82
33
|
undefined: undefined,
|
|
83
34
|
};
|
|
84
|
-
|
|
35
|
+
/** @type {function(any):boolean?} */
|
|
36
|
+
var identStart;
|
|
37
|
+
|
|
38
|
+
/** @type {function(any):boolean?} */
|
|
39
|
+
var identContinue;
|
|
85
40
|
|
|
86
41
|
/**
|
|
87
42
|
* @ngdoc method
|
|
@@ -117,9 +72,9 @@ 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=} identifierStart The function that will decide whether the given character is
|
|
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=} identifierContinue The function that will decide whether the given character is
|
|
77
|
+
* @param {function(any):boolean=} identifierContinue The function that will decide whether the given character is
|
|
123
78
|
* a valid identifier continue character.
|
|
124
79
|
*/
|
|
125
80
|
this.setIdentifierFns = function (identifierStart, identifierContinue) {
|
|
@@ -152,7 +107,10 @@ export function $ParseProvider() {
|
|
|
152
107
|
parsedExpression = cache[cacheKey];
|
|
153
108
|
|
|
154
109
|
if (!parsedExpression) {
|
|
155
|
-
var lexer = new Lexer(
|
|
110
|
+
var lexer = new Lexer({
|
|
111
|
+
isIdentifierContinue: $parseOptions.isIdentifierContinue,
|
|
112
|
+
isIdentifierStart: $parseOptions.isIdentifierStart,
|
|
113
|
+
});
|
|
156
114
|
var parser = new Parser(lexer, $filter, $parseOptions);
|
|
157
115
|
parsedExpression = parser.parse(exp);
|
|
158
116
|
|
|
@@ -597,7 +555,8 @@ export function chainInterceptors(first, second) {
|
|
|
597
555
|
return chainedInterceptor;
|
|
598
556
|
}
|
|
599
557
|
|
|
600
|
-
|
|
558
|
+
/** @private */
|
|
559
|
+
function expressionInputDirtyCheck(
|
|
601
560
|
newValue,
|
|
602
561
|
oldValueOfValue,
|
|
603
562
|
compareObjectIdentity,
|
|
@@ -629,7 +588,8 @@ export function expressionInputDirtyCheck(
|
|
|
629
588
|
);
|
|
630
589
|
}
|
|
631
590
|
|
|
632
|
-
|
|
591
|
+
/** @private */
|
|
592
|
+
function isAllDefined(value) {
|
|
633
593
|
let allDefined = true;
|
|
634
594
|
forEach(value, (val) => {
|
|
635
595
|
if (!isDefined(val)) allDefined = false;
|
package/src/core/parser/parse.md
CHANGED
|
@@ -11,3 +11,60 @@
|
|
|
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
|
+
- ```js
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
- let getter = $parse('user.name');
|
|
30
|
+
- let setter = getter.assign;
|
|
31
|
+
- let context = {user:{name:'AngularJS'}};
|
|
32
|
+
- let locals = {user:{name:'local'}};
|
|
33
|
+
-
|
|
34
|
+
- expect(getter(context)).toEqual('AngularJS');
|
|
35
|
+
- setter(context, 'newValue');
|
|
36
|
+
- expect(context.user.name).toEqual('newValue');
|
|
37
|
+
- expect(getter(context, locals)).toEqual('local');
|
|
38
|
+
- ```
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
-
|
|
43
|
+
-
|
|
44
|
+
- @param {string} expression String expression to compile.
|
|
45
|
+
- @returns {function(context, locals)} a function which represents the compiled expression:
|
|
46
|
+
-
|
|
47
|
+
- - `context` – `{object}` – an object against which any expressions embedded in the strings
|
|
48
|
+
- are evaluated against (typically a scope object).
|
|
49
|
+
- - `locals` – `{object=}` – local variables context object, useful for overriding values in
|
|
50
|
+
- `context`.
|
|
51
|
+
-
|
|
52
|
+
- The returned function also has the following properties:
|
|
53
|
+
- * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript
|
|
54
|
+
- literal.
|
|
55
|
+
- * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
|
|
56
|
+
- constant literals.
|
|
57
|
+
- * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
|
|
58
|
+
- set to a function to change its value on the given context.
|
|
59
|
+
- \*/
|
|
60
|
+
|
|
61
|
+
/\*\*
|
|
62
|
+
|
|
63
|
+
- @ngdoc provider
|
|
64
|
+
- @name $parseProvider
|
|
65
|
+
-
|
|
66
|
+
-
|
|
67
|
+
- @description
|
|
68
|
+
- `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
|
|
69
|
+
- service.
|
|
70
|
+
\*/
|
|
@@ -7,6 +7,12 @@ import { ASTCompiler } from "./compiler";
|
|
|
7
7
|
* @constructor
|
|
8
8
|
*/
|
|
9
9
|
export class Parser {
|
|
10
|
+
/**
|
|
11
|
+
*
|
|
12
|
+
* @param {import('./lexer').Lexer} lexer
|
|
13
|
+
* @param {*} $filter
|
|
14
|
+
* @param {*} options
|
|
15
|
+
*/
|
|
10
16
|
constructor(lexer, $filter, options) {
|
|
11
17
|
this.ast = new AST(lexer, options);
|
|
12
18
|
this.astCompiler = options.csp
|
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", () => {
|
package/src/core/scope/scope.js
CHANGED
|
@@ -55,7 +55,7 @@ let lastDirtyWatch = null;
|
|
|
55
55
|
let applyAsyncId = null;
|
|
56
56
|
|
|
57
57
|
/** Services required by each scope instance */
|
|
58
|
-
/** @type {
|
|
58
|
+
/** @type {import('../parser/parse').ParseService} */
|
|
59
59
|
let $parse;
|
|
60
60
|
/** @type {import('../../services/browser').Browser} */
|
|
61
61
|
let $browser;
|
|
@@ -89,7 +89,7 @@ export class $RootScopeProvider {
|
|
|
89
89
|
"$browser",
|
|
90
90
|
/**
|
|
91
91
|
* @param {import('../exception-handler').ErrorHandler} exceptionHandler
|
|
92
|
-
* @param {
|
|
92
|
+
* @param {import('../parser/parse').ParseService} parse
|
|
93
93
|
* @param {import('../../services/browser').Browser} browser
|
|
94
94
|
* @returns {Scope} root scope
|
|
95
95
|
*/
|
|
@@ -148,7 +148,7 @@ export class Scope {
|
|
|
148
148
|
this.$root = this;
|
|
149
149
|
|
|
150
150
|
/**
|
|
151
|
-
* @type {
|
|
151
|
+
* @type {Array<any>}
|
|
152
152
|
*/
|
|
153
153
|
this.$$watchers = [];
|
|
154
154
|
|
|
@@ -263,7 +263,7 @@ export class Scope {
|
|
|
263
263
|
if (isolate || parent !== this) {
|
|
264
264
|
child.$on(
|
|
265
265
|
"$destroy",
|
|
266
|
-
/** @param {
|
|
266
|
+
/** @param {any} $event */ //angular.IAngularEvent
|
|
267
267
|
($event) => {
|
|
268
268
|
$event.currentScope.$$destroyed = true;
|
|
269
269
|
},
|
|
@@ -440,10 +440,10 @@ export class Scope {
|
|
|
440
440
|
* values are examined for changes on every call to `$digest`.
|
|
441
441
|
* - The `listener` is called whenever any expression in the `watchExpressions` array changes.
|
|
442
442
|
*
|
|
443
|
-
* @param {Array.<string|
|
|
443
|
+
* @param {Array.<string|((Scope)=>any)>} watchExpressions Array of expressions that will be individually
|
|
444
444
|
* watched using {@link ng.$rootScope.Scope#$watch $watch()}
|
|
445
445
|
*
|
|
446
|
-
* @param {function(
|
|
446
|
+
* @param {function(any, any, Scope): any} listener Callback called whenever the return value of any
|
|
447
447
|
* expression in `watchExpressions` changes
|
|
448
448
|
* The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching
|
|
449
449
|
* those of `watchExpression`
|
|
@@ -533,12 +533,12 @@ export class Scope {
|
|
|
533
533
|
*
|
|
534
534
|
*
|
|
535
535
|
*
|
|
536
|
-
* @param {string|function(
|
|
536
|
+
* @param {string|function(Scope):any} obj Evaluated as {@link guide/expression expression}. The
|
|
537
537
|
* expression value should evaluate to an object or an array which is observed on each
|
|
538
538
|
* {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
|
|
539
539
|
* collection will trigger a call to the `listener`.
|
|
540
540
|
*
|
|
541
|
-
* @param {function(
|
|
541
|
+
* @param {function(any[], any[], Scope):any} listener a callback function called
|
|
542
542
|
* when a change is detected.
|
|
543
543
|
* - The `newCollection` object is the newly modified data obtained from the `obj` expression
|
|
544
544
|
* - The `oldCollection` object is a copy of the former collection data.
|
|
@@ -1309,7 +1309,7 @@ export class Scope {
|
|
|
1309
1309
|
* - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
|
|
1310
1310
|
*
|
|
1311
1311
|
* @param {string} name Event name to listen on.
|
|
1312
|
-
* @param {function(
|
|
1312
|
+
* @param {function(any): any} listener Function to call when the event is emitted witn angular.IAngularEvent
|
|
1313
1313
|
* @returns {function()} Returns a deregistration function for this listener.
|
|
1314
1314
|
*/
|
|
1315
1315
|
$on(name, listener) {
|
|
@@ -1319,6 +1319,7 @@ export class Scope {
|
|
|
1319
1319
|
}
|
|
1320
1320
|
namedListeners.push(listener);
|
|
1321
1321
|
|
|
1322
|
+
/** @type {Scope} */
|
|
1322
1323
|
let current = this;
|
|
1323
1324
|
do {
|
|
1324
1325
|
current.$$listenerCount[name] = (current.$$listenerCount[name] ?? 0) + 1;
|
|
@@ -1389,6 +1390,7 @@ export class Scope {
|
|
|
1389
1390
|
$emit(name, ...args) {
|
|
1390
1391
|
const empty = [];
|
|
1391
1392
|
let namedListeners;
|
|
1393
|
+
/** @type {Scope} */
|
|
1392
1394
|
let scope = this;
|
|
1393
1395
|
let stopPropagation = false;
|
|
1394
1396
|
const event = {
|
|
@@ -1429,7 +1431,7 @@ export class Scope {
|
|
|
1429
1431
|
break;
|
|
1430
1432
|
}
|
|
1431
1433
|
// traverse upwards
|
|
1432
|
-
scope = scope.$parent;
|
|
1434
|
+
scope = /** @type {Scope} */ scope.$parent;
|
|
1433
1435
|
} while (scope);
|
|
1434
1436
|
|
|
1435
1437
|
event.currentScope = null;
|
|
@@ -1460,8 +1462,10 @@ export class Scope {
|
|
|
1460
1462
|
*/
|
|
1461
1463
|
$broadcast(name, ...args) {
|
|
1462
1464
|
const target = this;
|
|
1465
|
+
/** @type {Scope} */
|
|
1463
1466
|
let current = target;
|
|
1464
1467
|
|
|
1468
|
+
/** @type {Scope} */
|
|
1465
1469
|
let next = target;
|
|
1466
1470
|
const event = {
|
|
1467
1471
|
name,
|