@angular-wave/angular.ts 0.0.1

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 (231) hide show
  1. package/.eslintignore +1 -0
  2. package/.eslintrc.cjs +29 -0
  3. package/.github/workflows/playwright.yml +27 -0
  4. package/CHANGELOG.md +17974 -0
  5. package/CODE_OF_CONDUCT.md +3 -0
  6. package/CONTRIBUTING.md +246 -0
  7. package/DEVELOPERS.md +488 -0
  8. package/LICENSE +22 -0
  9. package/Makefile +31 -0
  10. package/README.md +115 -0
  11. package/RELEASE.md +98 -0
  12. package/SECURITY.md +16 -0
  13. package/TRIAGING.md +135 -0
  14. package/css/angular.css +22 -0
  15. package/dist/angular-ts.cjs.js +36843 -0
  16. package/dist/angular-ts.esm.js +36841 -0
  17. package/dist/angular-ts.umd.js +36848 -0
  18. package/dist/build/angular-animate.js +4272 -0
  19. package/dist/build/angular-aria.js +426 -0
  20. package/dist/build/angular-message-format.js +1072 -0
  21. package/dist/build/angular-messages.js +829 -0
  22. package/dist/build/angular-mocks.js +3757 -0
  23. package/dist/build/angular-parse-ext.js +1275 -0
  24. package/dist/build/angular-resource.js +911 -0
  25. package/dist/build/angular-route.js +1266 -0
  26. package/dist/build/angular-sanitize.js +891 -0
  27. package/dist/build/angular-touch.js +368 -0
  28. package/dist/build/angular.js +36600 -0
  29. package/e2e/unit.spec.ts +15 -0
  30. package/images/android-chrome-192x192.png +0 -0
  31. package/images/android-chrome-512x512.png +0 -0
  32. package/images/apple-touch-icon.png +0 -0
  33. package/images/favicon-16x16.png +0 -0
  34. package/images/favicon-32x32.png +0 -0
  35. package/images/favicon.ico +0 -0
  36. package/images/site.webmanifest +1 -0
  37. package/index.html +104 -0
  38. package/package.json +47 -0
  39. package/playwright.config.ts +78 -0
  40. package/public/circle.html +1 -0
  41. package/public/my_child_directive.html +1 -0
  42. package/public/my_directive.html +1 -0
  43. package/public/my_other_directive.html +1 -0
  44. package/public/test.html +1 -0
  45. package/rollup.config.js +31 -0
  46. package/src/animations/animateCache.js +55 -0
  47. package/src/animations/animateChildrenDirective.js +105 -0
  48. package/src/animations/animateCss.js +1139 -0
  49. package/src/animations/animateCssDriver.js +291 -0
  50. package/src/animations/animateJs.js +367 -0
  51. package/src/animations/animateJsDriver.js +67 -0
  52. package/src/animations/animateQueue.js +851 -0
  53. package/src/animations/animation.js +506 -0
  54. package/src/animations/module.js +779 -0
  55. package/src/animations/ngAnimateSwap.js +119 -0
  56. package/src/animations/rafScheduler.js +50 -0
  57. package/src/animations/shared.js +378 -0
  58. package/src/constants.js +20 -0
  59. package/src/core/animate.js +845 -0
  60. package/src/core/animateCss.js +73 -0
  61. package/src/core/animateRunner.js +195 -0
  62. package/src/core/attributes.js +199 -0
  63. package/src/core/cache.js +45 -0
  64. package/src/core/compile.js +4727 -0
  65. package/src/core/controller.js +225 -0
  66. package/src/core/exceptionHandler.js +63 -0
  67. package/src/core/filter.js +146 -0
  68. package/src/core/interpolate.js +442 -0
  69. package/src/core/interval.js +188 -0
  70. package/src/core/intervalFactory.js +57 -0
  71. package/src/core/location.js +1086 -0
  72. package/src/core/parser/parse.js +2562 -0
  73. package/src/core/parser/parse.md +13 -0
  74. package/src/core/q.js +746 -0
  75. package/src/core/rootScope.js +1596 -0
  76. package/src/core/sanitizeUri.js +85 -0
  77. package/src/core/sce.js +1161 -0
  78. package/src/core/taskTrackerFactory.js +125 -0
  79. package/src/core/timeout.js +121 -0
  80. package/src/core/urlUtils.js +187 -0
  81. package/src/core/utils.js +1349 -0
  82. package/src/directive/a.js +37 -0
  83. package/src/directive/attrs.js +283 -0
  84. package/src/directive/bind.js +51 -0
  85. package/src/directive/bind.md +142 -0
  86. package/src/directive/change.js +12 -0
  87. package/src/directive/change.md +25 -0
  88. package/src/directive/cloak.js +12 -0
  89. package/src/directive/cloak.md +24 -0
  90. package/src/directive/events.js +75 -0
  91. package/src/directive/events.md +166 -0
  92. package/src/directive/form.js +725 -0
  93. package/src/directive/init.js +15 -0
  94. package/src/directive/init.md +41 -0
  95. package/src/directive/input.js +1783 -0
  96. package/src/directive/list.js +46 -0
  97. package/src/directive/list.md +22 -0
  98. package/src/directive/ngClass.js +249 -0
  99. package/src/directive/ngController.js +64 -0
  100. package/src/directive/ngCsp.js +82 -0
  101. package/src/directive/ngIf.js +134 -0
  102. package/src/directive/ngInclude.js +217 -0
  103. package/src/directive/ngModel.js +1356 -0
  104. package/src/directive/ngModelOptions.js +509 -0
  105. package/src/directive/ngOptions.js +670 -0
  106. package/src/directive/ngRef.js +90 -0
  107. package/src/directive/ngRepeat.js +650 -0
  108. package/src/directive/ngShowHide.js +255 -0
  109. package/src/directive/ngSwitch.js +178 -0
  110. package/src/directive/ngTransclude.js +98 -0
  111. package/src/directive/non-bindable.js +11 -0
  112. package/src/directive/non-bindable.md +17 -0
  113. package/src/directive/script.js +30 -0
  114. package/src/directive/select.js +624 -0
  115. package/src/directive/style.js +25 -0
  116. package/src/directive/style.md +23 -0
  117. package/src/directive/validators.js +329 -0
  118. package/src/exts/aria.js +544 -0
  119. package/src/exts/messages.js +852 -0
  120. package/src/filters/filter.js +207 -0
  121. package/src/filters/filter.md +69 -0
  122. package/src/filters/filters.js +239 -0
  123. package/src/filters/json.md +16 -0
  124. package/src/filters/limit-to.js +43 -0
  125. package/src/filters/limit-to.md +19 -0
  126. package/src/filters/order-by.js +183 -0
  127. package/src/filters/order-by.md +83 -0
  128. package/src/index.js +13 -0
  129. package/src/injector.js +1034 -0
  130. package/src/jqLite.js +1117 -0
  131. package/src/loader.js +1320 -0
  132. package/src/public.js +215 -0
  133. package/src/routeToRegExp.js +41 -0
  134. package/src/services/anchorScroll.js +135 -0
  135. package/src/services/browser.js +321 -0
  136. package/src/services/cacheFactory.js +398 -0
  137. package/src/services/cookieReader.js +72 -0
  138. package/src/services/document.js +64 -0
  139. package/src/services/http.js +1537 -0
  140. package/src/services/httpBackend.js +206 -0
  141. package/src/services/log.js +160 -0
  142. package/src/services/templateRequest.js +139 -0
  143. package/test/angular.spec.js +2153 -0
  144. package/test/aria/aria.spec.js +1245 -0
  145. package/test/binding.spec.js +504 -0
  146. package/test/build-test.html +14 -0
  147. package/test/injector.spec.js +2327 -0
  148. package/test/jasmine/jasmine-5.1.2/boot0.js +65 -0
  149. package/test/jasmine/jasmine-5.1.2/boot1.js +133 -0
  150. package/test/jasmine/jasmine-5.1.2/jasmine-html.js +963 -0
  151. package/test/jasmine/jasmine-5.1.2/jasmine.css +320 -0
  152. package/test/jasmine/jasmine-5.1.2/jasmine.js +10824 -0
  153. package/test/jasmine/jasmine-5.1.2/jasmine_favicon.png +0 -0
  154. package/test/jasmine/jasmine-browser.json +17 -0
  155. package/test/jasmine/jasmine.json +9 -0
  156. package/test/jqlite.spec.js +2133 -0
  157. package/test/loader.spec.js +219 -0
  158. package/test/messages/messages.spec.js +1146 -0
  159. package/test/min-err.spec.js +174 -0
  160. package/test/mock-test.html +13 -0
  161. package/test/module-test.html +15 -0
  162. package/test/ng/anomate.spec.js +606 -0
  163. package/test/ng/cache-factor.spec.js +334 -0
  164. package/test/ng/compile.spec.js +17956 -0
  165. package/test/ng/controller-provider.spec.js +227 -0
  166. package/test/ng/cookie-reader.spec.js +98 -0
  167. package/test/ng/directive/a.spec.js +192 -0
  168. package/test/ng/directive/bind.spec.js +334 -0
  169. package/test/ng/directive/boolean.spec.js +136 -0
  170. package/test/ng/directive/change.spec.js +71 -0
  171. package/test/ng/directive/class.spec.js +858 -0
  172. package/test/ng/directive/click.spec.js +38 -0
  173. package/test/ng/directive/cloak.spec.js +44 -0
  174. package/test/ng/directive/constoller.spec.js +194 -0
  175. package/test/ng/directive/element-style.spec.js +92 -0
  176. package/test/ng/directive/event.spec.js +282 -0
  177. package/test/ng/directive/form.spec.js +1518 -0
  178. package/test/ng/directive/href.spec.js +143 -0
  179. package/test/ng/directive/if.spec.js +402 -0
  180. package/test/ng/directive/include.spec.js +828 -0
  181. package/test/ng/directive/init.spec.js +68 -0
  182. package/test/ng/directive/input.spec.js +3810 -0
  183. package/test/ng/directive/list.spec.js +170 -0
  184. package/test/ng/directive/model-options.spec.js +1008 -0
  185. package/test/ng/directive/model.spec.js +1905 -0
  186. package/test/ng/directive/non-bindable.spec.js +55 -0
  187. package/test/ng/directive/options.spec.js +3583 -0
  188. package/test/ng/directive/ref.spec.js +575 -0
  189. package/test/ng/directive/repeat.spec.js +1675 -0
  190. package/test/ng/directive/script.spec.js +52 -0
  191. package/test/ng/directive/scrset.spec.js +67 -0
  192. package/test/ng/directive/select.spec.js +2541 -0
  193. package/test/ng/directive/show-hide.spec.js +253 -0
  194. package/test/ng/directive/src.spec.js +157 -0
  195. package/test/ng/directive/style.spec.js +178 -0
  196. package/test/ng/directive/switch.spec.js +647 -0
  197. package/test/ng/directive/validators.spec.js +717 -0
  198. package/test/ng/document.spec.js +52 -0
  199. package/test/ng/filter/filter.spec.js +714 -0
  200. package/test/ng/filter/filters.spec.js +35 -0
  201. package/test/ng/filter/limit-to.spec.js +251 -0
  202. package/test/ng/filter/order-by.spec.js +891 -0
  203. package/test/ng/filter.spec.js +149 -0
  204. package/test/ng/http-backend.spec.js +398 -0
  205. package/test/ng/http.spec.js +4071 -0
  206. package/test/ng/interpolate.spec.js +642 -0
  207. package/test/ng/interval.spec.js +343 -0
  208. package/test/ng/location.spec.js +3488 -0
  209. package/test/ng/on.spec.js +229 -0
  210. package/test/ng/parse.spec.js +4655 -0
  211. package/test/ng/prop.spec.js +805 -0
  212. package/test/ng/q.spec.js +2904 -0
  213. package/test/ng/root-element.spec.js +16 -0
  214. package/test/ng/sanitize-uri.spec.js +249 -0
  215. package/test/ng/sce.spec.js +660 -0
  216. package/test/ng/scope.spec.js +3442 -0
  217. package/test/ng/template-request.spec.js +236 -0
  218. package/test/ng/timeout.spec.js +351 -0
  219. package/test/ng/url-utils.spec.js +156 -0
  220. package/test/ng/utils.spec.js +144 -0
  221. package/test/original-test.html +21 -0
  222. package/test/public.spec.js +34 -0
  223. package/test/sanitize/bing-html.spec.js +36 -0
  224. package/test/server/express.js +158 -0
  225. package/test/test-utils.js +11 -0
  226. package/tsconfig.json +17 -0
  227. package/types/angular.d.ts +138 -0
  228. package/types/global.d.ts +9 -0
  229. package/types/index.d.ts +2357 -0
  230. package/types/jqlite.d.ts +558 -0
  231. package/vite.config.js +14 -0
@@ -0,0 +1,2562 @@
1
+ import {
2
+ createMap,
3
+ csp,
4
+ forEach,
5
+ identity,
6
+ isDefined,
7
+ isFunction,
8
+ minErr,
9
+ isString,
10
+ lowercase,
11
+ isNumber,
12
+ } from "../utils";
13
+
14
+ const $parseMinErr = minErr("$parse");
15
+
16
+ const objectValueOf = {}.constructor.prototype.valueOf;
17
+
18
+ /**
19
+ * Converts parameter to strings property name for use as keys in an object.
20
+ * Any non-string object, including a number, is typecasted into a string via the toString method.
21
+ * {@link https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Property_accessors#Property_names}
22
+ *
23
+ * @param {!any} name
24
+ * @returns {string}
25
+ */
26
+ function getStringValue(name) {
27
+ return `${name}`;
28
+ }
29
+
30
+ const OPERATORS = createMap();
31
+
32
+ "+ - * / % === !== == != < > <= >= && || ! = |"
33
+ .split(" ")
34
+ .forEach((operator) => (OPERATORS[operator] = true));
35
+
36
+ const ESCAPE = {
37
+ n: "\n",
38
+ f: "\f",
39
+ r: "\r",
40
+ t: "\t",
41
+ v: "\v",
42
+ "'": "'",
43
+ '"': '"',
44
+ };
45
+
46
+ /// //////////////////////////////////////
47
+
48
+ /**
49
+ * @constructor
50
+ */
51
+ export const Lexer = function Lexer(options) {
52
+ this.options = options;
53
+ };
54
+
55
+ Lexer.prototype = {
56
+ constructor: Lexer,
57
+
58
+ lex(text) {
59
+ this.text = text;
60
+ this.index = 0;
61
+ this.tokens = [];
62
+
63
+ while (this.index < this.text.length) {
64
+ const ch = this.text.charAt(this.index);
65
+ if (ch === '"' || ch === "'") {
66
+ this.readString(ch);
67
+ } else if (
68
+ this.isNumber(ch) ||
69
+ (ch === "." && this.isNumber(this.peek()))
70
+ ) {
71
+ this.readNumber();
72
+ } else if (this.isIdentifierStart(this.peekMultichar())) {
73
+ this.readIdent();
74
+ } else if (this.is(ch, "(){}[].,;:?")) {
75
+ this.tokens.push({ index: this.index, text: ch });
76
+ this.index++;
77
+ } else if (this.isWhitespace(ch)) {
78
+ this.index++;
79
+ } else {
80
+ const ch2 = ch + this.peek();
81
+ const ch3 = ch2 + this.peek(2);
82
+ const op1 = OPERATORS[ch];
83
+ const op2 = OPERATORS[ch2];
84
+ const op3 = OPERATORS[ch3];
85
+ if (op1 || op2 || op3) {
86
+ const token = op3 ? ch3 : op2 ? ch2 : ch;
87
+ this.tokens.push({ index: this.index, text: token, operator: true });
88
+ this.index += token.length;
89
+ } else {
90
+ this.throwError(
91
+ "Unexpected next character ",
92
+ this.index,
93
+ this.index + 1,
94
+ );
95
+ }
96
+ }
97
+ }
98
+ return this.tokens;
99
+ },
100
+
101
+ is(ch, chars) {
102
+ return chars.indexOf(ch) !== -1;
103
+ },
104
+
105
+ peek(i) {
106
+ const num = i || 1;
107
+ return this.index + num < this.text.length
108
+ ? this.text.charAt(this.index + num)
109
+ : false;
110
+ },
111
+
112
+ isNumber(ch) {
113
+ return ch >= "0" && ch <= "9" && typeof ch === "string";
114
+ },
115
+
116
+ isWhitespace(ch) {
117
+ // IE treats non-breaking space as \u00A0
118
+ return (
119
+ ch === " " ||
120
+ ch === "\r" ||
121
+ ch === "\t" ||
122
+ ch === "\n" ||
123
+ ch === "\v" ||
124
+ ch === "\u00A0"
125
+ );
126
+ },
127
+
128
+ isIdentifierStart(ch) {
129
+ return this.options.isIdentifierStart
130
+ ? this.options.isIdentifierStart(ch, this.codePointAt(ch))
131
+ : this.isValidIdentifierStart(ch);
132
+ },
133
+
134
+ isValidIdentifierStart(ch) {
135
+ return (
136
+ (ch >= "a" && ch <= "z") ||
137
+ (ch >= "A" && ch <= "Z") ||
138
+ ch === "_" ||
139
+ ch === "$"
140
+ );
141
+ },
142
+
143
+ isIdentifierContinue(ch) {
144
+ return this.options.isIdentifierContinue
145
+ ? this.options.isIdentifierContinue(ch, this.codePointAt(ch))
146
+ : this.isValidIdentifierContinue(ch);
147
+ },
148
+
149
+ isValidIdentifierContinue(ch) {
150
+ return this.isValidIdentifierStart(ch) || this.isNumber(ch);
151
+ },
152
+
153
+ codePointAt(ch) {
154
+ if (ch.length === 1) return ch.charCodeAt(0);
155
+ // eslint-disable-next-line no-bitwise
156
+ return (ch.charCodeAt(0) << 10) + ch.charCodeAt(1) - 0x35fdc00;
157
+ },
158
+
159
+ peekMultichar() {
160
+ const ch = this.text.charAt(this.index);
161
+ const peek = this.peek();
162
+ if (!peek) {
163
+ return ch;
164
+ }
165
+ const cp1 = ch.charCodeAt(0);
166
+ const cp2 = peek.charCodeAt(0);
167
+ if (cp1 >= 0xd800 && cp1 <= 0xdbff && cp2 >= 0xdc00 && cp2 <= 0xdfff) {
168
+ return ch + peek;
169
+ }
170
+ return ch;
171
+ },
172
+
173
+ isExpOperator(ch) {
174
+ return ch === "-" || ch === "+" || this.isNumber(ch);
175
+ },
176
+
177
+ throwError(error, start, end) {
178
+ end = end || this.index;
179
+ const colStr = isDefined(start)
180
+ ? `s ${start}-${this.index} [${this.text.substring(start, end)}]`
181
+ : ` ${end}`;
182
+ throw $parseMinErr(
183
+ "lexerr",
184
+ "Lexer Error: {0} at column{1} in expression [{2}].",
185
+ error,
186
+ colStr,
187
+ this.text,
188
+ );
189
+ },
190
+
191
+ readNumber() {
192
+ let number = "";
193
+ const start = this.index;
194
+ while (this.index < this.text.length) {
195
+ const ch = lowercase(this.text.charAt(this.index));
196
+ if (ch === "." || this.isNumber(ch)) {
197
+ number += ch;
198
+ } else {
199
+ const peekCh = this.peek();
200
+ if (ch === "e" && this.isExpOperator(peekCh)) {
201
+ number += ch;
202
+ } else if (
203
+ this.isExpOperator(ch) &&
204
+ peekCh &&
205
+ this.isNumber(peekCh) &&
206
+ number.charAt(number.length - 1) === "e"
207
+ ) {
208
+ number += ch;
209
+ } else if (
210
+ this.isExpOperator(ch) &&
211
+ (!peekCh || !this.isNumber(peekCh)) &&
212
+ number.charAt(number.length - 1) === "e"
213
+ ) {
214
+ this.throwError("Invalid exponent");
215
+ } else {
216
+ break;
217
+ }
218
+ }
219
+ this.index++;
220
+ }
221
+ this.tokens.push({
222
+ index: start,
223
+ text: number,
224
+ constant: true,
225
+ value: Number(number),
226
+ });
227
+ },
228
+
229
+ readIdent() {
230
+ const start = this.index;
231
+ this.index += this.peekMultichar().length;
232
+ while (this.index < this.text.length) {
233
+ const ch = this.peekMultichar();
234
+ if (!this.isIdentifierContinue(ch)) {
235
+ break;
236
+ }
237
+ this.index += ch.length;
238
+ }
239
+ this.tokens.push({
240
+ index: start,
241
+ text: this.text.slice(start, this.index),
242
+ identifier: true,
243
+ });
244
+ },
245
+
246
+ readString(quote) {
247
+ const start = this.index;
248
+ this.index++;
249
+ let string = "";
250
+ let rawString = quote;
251
+ let escape = false;
252
+ while (this.index < this.text.length) {
253
+ const ch = this.text.charAt(this.index);
254
+ rawString += ch;
255
+ if (escape) {
256
+ if (ch === "u") {
257
+ const hex = this.text.substring(this.index + 1, this.index + 5);
258
+ if (!hex.match(/[\da-f]{4}/i)) {
259
+ this.throwError(`Invalid unicode escape [\\u${hex}]`);
260
+ }
261
+ this.index += 4;
262
+ string += String.fromCharCode(parseInt(hex, 16));
263
+ } else {
264
+ const rep = ESCAPE[ch];
265
+ string += rep || ch;
266
+ }
267
+ escape = false;
268
+ } else if (ch === "\\") {
269
+ escape = true;
270
+ } else if (ch === quote) {
271
+ this.index++;
272
+ this.tokens.push({
273
+ index: start,
274
+ text: rawString,
275
+ constant: true,
276
+ value: string,
277
+ });
278
+ return;
279
+ } else {
280
+ string += ch;
281
+ }
282
+ this.index++;
283
+ }
284
+ this.throwError("Unterminated quote", start);
285
+ },
286
+ };
287
+
288
+ /**
289
+ * @typedef {("Program"|"ExpressionStatement"|"AssignmentExpression"|"ConditionalExpression"|"LogicalExpression"|"BinaryExpression"|"UnaryExpression"|"CallExpression"|"MemberExpression"|"Identifier"|"Literal"|"ArrayExpression"|"Property"|"ObjectExpression"|"ThisExpression"|"LocalsExpression"|"NGValueParameter")} ASTType
290
+ */
291
+ const ASTType = {
292
+ Program: "Program",
293
+ ExpressionStatement: "ExpressionStatement",
294
+ AssignmentExpression: "AssignmentExpression",
295
+ ConditionalExpression: "ConditionalExpression",
296
+ LogicalExpression: "LogicalExpression",
297
+ BinaryExpression: "BinaryExpression",
298
+ UnaryExpression: "UnaryExpression",
299
+ CallExpression: "CallExpression",
300
+ MemberExpression: "MemberExpression",
301
+ Identifier: "Identifier",
302
+ Literal: "Literal",
303
+ ArrayExpression: "ArrayExpression",
304
+ Property: "Property",
305
+ ObjectExpression: "ObjectExpression",
306
+ ThisExpression: "ThisExpression",
307
+ LocalsExpression: "LocalsExpression",
308
+ NGValueParameter: "NGValueParameter",
309
+ };
310
+
311
+ export function AST(lexer, options) {
312
+ this.lexer = lexer;
313
+ this.options = options;
314
+ }
315
+
316
+ AST.prototype = {
317
+ ast(text) {
318
+ this.text = text;
319
+ this.tokens = this.lexer.lex(text);
320
+
321
+ const value = this.program();
322
+
323
+ if (this.tokens.length !== 0) {
324
+ this.throwError("is an unexpected token", this.tokens[0]);
325
+ }
326
+
327
+ return value;
328
+ },
329
+
330
+ program() {
331
+ const body = [];
332
+ let hasMore = true;
333
+ while (hasMore) {
334
+ if (this.tokens.length > 0 && !this.peek("}", ")", ";", "]"))
335
+ body.push(this.expressionStatement());
336
+ if (!this.expect(";")) {
337
+ hasMore = false;
338
+ }
339
+ }
340
+ return { type: ASTType.Program, body };
341
+ },
342
+
343
+ expressionStatement() {
344
+ return {
345
+ type: ASTType.ExpressionStatement,
346
+ expression: this.filterChain(),
347
+ };
348
+ },
349
+
350
+ filterChain() {
351
+ let left = this.expression();
352
+ while (this.expect("|")) {
353
+ left = this.filter(left);
354
+ }
355
+ return left;
356
+ },
357
+
358
+ expression() {
359
+ return this.assignment();
360
+ },
361
+
362
+ assignment() {
363
+ let result = this.ternary();
364
+ if (this.expect("=")) {
365
+ if (!isAssignable(result)) {
366
+ throw $parseMinErr("lval", "Trying to assign a value to a non l-value");
367
+ }
368
+
369
+ result = {
370
+ type: ASTType.AssignmentExpression,
371
+ left: result,
372
+ right: this.assignment(),
373
+ operator: "=",
374
+ };
375
+ }
376
+ return result;
377
+ },
378
+
379
+ ternary() {
380
+ const test = this.logicalOR();
381
+ let alternate;
382
+ let consequent;
383
+ if (this.expect("?")) {
384
+ alternate = this.expression();
385
+ if (this.consume(":")) {
386
+ consequent = this.expression();
387
+ return {
388
+ type: ASTType.ConditionalExpression,
389
+ test,
390
+ alternate,
391
+ consequent,
392
+ };
393
+ }
394
+ }
395
+ return test;
396
+ },
397
+
398
+ logicalOR() {
399
+ let left = this.logicalAND();
400
+ while (this.expect("||")) {
401
+ left = {
402
+ type: ASTType.LogicalExpression,
403
+ operator: "||",
404
+ left,
405
+ right: this.logicalAND(),
406
+ };
407
+ }
408
+ return left;
409
+ },
410
+
411
+ logicalAND() {
412
+ let left = this.equality();
413
+ while (this.expect("&&")) {
414
+ left = {
415
+ type: ASTType.LogicalExpression,
416
+ operator: "&&",
417
+ left,
418
+ right: this.equality(),
419
+ };
420
+ }
421
+ return left;
422
+ },
423
+
424
+ equality() {
425
+ let left = this.relational();
426
+ let token;
427
+ while ((token = this.expect("==", "!=", "===", "!=="))) {
428
+ left = {
429
+ type: ASTType.BinaryExpression,
430
+ operator: token.text,
431
+ left,
432
+ right: this.relational(),
433
+ };
434
+ }
435
+ return left;
436
+ },
437
+
438
+ relational() {
439
+ let left = this.additive();
440
+ let token;
441
+ while ((token = this.expect("<", ">", "<=", ">="))) {
442
+ left = {
443
+ type: ASTType.BinaryExpression,
444
+ operator: token.text,
445
+ left,
446
+ right: this.additive(),
447
+ };
448
+ }
449
+ return left;
450
+ },
451
+
452
+ additive() {
453
+ let left = this.multiplicative();
454
+ let token;
455
+ while ((token = this.expect("+", "-"))) {
456
+ left = {
457
+ type: ASTType.BinaryExpression,
458
+ operator: token.text,
459
+ left,
460
+ right: this.multiplicative(),
461
+ };
462
+ }
463
+ return left;
464
+ },
465
+
466
+ multiplicative() {
467
+ let left = this.unary();
468
+ let token;
469
+ while ((token = this.expect("*", "/", "%"))) {
470
+ left = {
471
+ type: ASTType.BinaryExpression,
472
+ operator: token.text,
473
+ left,
474
+ right: this.unary(),
475
+ };
476
+ }
477
+ return left;
478
+ },
479
+
480
+ unary() {
481
+ let token;
482
+ if ((token = this.expect("+", "-", "!"))) {
483
+ return {
484
+ type: ASTType.UnaryExpression,
485
+ operator: token.text,
486
+ prefix: true,
487
+ argument: this.unary(),
488
+ };
489
+ }
490
+ return this.primary();
491
+ },
492
+
493
+ primary() {
494
+ let primary;
495
+ if (this.expect("(")) {
496
+ primary = this.filterChain();
497
+ this.consume(")");
498
+ } else if (this.expect("[")) {
499
+ primary = this.arrayDeclaration();
500
+ } else if (this.expect("{")) {
501
+ primary = this.object();
502
+ } else if (
503
+ Object.prototype.hasOwnProperty.call(
504
+ this.selfReferential,
505
+ this.peek().text,
506
+ )
507
+ ) {
508
+ primary = structuredClone(this.selfReferential[this.consume().text]);
509
+ } else if (
510
+ Object.prototype.hasOwnProperty.call(
511
+ this.options.literals,
512
+ this.peek().text,
513
+ )
514
+ ) {
515
+ primary = {
516
+ type: ASTType.Literal,
517
+ value: this.options.literals[this.consume().text],
518
+ };
519
+ } else if (this.peek().identifier) {
520
+ primary = this.identifier();
521
+ } else if (this.peek().constant) {
522
+ primary = this.constant();
523
+ } else {
524
+ this.throwError("not a primary expression", this.peek());
525
+ }
526
+
527
+ let next;
528
+ while ((next = this.expect("(", "[", "."))) {
529
+ if (next.text === "(") {
530
+ primary = {
531
+ type: ASTType.CallExpression,
532
+ callee: primary,
533
+ arguments: this.parseArguments(),
534
+ };
535
+ this.consume(")");
536
+ } else if (next.text === "[") {
537
+ primary = {
538
+ type: ASTType.MemberExpression,
539
+ object: primary,
540
+ property: this.expression(),
541
+ computed: true,
542
+ };
543
+ this.consume("]");
544
+ } else if (next.text === ".") {
545
+ primary = {
546
+ type: ASTType.MemberExpression,
547
+ object: primary,
548
+ property: this.identifier(),
549
+ computed: false,
550
+ };
551
+ } else {
552
+ this.throwError("IMPOSSIBLE");
553
+ }
554
+ }
555
+ return primary;
556
+ },
557
+
558
+ filter(baseExpression) {
559
+ const args = [baseExpression];
560
+ const result = {
561
+ type: ASTType.CallExpression,
562
+ callee: this.identifier(),
563
+ arguments: args,
564
+ filter: true,
565
+ };
566
+
567
+ while (this.expect(":")) {
568
+ args.push(this.expression());
569
+ }
570
+
571
+ return result;
572
+ },
573
+
574
+ parseArguments() {
575
+ const args = [];
576
+ if (this.peekToken().text !== ")") {
577
+ do {
578
+ args.push(this.filterChain());
579
+ } while (this.expect(","));
580
+ }
581
+ return args;
582
+ },
583
+
584
+ identifier() {
585
+ const token = this.consume();
586
+ if (!token.identifier) {
587
+ this.throwError("is not a valid identifier", token);
588
+ }
589
+ return { type: ASTType.Identifier, name: token.text };
590
+ },
591
+
592
+ constant() {
593
+ // TODO check that it is a constant
594
+ return { type: ASTType.Literal, value: this.consume().value };
595
+ },
596
+
597
+ arrayDeclaration() {
598
+ const elements = [];
599
+ if (this.peekToken().text !== "]") {
600
+ do {
601
+ if (this.peek("]")) {
602
+ // Support trailing commas per ES5.1.
603
+ break;
604
+ }
605
+ elements.push(this.expression());
606
+ } while (this.expect(","));
607
+ }
608
+ this.consume("]");
609
+
610
+ return { type: ASTType.ArrayExpression, elements };
611
+ },
612
+
613
+ object() {
614
+ const properties = [];
615
+ let property;
616
+ if (this.peekToken().text !== "}") {
617
+ do {
618
+ if (this.peek("}")) {
619
+ // Support trailing commas per ES5.1.
620
+ break;
621
+ }
622
+ property = { type: ASTType.Property, kind: "init" };
623
+ if (this.peek().constant) {
624
+ property.key = this.constant();
625
+ property.computed = false;
626
+ this.consume(":");
627
+ property.value = this.expression();
628
+ } else if (this.peek().identifier) {
629
+ property.key = this.identifier();
630
+ property.computed = false;
631
+ if (this.peek(":")) {
632
+ this.consume(":");
633
+ property.value = this.expression();
634
+ } else {
635
+ property.value = property.key;
636
+ }
637
+ } else if (this.peek("[")) {
638
+ this.consume("[");
639
+ property.key = this.expression();
640
+ this.consume("]");
641
+ property.computed = true;
642
+ this.consume(":");
643
+ property.value = this.expression();
644
+ } else {
645
+ this.throwError("invalid key", this.peek());
646
+ }
647
+ properties.push(property);
648
+ } while (this.expect(","));
649
+ }
650
+ this.consume("}");
651
+
652
+ return { type: ASTType.ObjectExpression, properties };
653
+ },
654
+
655
+ throwError(msg, token) {
656
+ throw $parseMinErr(
657
+ "syntax",
658
+ "Syntax Error: Token '{0}' {1} at column {2} of the expression [{3}] starting at [{4}].",
659
+ token.text,
660
+ msg,
661
+ token.index + 1,
662
+ this.text,
663
+ this.text.substring(token.index),
664
+ );
665
+ },
666
+
667
+ consume(e1) {
668
+ if (this.tokens.length === 0) {
669
+ throw $parseMinErr(
670
+ "ueoe",
671
+ "Unexpected end of expression: {0}",
672
+ this.text,
673
+ );
674
+ }
675
+
676
+ const token = this.expect(e1);
677
+ if (!token) {
678
+ this.throwError(`is unexpected, expecting [${e1}]`, this.peek());
679
+ }
680
+ return token;
681
+ },
682
+
683
+ peekToken() {
684
+ if (this.tokens.length === 0) {
685
+ throw $parseMinErr(
686
+ "ueoe",
687
+ "Unexpected end of expression: {0}",
688
+ this.text,
689
+ );
690
+ }
691
+ return this.tokens[0];
692
+ },
693
+
694
+ peek(e1, e2, e3, e4) {
695
+ return this.peekAhead(0, e1, e2, e3, e4);
696
+ },
697
+
698
+ peekAhead(i, e1, e2, e3, e4) {
699
+ if (this.tokens.length > i) {
700
+ const token = this.tokens[i];
701
+ const t = token.text;
702
+ if (
703
+ t === e1 ||
704
+ t === e2 ||
705
+ t === e3 ||
706
+ t === e4 ||
707
+ (!e1 && !e2 && !e3 && !e4)
708
+ ) {
709
+ return token;
710
+ }
711
+ }
712
+ return false;
713
+ },
714
+
715
+ expect(e1, e2, e3, e4) {
716
+ const token = this.peek(e1, e2, e3, e4);
717
+ if (token) {
718
+ this.tokens.shift();
719
+ return token;
720
+ }
721
+ return false;
722
+ },
723
+
724
+ selfReferential: {
725
+ this: { type: ASTType.ThisExpression },
726
+ $locals: { type: ASTType.LocalsExpression },
727
+ },
728
+ };
729
+
730
+ function ifDefined(v, d) {
731
+ return typeof v !== "undefined" ? v : d;
732
+ }
733
+
734
+ function plusFn(l, r) {
735
+ if (typeof l === "undefined") return r;
736
+ if (typeof r === "undefined") return l;
737
+ return l + r;
738
+ }
739
+
740
+ function isStateless($filter, filterName) {
741
+ const fn = $filter(filterName);
742
+ return !fn.$stateful;
743
+ }
744
+
745
+ const PURITY_ABSOLUTE = 1;
746
+ const PURITY_RELATIVE = 2;
747
+
748
+ // Detect nodes which could depend on non-shallow state of objects
749
+ function isPure(node, parentIsPure) {
750
+ switch (node.type) {
751
+ // Computed members might invoke a stateful toString()
752
+ case ASTType.MemberExpression:
753
+ if (node.computed) {
754
+ return false;
755
+ }
756
+ break;
757
+
758
+ // Unary always convert to primative
759
+ case ASTType.UnaryExpression:
760
+ return PURITY_ABSOLUTE;
761
+
762
+ // The binary + operator can invoke a stateful toString().
763
+ case ASTType.BinaryExpression:
764
+ return node.operator !== "+" ? PURITY_ABSOLUTE : false;
765
+
766
+ // Functions / filters probably read state from within objects
767
+ case ASTType.CallExpression:
768
+ return false;
769
+ }
770
+
771
+ return undefined === parentIsPure ? PURITY_RELATIVE : parentIsPure;
772
+ }
773
+
774
+ function findConstantAndWatchExpressions(ast, $filter, parentIsPure) {
775
+ let allConstants;
776
+ let argsToWatch;
777
+ let isStatelessFilter;
778
+
779
+ const astIsPure = (ast.isPure = isPure(ast, parentIsPure));
780
+
781
+ switch (ast.type) {
782
+ case ASTType.Program:
783
+ allConstants = true;
784
+ forEach(ast.body, (expr) => {
785
+ findConstantAndWatchExpressions(expr.expression, $filter, astIsPure);
786
+ allConstants = allConstants && expr.expression.constant;
787
+ });
788
+ ast.constant = allConstants;
789
+ break;
790
+ case ASTType.Literal:
791
+ ast.constant = true;
792
+ ast.toWatch = [];
793
+ break;
794
+ case ASTType.UnaryExpression:
795
+ findConstantAndWatchExpressions(ast.argument, $filter, astIsPure);
796
+ ast.constant = ast.argument.constant;
797
+ ast.toWatch = ast.argument.toWatch;
798
+ break;
799
+ case ASTType.BinaryExpression:
800
+ findConstantAndWatchExpressions(ast.left, $filter, astIsPure);
801
+ findConstantAndWatchExpressions(ast.right, $filter, astIsPure);
802
+ ast.constant = ast.left.constant && ast.right.constant;
803
+ ast.toWatch = ast.left.toWatch.concat(ast.right.toWatch);
804
+ break;
805
+ case ASTType.LogicalExpression:
806
+ findConstantAndWatchExpressions(ast.left, $filter, astIsPure);
807
+ findConstantAndWatchExpressions(ast.right, $filter, astIsPure);
808
+ ast.constant = ast.left.constant && ast.right.constant;
809
+ ast.toWatch = ast.constant ? [] : [ast];
810
+ break;
811
+ case ASTType.ConditionalExpression:
812
+ findConstantAndWatchExpressions(ast.test, $filter, astIsPure);
813
+ findConstantAndWatchExpressions(ast.alternate, $filter, astIsPure);
814
+ findConstantAndWatchExpressions(ast.consequent, $filter, astIsPure);
815
+ ast.constant =
816
+ ast.test.constant && ast.alternate.constant && ast.consequent.constant;
817
+ ast.toWatch = ast.constant ? [] : [ast];
818
+ break;
819
+ case ASTType.Identifier:
820
+ ast.constant = false;
821
+ ast.toWatch = [ast];
822
+ break;
823
+ case ASTType.MemberExpression:
824
+ findConstantAndWatchExpressions(ast.object, $filter, astIsPure);
825
+ if (ast.computed) {
826
+ findConstantAndWatchExpressions(ast.property, $filter, astIsPure);
827
+ }
828
+ ast.constant =
829
+ ast.object.constant && (!ast.computed || ast.property.constant);
830
+ ast.toWatch = ast.constant ? [] : [ast];
831
+ break;
832
+ case ASTType.CallExpression:
833
+ isStatelessFilter = ast.filter
834
+ ? isStateless($filter, ast.callee.name)
835
+ : false;
836
+ allConstants = isStatelessFilter;
837
+ argsToWatch = [];
838
+ forEach(ast.arguments, (expr) => {
839
+ findConstantAndWatchExpressions(expr, $filter, astIsPure);
840
+ allConstants = allConstants && expr.constant;
841
+ argsToWatch.push.apply(argsToWatch, expr.toWatch);
842
+ });
843
+ ast.constant = allConstants;
844
+ ast.toWatch = isStatelessFilter ? argsToWatch : [ast];
845
+ break;
846
+ case ASTType.AssignmentExpression:
847
+ findConstantAndWatchExpressions(ast.left, $filter, astIsPure);
848
+ findConstantAndWatchExpressions(ast.right, $filter, astIsPure);
849
+ ast.constant = ast.left.constant && ast.right.constant;
850
+ ast.toWatch = [ast];
851
+ break;
852
+ case ASTType.ArrayExpression:
853
+ allConstants = true;
854
+ argsToWatch = [];
855
+ forEach(ast.elements, (expr) => {
856
+ findConstantAndWatchExpressions(expr, $filter, astIsPure);
857
+ allConstants = allConstants && expr.constant;
858
+ argsToWatch.push.apply(argsToWatch, expr.toWatch);
859
+ });
860
+ ast.constant = allConstants;
861
+ ast.toWatch = argsToWatch;
862
+ break;
863
+ case ASTType.ObjectExpression:
864
+ allConstants = true;
865
+ argsToWatch = [];
866
+ forEach(ast.properties, (property) => {
867
+ findConstantAndWatchExpressions(property.value, $filter, astIsPure);
868
+ allConstants = allConstants && property.value.constant;
869
+ argsToWatch.push.apply(argsToWatch, property.value.toWatch);
870
+ if (property.computed) {
871
+ // `{[key]: value}` implicitly does `key.toString()` which may be non-pure
872
+ findConstantAndWatchExpressions(
873
+ property.key,
874
+ $filter,
875
+ /* parentIsPure= */ false,
876
+ );
877
+ allConstants = allConstants && property.key.constant;
878
+ argsToWatch.push.apply(argsToWatch, property.key.toWatch);
879
+ }
880
+ });
881
+ ast.constant = allConstants;
882
+ ast.toWatch = argsToWatch;
883
+ break;
884
+ case ASTType.ThisExpression:
885
+ ast.constant = false;
886
+ ast.toWatch = [];
887
+ break;
888
+ case ASTType.LocalsExpression:
889
+ ast.constant = false;
890
+ ast.toWatch = [];
891
+ break;
892
+ }
893
+ }
894
+
895
+ function getInputs(body) {
896
+ if (body.length !== 1) return;
897
+ const lastExpression = body[0].expression;
898
+ const candidate = lastExpression.toWatch;
899
+ if (candidate.length !== 1) return candidate;
900
+ return candidate[0] !== lastExpression ? candidate : undefined;
901
+ }
902
+
903
+ function isAssignable(ast) {
904
+ return (
905
+ ast.type === ASTType.Identifier || ast.type === ASTType.MemberExpression
906
+ );
907
+ }
908
+
909
+ function assignableAST(ast) {
910
+ if (ast.body.length === 1 && isAssignable(ast.body[0].expression)) {
911
+ return {
912
+ type: ASTType.AssignmentExpression,
913
+ left: ast.body[0].expression,
914
+ right: { type: ASTType.NGValueParameter },
915
+ operator: "=",
916
+ };
917
+ }
918
+ }
919
+
920
+ function isLiteral(ast) {
921
+ return (
922
+ ast.body.length === 0 ||
923
+ (ast.body.length === 1 &&
924
+ (ast.body[0].expression.type === ASTType.Literal ||
925
+ ast.body[0].expression.type === ASTType.ArrayExpression ||
926
+ ast.body[0].expression.type === ASTType.ObjectExpression))
927
+ );
928
+ }
929
+
930
+ function isConstant(ast) {
931
+ return ast.constant;
932
+ }
933
+
934
+ function ASTCompiler($filter) {
935
+ this.$filter = $filter;
936
+ }
937
+
938
+ ASTCompiler.prototype = {
939
+ compile(ast) {
940
+ const self = this;
941
+ this.state = {
942
+ nextId: 0,
943
+ filters: {},
944
+ fn: { vars: [], body: [], own: {} },
945
+ assign: { vars: [], body: [], own: {} },
946
+ inputs: [],
947
+ };
948
+ findConstantAndWatchExpressions(ast, self.$filter);
949
+ let extra = "";
950
+ let assignable;
951
+ this.stage = "assign";
952
+ if ((assignable = assignableAST(ast))) {
953
+ this.state.computing = "assign";
954
+ const result = this.nextId();
955
+ this.recurse(assignable, result);
956
+ this.return_(result);
957
+ extra = `fn.assign=${this.generateFunction("assign", "s,v,l")}`;
958
+ }
959
+ const toWatch = getInputs(ast.body);
960
+ self.stage = "inputs";
961
+ forEach(toWatch, (watch, key) => {
962
+ const fnKey = `fn${key}`;
963
+ self.state[fnKey] = { vars: [], body: [], own: {} };
964
+ self.state.computing = fnKey;
965
+ const intoId = self.nextId();
966
+ self.recurse(watch, intoId);
967
+ self.return_(intoId);
968
+ self.state.inputs.push({ name: fnKey, isPure: watch.isPure });
969
+ watch.watchId = key;
970
+ });
971
+ this.state.computing = "fn";
972
+ this.stage = "main";
973
+ this.recurse(ast);
974
+ const fnString = `\n${this.filterPrefix()}let fn=${this.generateFunction(
975
+ "fn",
976
+ "s,l,a,i",
977
+ )}${extra}${this.watchFns()}return fn;`;
978
+
979
+ // eslint-disable-next-line no-new-func
980
+ const fn = new Function(
981
+ "$filter",
982
+ "getStringValue",
983
+ "ifDefined",
984
+ "plus",
985
+ fnString,
986
+ )(this.$filter, getStringValue, ifDefined, plusFn);
987
+ this.state = this.stage = undefined;
988
+ return fn;
989
+ },
990
+
991
+ watchFns() {
992
+ const result = [];
993
+ const { inputs } = this.state;
994
+ const self = this;
995
+ forEach(inputs, (input) => {
996
+ result.push(
997
+ `let ${input.name}=${self.generateFunction(input.name, "s")}`,
998
+ );
999
+ if (input.isPure) {
1000
+ result.push(input.name, `.isPure=${JSON.stringify(input.isPure)};`);
1001
+ }
1002
+ });
1003
+ if (inputs.length) {
1004
+ result.push(`fn.inputs=[${inputs.map((i) => i.name).join(",")}];`);
1005
+ }
1006
+ return result.join("");
1007
+ },
1008
+
1009
+ generateFunction(name, params) {
1010
+ return `function(${params}){${this.varsPrefix(name)}${this.body(name)}};`;
1011
+ },
1012
+
1013
+ filterPrefix() {
1014
+ const parts = [];
1015
+ const self = this;
1016
+ forEach(this.state.filters, (id, filter) => {
1017
+ parts.push(`${id}=$filter(${self.escape(filter)})`);
1018
+ });
1019
+ if (parts.length) return `let ${parts.join(",")};`;
1020
+ return "";
1021
+ },
1022
+
1023
+ varsPrefix(section) {
1024
+ return this.state[section].vars.length
1025
+ ? `let ${this.state[section].vars.join(",")};`
1026
+ : "";
1027
+ },
1028
+
1029
+ body(section) {
1030
+ return this.state[section].body.join("");
1031
+ },
1032
+
1033
+ recurse(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
1034
+ let left;
1035
+ let right;
1036
+ const self = this;
1037
+ let args;
1038
+ let expression;
1039
+ let computed;
1040
+ recursionFn = recursionFn || (() => {});
1041
+ if (!skipWatchIdCheck && isDefined(ast.watchId)) {
1042
+ intoId = intoId || this.nextId();
1043
+ this.if_(
1044
+ "i",
1045
+ this.lazyAssign(intoId, this.computedMember("i", ast.watchId)),
1046
+ this.lazyRecurse(ast, intoId, nameId, recursionFn, create, true),
1047
+ );
1048
+ return;
1049
+ }
1050
+ switch (ast.type) {
1051
+ case ASTType.Program:
1052
+ forEach(ast.body, (expression, pos) => {
1053
+ self.recurse(expression.expression, undefined, undefined, (expr) => {
1054
+ right = expr;
1055
+ });
1056
+ if (pos !== ast.body.length - 1) {
1057
+ self.current().body.push(right, ";");
1058
+ } else {
1059
+ self.return_(right);
1060
+ }
1061
+ });
1062
+ break;
1063
+ case ASTType.Literal:
1064
+ expression = this.escape(ast.value);
1065
+ this.assign(intoId, expression);
1066
+ recursionFn(intoId || expression);
1067
+ break;
1068
+ case ASTType.UnaryExpression:
1069
+ this.recurse(ast.argument, undefined, undefined, (expr) => {
1070
+ right = expr;
1071
+ });
1072
+ expression = `${ast.operator}(${this.ifDefined(right, 0)})`;
1073
+ this.assign(intoId, expression);
1074
+ recursionFn(expression);
1075
+ break;
1076
+ case ASTType.BinaryExpression:
1077
+ this.recurse(ast.left, undefined, undefined, (expr) => {
1078
+ left = expr;
1079
+ });
1080
+ this.recurse(ast.right, undefined, undefined, (expr) => {
1081
+ right = expr;
1082
+ });
1083
+ if (ast.operator === "+") {
1084
+ expression = this.plus(left, right);
1085
+ } else if (ast.operator === "-") {
1086
+ expression =
1087
+ this.ifDefined(left, 0) + ast.operator + this.ifDefined(right, 0);
1088
+ } else {
1089
+ expression = `(${left})${ast.operator}(${right})`;
1090
+ }
1091
+ this.assign(intoId, expression);
1092
+ recursionFn(expression);
1093
+ break;
1094
+ case ASTType.LogicalExpression:
1095
+ intoId = intoId || this.nextId();
1096
+ self.recurse(ast.left, intoId);
1097
+ self.if_(
1098
+ ast.operator === "&&" ? intoId : self.not(intoId),
1099
+ self.lazyRecurse(ast.right, intoId),
1100
+ );
1101
+ recursionFn(intoId);
1102
+ break;
1103
+ case ASTType.ConditionalExpression:
1104
+ intoId = intoId || this.nextId();
1105
+ self.recurse(ast.test, intoId);
1106
+ self.if_(
1107
+ intoId,
1108
+ self.lazyRecurse(ast.alternate, intoId),
1109
+ self.lazyRecurse(ast.consequent, intoId),
1110
+ );
1111
+ recursionFn(intoId);
1112
+ break;
1113
+ case ASTType.Identifier:
1114
+ intoId = intoId || this.nextId();
1115
+ if (nameId) {
1116
+ nameId.context =
1117
+ self.stage === "inputs"
1118
+ ? "s"
1119
+ : this.assign(
1120
+ this.nextId(),
1121
+ `${this.getHasOwnProperty("l", ast.name)}?l:s`,
1122
+ );
1123
+ nameId.computed = false;
1124
+ nameId.name = ast.name;
1125
+ }
1126
+ self.if_(
1127
+ self.stage === "inputs" ||
1128
+ self.not(self.getHasOwnProperty("l", ast.name)),
1129
+ () => {
1130
+ self.if_(self.stage === "inputs" || "s", () => {
1131
+ if (create && create !== 1) {
1132
+ self.if_(
1133
+ self.isNull(self.nonComputedMember("s", ast.name)),
1134
+ self.lazyAssign(self.nonComputedMember("s", ast.name), "{}"),
1135
+ );
1136
+ }
1137
+ self.assign(intoId, self.nonComputedMember("s", ast.name));
1138
+ });
1139
+ },
1140
+ intoId &&
1141
+ self.lazyAssign(intoId, self.nonComputedMember("l", ast.name)),
1142
+ );
1143
+ recursionFn(intoId);
1144
+ break;
1145
+ case ASTType.MemberExpression:
1146
+ left = (nameId && (nameId.context = this.nextId())) || this.nextId();
1147
+ intoId = intoId || this.nextId();
1148
+ self.recurse(
1149
+ ast.object,
1150
+ left,
1151
+ undefined,
1152
+ () => {
1153
+ self.if_(
1154
+ self.notNull(left),
1155
+ () => {
1156
+ if (ast.computed) {
1157
+ right = self.nextId();
1158
+ self.recurse(ast.property, right);
1159
+ self.getStringValue(right);
1160
+ if (create && create !== 1) {
1161
+ self.if_(
1162
+ self.not(self.computedMember(left, right)),
1163
+ self.lazyAssign(self.computedMember(left, right), "{}"),
1164
+ );
1165
+ }
1166
+ expression = self.computedMember(left, right);
1167
+ self.assign(intoId, expression);
1168
+ if (nameId) {
1169
+ nameId.computed = true;
1170
+ nameId.name = right;
1171
+ }
1172
+ } else {
1173
+ if (create && create !== 1) {
1174
+ self.if_(
1175
+ self.isNull(
1176
+ self.nonComputedMember(left, ast.property.name),
1177
+ ),
1178
+ self.lazyAssign(
1179
+ self.nonComputedMember(left, ast.property.name),
1180
+ "{}",
1181
+ ),
1182
+ );
1183
+ }
1184
+ expression = self.nonComputedMember(left, ast.property.name);
1185
+ self.assign(intoId, expression);
1186
+ if (nameId) {
1187
+ nameId.computed = false;
1188
+ nameId.name = ast.property.name;
1189
+ }
1190
+ }
1191
+ },
1192
+ () => {
1193
+ self.assign(intoId, "undefined");
1194
+ },
1195
+ );
1196
+ recursionFn(intoId);
1197
+ },
1198
+ !!create,
1199
+ );
1200
+ break;
1201
+ case ASTType.CallExpression:
1202
+ intoId = intoId || this.nextId();
1203
+ if (ast.filter) {
1204
+ right = self.filter(ast.callee.name);
1205
+ args = [];
1206
+ forEach(ast.arguments, (expr) => {
1207
+ const argument = self.nextId();
1208
+ self.recurse(expr, argument);
1209
+ args.push(argument);
1210
+ });
1211
+ expression = `${right}(${args.join(",")})`;
1212
+ self.assign(intoId, expression);
1213
+ recursionFn(intoId);
1214
+ } else {
1215
+ right = self.nextId();
1216
+ left = {};
1217
+ args = [];
1218
+ self.recurse(ast.callee, right, left, () => {
1219
+ self.if_(
1220
+ self.notNull(right),
1221
+ () => {
1222
+ forEach(ast.arguments, (expr) => {
1223
+ self.recurse(
1224
+ expr,
1225
+ ast.constant ? undefined : self.nextId(),
1226
+ undefined,
1227
+ (argument) => {
1228
+ args.push(argument);
1229
+ },
1230
+ );
1231
+ });
1232
+ if (left.name) {
1233
+ expression = `${self.member(
1234
+ left.context,
1235
+ left.name,
1236
+ left.computed,
1237
+ )}(${args.join(",")})`;
1238
+ } else {
1239
+ expression = `${right}(${args.join(",")})`;
1240
+ }
1241
+ self.assign(intoId, expression);
1242
+ },
1243
+ () => {
1244
+ self.assign(intoId, "undefined");
1245
+ },
1246
+ );
1247
+ recursionFn(intoId);
1248
+ });
1249
+ }
1250
+ break;
1251
+ case ASTType.AssignmentExpression:
1252
+ right = this.nextId();
1253
+ left = {};
1254
+ this.recurse(
1255
+ ast.left,
1256
+ undefined,
1257
+ left,
1258
+ () => {
1259
+ self.if_(self.notNull(left.context), () => {
1260
+ self.recurse(ast.right, right);
1261
+ expression =
1262
+ self.member(left.context, left.name, left.computed) +
1263
+ ast.operator +
1264
+ right;
1265
+ self.assign(intoId, expression);
1266
+ recursionFn(intoId || expression);
1267
+ });
1268
+ },
1269
+ 1,
1270
+ );
1271
+ break;
1272
+ case ASTType.ArrayExpression:
1273
+ args = [];
1274
+ forEach(ast.elements, (expr) => {
1275
+ self.recurse(
1276
+ expr,
1277
+ ast.constant ? undefined : self.nextId(),
1278
+ undefined,
1279
+ (argument) => {
1280
+ args.push(argument);
1281
+ },
1282
+ );
1283
+ });
1284
+ expression = `[${args.join(",")}]`;
1285
+ this.assign(intoId, expression);
1286
+ recursionFn(intoId || expression);
1287
+ break;
1288
+ case ASTType.ObjectExpression:
1289
+ args = [];
1290
+ computed = false;
1291
+ forEach(ast.properties, (property) => {
1292
+ if (property.computed) {
1293
+ computed = true;
1294
+ }
1295
+ });
1296
+ if (computed) {
1297
+ intoId = intoId || this.nextId();
1298
+ this.assign(intoId, "{}");
1299
+ forEach(ast.properties, (property) => {
1300
+ if (property.computed) {
1301
+ left = self.nextId();
1302
+ self.recurse(property.key, left);
1303
+ } else {
1304
+ left =
1305
+ property.key.type === ASTType.Identifier
1306
+ ? property.key.name
1307
+ : `${property.key.value}`;
1308
+ }
1309
+ right = self.nextId();
1310
+ self.recurse(property.value, right);
1311
+ self.assign(self.member(intoId, left, property.computed), right);
1312
+ });
1313
+ } else {
1314
+ forEach(ast.properties, (property) => {
1315
+ self.recurse(
1316
+ property.value,
1317
+ ast.constant ? undefined : self.nextId(),
1318
+ undefined,
1319
+ (expr) => {
1320
+ args.push(
1321
+ `${self.escape(
1322
+ property.key.type === ASTType.Identifier
1323
+ ? property.key.name
1324
+ : `${property.key.value}`,
1325
+ )}:${expr}`,
1326
+ );
1327
+ },
1328
+ );
1329
+ });
1330
+ expression = `{${args.join(",")}}`;
1331
+ this.assign(intoId, expression);
1332
+ }
1333
+ recursionFn(intoId || expression);
1334
+ break;
1335
+ case ASTType.ThisExpression:
1336
+ this.assign(intoId, "s");
1337
+ recursionFn(intoId || "s");
1338
+ break;
1339
+ case ASTType.LocalsExpression:
1340
+ this.assign(intoId, "l");
1341
+ recursionFn(intoId || "l");
1342
+ break;
1343
+ case ASTType.NGValueParameter:
1344
+ this.assign(intoId, "v");
1345
+ recursionFn(intoId || "v");
1346
+ break;
1347
+ }
1348
+ },
1349
+
1350
+ getHasOwnProperty(element, property) {
1351
+ const key = `${element}.${property}`;
1352
+ const { own } = this.current();
1353
+ if (!Object.prototype.hasOwnProperty.call(own, key)) {
1354
+ own[key] = this.nextId(
1355
+ false,
1356
+ `${element}&&(${this.escape(property)} in ${element})`,
1357
+ );
1358
+ }
1359
+ return own[key];
1360
+ },
1361
+
1362
+ assign(id, value) {
1363
+ if (!id) return;
1364
+ this.current().body.push(id, "=", value, ";");
1365
+ return id;
1366
+ },
1367
+
1368
+ filter(filterName) {
1369
+ if (!Object.prototype.hasOwnProperty.call(this.state.filters, filterName)) {
1370
+ this.state.filters[filterName] = this.nextId(true);
1371
+ }
1372
+ return this.state.filters[filterName];
1373
+ },
1374
+
1375
+ ifDefined(id, defaultValue) {
1376
+ return `ifDefined(${id},${this.escape(defaultValue)})`;
1377
+ },
1378
+
1379
+ plus(left, right) {
1380
+ return `plus(${left},${right})`;
1381
+ },
1382
+
1383
+ return_(id) {
1384
+ this.current().body.push("return ", id, ";");
1385
+ },
1386
+
1387
+ if_(test, alternate, consequent) {
1388
+ if (test === true) {
1389
+ alternate();
1390
+ } else {
1391
+ const { body } = this.current();
1392
+ body.push("if(", test, "){");
1393
+ alternate();
1394
+ body.push("}");
1395
+ if (consequent) {
1396
+ body.push("else{");
1397
+ consequent();
1398
+ body.push("}");
1399
+ }
1400
+ }
1401
+ },
1402
+
1403
+ not(expression) {
1404
+ return `!(${expression})`;
1405
+ },
1406
+
1407
+ isNull(expression) {
1408
+ return `${expression}==null`;
1409
+ },
1410
+
1411
+ notNull(expression) {
1412
+ return `${expression}!=null`;
1413
+ },
1414
+
1415
+ nonComputedMember(left, right) {
1416
+ const SAFE_IDENTIFIER = /^[$_a-zA-Z][$_a-zA-Z0-9]*$/;
1417
+ const UNSAFE_CHARACTERS = /[^$_a-zA-Z0-9]/g;
1418
+ if (SAFE_IDENTIFIER.test(right)) {
1419
+ return `${left}.${right}`;
1420
+ }
1421
+ return `${left}["${right.replace(
1422
+ UNSAFE_CHARACTERS,
1423
+ this.stringEscapeFn,
1424
+ )}"]`;
1425
+ },
1426
+
1427
+ computedMember(left, right) {
1428
+ return `${left}[${right}]`;
1429
+ },
1430
+
1431
+ member(left, right, computed) {
1432
+ if (computed) return this.computedMember(left, right);
1433
+ return this.nonComputedMember(left, right);
1434
+ },
1435
+
1436
+ getStringValue(item) {
1437
+ this.assign(item, `getStringValue(${item})`);
1438
+ },
1439
+
1440
+ lazyRecurse(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
1441
+ const self = this;
1442
+ return function () {
1443
+ self.recurse(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck);
1444
+ };
1445
+ },
1446
+
1447
+ lazyAssign(id, value) {
1448
+ const self = this;
1449
+ return function () {
1450
+ self.assign(id, value);
1451
+ };
1452
+ },
1453
+
1454
+ stringEscapeRegex: /[^ a-zA-Z0-9]/g,
1455
+
1456
+ stringEscapeFn(c) {
1457
+ return `\\u${`0000${c.charCodeAt(0).toString(16)}`.slice(-4)}`;
1458
+ },
1459
+
1460
+ escape(value) {
1461
+ if (isString(value))
1462
+ return `'${value.replace(this.stringEscapeRegex, this.stringEscapeFn)}'`;
1463
+ if (isNumber(value)) return value.toString();
1464
+ if (value === true) return "true";
1465
+ if (value === false) return "false";
1466
+ if (value === null) return "null";
1467
+ if (typeof value === "undefined") return "undefined";
1468
+
1469
+ throw $parseMinErr("esc", "IMPOSSIBLE");
1470
+ },
1471
+
1472
+ nextId(skip, init) {
1473
+ const id = `v${this.state.nextId++}`;
1474
+ if (!skip) {
1475
+ this.current().vars.push(id + (init ? `=${init}` : ""));
1476
+ }
1477
+ return id;
1478
+ },
1479
+
1480
+ current() {
1481
+ return this.state[this.state.computing];
1482
+ },
1483
+ };
1484
+
1485
+ function ASTInterpreter($filter) {
1486
+ this.$filter = $filter;
1487
+ }
1488
+
1489
+ ASTInterpreter.prototype = {
1490
+ compile(ast) {
1491
+ const self = this;
1492
+ findConstantAndWatchExpressions(ast, self.$filter);
1493
+ let assignable;
1494
+ let assign;
1495
+ if ((assignable = assignableAST(ast))) {
1496
+ assign = this.recurse(assignable);
1497
+ }
1498
+ const toWatch = getInputs(ast.body);
1499
+ let inputs;
1500
+ if (toWatch) {
1501
+ inputs = [];
1502
+ forEach(toWatch, (watch, key) => {
1503
+ const input = self.recurse(watch);
1504
+ input.isPure = watch.isPure;
1505
+ watch.input = input;
1506
+ inputs.push(input);
1507
+ watch.watchId = key;
1508
+ });
1509
+ }
1510
+ const expressions = [];
1511
+ forEach(ast.body, (expression) => {
1512
+ expressions.push(self.recurse(expression.expression));
1513
+ });
1514
+ const fn =
1515
+ ast.body.length === 0
1516
+ ? () => {}
1517
+ : ast.body.length === 1
1518
+ ? expressions[0]
1519
+ : function (scope, locals) {
1520
+ let lastValue;
1521
+ forEach(expressions, (exp) => {
1522
+ lastValue = exp(scope, locals);
1523
+ });
1524
+ return lastValue;
1525
+ };
1526
+ if (assign) {
1527
+ fn.assign = function (scope, value, locals) {
1528
+ return assign(scope, locals, value);
1529
+ };
1530
+ }
1531
+ if (inputs) {
1532
+ fn.inputs = inputs;
1533
+ }
1534
+ return fn;
1535
+ },
1536
+
1537
+ recurse(ast, context, create) {
1538
+ let left;
1539
+ let right;
1540
+ const self = this;
1541
+ let args;
1542
+ if (ast.input) {
1543
+ return this.inputs(ast.input, ast.watchId);
1544
+ }
1545
+ switch (ast.type) {
1546
+ case ASTType.Literal:
1547
+ return this.value(ast.value, context);
1548
+ case ASTType.UnaryExpression:
1549
+ right = this.recurse(ast.argument);
1550
+ return this[`unary${ast.operator}`](right, context);
1551
+ case ASTType.BinaryExpression:
1552
+ left = this.recurse(ast.left);
1553
+ right = this.recurse(ast.right);
1554
+ return this[`binary${ast.operator}`](left, right, context);
1555
+ case ASTType.LogicalExpression:
1556
+ left = this.recurse(ast.left);
1557
+ right = this.recurse(ast.right);
1558
+ return this[`binary${ast.operator}`](left, right, context);
1559
+ case ASTType.ConditionalExpression:
1560
+ return this["ternary?:"](
1561
+ this.recurse(ast.test),
1562
+ this.recurse(ast.alternate),
1563
+ this.recurse(ast.consequent),
1564
+ context,
1565
+ );
1566
+ case ASTType.Identifier:
1567
+ return self.identifier(ast.name, context, create);
1568
+ case ASTType.MemberExpression:
1569
+ left = this.recurse(ast.object, false, !!create);
1570
+ if (!ast.computed) {
1571
+ right = ast.property.name;
1572
+ }
1573
+ if (ast.computed) right = this.recurse(ast.property);
1574
+ return ast.computed
1575
+ ? this.computedMember(left, right, context, create)
1576
+ : this.nonComputedMember(left, right, context, create);
1577
+ case ASTType.CallExpression:
1578
+ args = [];
1579
+ forEach(ast.arguments, (expr) => {
1580
+ args.push(self.recurse(expr));
1581
+ });
1582
+ if (ast.filter) right = this.$filter(ast.callee.name);
1583
+ if (!ast.filter) right = this.recurse(ast.callee, true);
1584
+ return ast.filter
1585
+ ? function (scope, locals, assign, inputs) {
1586
+ const values = [];
1587
+ for (let i = 0; i < args.length; ++i) {
1588
+ values.push(args[i](scope, locals, assign, inputs));
1589
+ }
1590
+ const value = right.apply(undefined, values, inputs);
1591
+ return context
1592
+ ? { context: undefined, name: undefined, value }
1593
+ : value;
1594
+ }
1595
+ : function (scope, locals, assign, inputs) {
1596
+ const rhs = right(scope, locals, assign, inputs);
1597
+ let value;
1598
+ if (rhs.value != null) {
1599
+ const values = [];
1600
+ for (let i = 0; i < args.length; ++i) {
1601
+ values.push(args[i](scope, locals, assign, inputs));
1602
+ }
1603
+ value = rhs.value.apply(rhs.context, values);
1604
+ }
1605
+ return context ? { value } : value;
1606
+ };
1607
+ case ASTType.AssignmentExpression:
1608
+ left = this.recurse(ast.left, true, 1);
1609
+ right = this.recurse(ast.right);
1610
+ return function (scope, locals, assign, inputs) {
1611
+ const lhs = left(scope, locals, assign, inputs);
1612
+ const rhs = right(scope, locals, assign, inputs);
1613
+ lhs.context[lhs.name] = rhs;
1614
+ return context ? { value: rhs } : rhs;
1615
+ };
1616
+ case ASTType.ArrayExpression:
1617
+ args = [];
1618
+ forEach(ast.elements, (expr) => {
1619
+ args.push(self.recurse(expr));
1620
+ });
1621
+ return function (scope, locals, assign, inputs) {
1622
+ const value = [];
1623
+ for (let i = 0; i < args.length; ++i) {
1624
+ value.push(args[i](scope, locals, assign, inputs));
1625
+ }
1626
+ return context ? { value } : value;
1627
+ };
1628
+ case ASTType.ObjectExpression:
1629
+ args = [];
1630
+ forEach(ast.properties, (property) => {
1631
+ if (property.computed) {
1632
+ args.push({
1633
+ key: self.recurse(property.key),
1634
+ computed: true,
1635
+ value: self.recurse(property.value),
1636
+ });
1637
+ } else {
1638
+ args.push({
1639
+ key:
1640
+ property.key.type === ASTType.Identifier
1641
+ ? property.key.name
1642
+ : `${property.key.value}`,
1643
+ computed: false,
1644
+ value: self.recurse(property.value),
1645
+ });
1646
+ }
1647
+ });
1648
+ return function (scope, locals, assign, inputs) {
1649
+ const value = {};
1650
+ for (let i = 0; i < args.length; ++i) {
1651
+ if (args[i].computed) {
1652
+ value[args[i].key(scope, locals, assign, inputs)] = args[i].value(
1653
+ scope,
1654
+ locals,
1655
+ assign,
1656
+ inputs,
1657
+ );
1658
+ } else {
1659
+ value[args[i].key] = args[i].value(scope, locals, assign, inputs);
1660
+ }
1661
+ }
1662
+ return context ? { value } : value;
1663
+ };
1664
+ case ASTType.ThisExpression:
1665
+ return function (scope) {
1666
+ return context ? { value: scope } : scope;
1667
+ };
1668
+ case ASTType.LocalsExpression:
1669
+ return function (scope, locals) {
1670
+ return context ? { value: locals } : locals;
1671
+ };
1672
+ case ASTType.NGValueParameter:
1673
+ return function (scope, locals, assign) {
1674
+ return context ? { value: assign } : assign;
1675
+ };
1676
+ }
1677
+ },
1678
+
1679
+ "unary+": function (argument, context) {
1680
+ return function (scope, locals, assign, inputs) {
1681
+ let arg = argument(scope, locals, assign, inputs);
1682
+ if (isDefined(arg)) {
1683
+ arg = +arg;
1684
+ } else {
1685
+ arg = 0;
1686
+ }
1687
+ return context ? { value: arg } : arg;
1688
+ };
1689
+ },
1690
+ "unary-": function (argument, context) {
1691
+ return function (scope, locals, assign, inputs) {
1692
+ let arg = argument(scope, locals, assign, inputs);
1693
+ if (isDefined(arg)) {
1694
+ arg = -arg;
1695
+ } else {
1696
+ arg = -0;
1697
+ }
1698
+ return context ? { value: arg } : arg;
1699
+ };
1700
+ },
1701
+ "unary!": function (argument, context) {
1702
+ return function (scope, locals, assign, inputs) {
1703
+ const arg = !argument(scope, locals, assign, inputs);
1704
+ return context ? { value: arg } : arg;
1705
+ };
1706
+ },
1707
+ "binary+": function (left, right, context) {
1708
+ return function (scope, locals, assign, inputs) {
1709
+ const lhs = left(scope, locals, assign, inputs);
1710
+ const rhs = right(scope, locals, assign, inputs);
1711
+ const arg = plusFn(lhs, rhs);
1712
+ return context ? { value: arg } : arg;
1713
+ };
1714
+ },
1715
+ "binary-": function (left, right, context) {
1716
+ return function (scope, locals, assign, inputs) {
1717
+ const lhs = left(scope, locals, assign, inputs);
1718
+ const rhs = right(scope, locals, assign, inputs);
1719
+ const arg = (isDefined(lhs) ? lhs : 0) - (isDefined(rhs) ? rhs : 0);
1720
+ return context ? { value: arg } : arg;
1721
+ };
1722
+ },
1723
+ "binary*": function (left, right, context) {
1724
+ return function (scope, locals, assign, inputs) {
1725
+ const arg =
1726
+ left(scope, locals, assign, inputs) *
1727
+ right(scope, locals, assign, inputs);
1728
+ return context ? { value: arg } : arg;
1729
+ };
1730
+ },
1731
+ "binary/": function (left, right, context) {
1732
+ return function (scope, locals, assign, inputs) {
1733
+ const arg =
1734
+ left(scope, locals, assign, inputs) /
1735
+ right(scope, locals, assign, inputs);
1736
+ return context ? { value: arg } : arg;
1737
+ };
1738
+ },
1739
+ "binary%": function (left, right, context) {
1740
+ return function (scope, locals, assign, inputs) {
1741
+ const arg =
1742
+ left(scope, locals, assign, inputs) %
1743
+ right(scope, locals, assign, inputs);
1744
+ return context ? { value: arg } : arg;
1745
+ };
1746
+ },
1747
+ "binary===": function (left, right, context) {
1748
+ return function (scope, locals, assign, inputs) {
1749
+ const arg =
1750
+ left(scope, locals, assign, inputs) ===
1751
+ right(scope, locals, assign, inputs);
1752
+ return context ? { value: arg } : arg;
1753
+ };
1754
+ },
1755
+ "binary!==": function (left, right, context) {
1756
+ return function (scope, locals, assign, inputs) {
1757
+ const arg =
1758
+ left(scope, locals, assign, inputs) !==
1759
+ right(scope, locals, assign, inputs);
1760
+ return context ? { value: arg } : arg;
1761
+ };
1762
+ },
1763
+ "binary==": function (left, right, context) {
1764
+ return function (scope, locals, assign, inputs) {
1765
+ const arg =
1766
+ left(scope, locals, assign, inputs) ==
1767
+ right(scope, locals, assign, inputs);
1768
+ return context ? { value: arg } : arg;
1769
+ };
1770
+ },
1771
+ "binary!=": function (left, right, context) {
1772
+ return function (scope, locals, assign, inputs) {
1773
+ const arg =
1774
+ left(scope, locals, assign, inputs) !=
1775
+ right(scope, locals, assign, inputs);
1776
+ return context ? { value: arg } : arg;
1777
+ };
1778
+ },
1779
+ "binary<": function (left, right, context) {
1780
+ return function (scope, locals, assign, inputs) {
1781
+ const arg =
1782
+ left(scope, locals, assign, inputs) <
1783
+ right(scope, locals, assign, inputs);
1784
+ return context ? { value: arg } : arg;
1785
+ };
1786
+ },
1787
+ "binary>": function (left, right, context) {
1788
+ return function (scope, locals, assign, inputs) {
1789
+ const arg =
1790
+ left(scope, locals, assign, inputs) >
1791
+ right(scope, locals, assign, inputs);
1792
+ return context ? { value: arg } : arg;
1793
+ };
1794
+ },
1795
+ "binary<=": function (left, right, context) {
1796
+ return function (scope, locals, assign, inputs) {
1797
+ const arg =
1798
+ left(scope, locals, assign, inputs) <=
1799
+ right(scope, locals, assign, inputs);
1800
+ return context ? { value: arg } : arg;
1801
+ };
1802
+ },
1803
+ "binary>=": function (left, right, context) {
1804
+ return function (scope, locals, assign, inputs) {
1805
+ const arg =
1806
+ left(scope, locals, assign, inputs) >=
1807
+ right(scope, locals, assign, inputs);
1808
+ return context ? { value: arg } : arg;
1809
+ };
1810
+ },
1811
+ "binary&&": function (left, right, context) {
1812
+ return function (scope, locals, assign, inputs) {
1813
+ const arg =
1814
+ left(scope, locals, assign, inputs) &&
1815
+ right(scope, locals, assign, inputs);
1816
+ return context ? { value: arg } : arg;
1817
+ };
1818
+ },
1819
+ "binary||": function (left, right, context) {
1820
+ return function (scope, locals, assign, inputs) {
1821
+ const arg =
1822
+ left(scope, locals, assign, inputs) ||
1823
+ right(scope, locals, assign, inputs);
1824
+ return context ? { value: arg } : arg;
1825
+ };
1826
+ },
1827
+ "ternary?:": function (test, alternate, consequent, context) {
1828
+ return function (scope, locals, assign, inputs) {
1829
+ const arg = test(scope, locals, assign, inputs)
1830
+ ? alternate(scope, locals, assign, inputs)
1831
+ : consequent(scope, locals, assign, inputs);
1832
+ return context ? { value: arg } : arg;
1833
+ };
1834
+ },
1835
+ value(value, context) {
1836
+ return function () {
1837
+ return context ? { context: undefined, name: undefined, value } : value;
1838
+ };
1839
+ },
1840
+ identifier(name, context, create) {
1841
+ return function (scope, locals) {
1842
+ const base = locals && name in locals ? locals : scope;
1843
+ if (create && create !== 1 && base && base[name] == null) {
1844
+ base[name] = {};
1845
+ }
1846
+ const value = base ? base[name] : undefined;
1847
+ if (context) {
1848
+ return { context: base, name, value };
1849
+ }
1850
+ return value;
1851
+ };
1852
+ },
1853
+ computedMember(left, right, context, create) {
1854
+ return function (scope, locals, assign, inputs) {
1855
+ const lhs = left(scope, locals, assign, inputs);
1856
+ let rhs;
1857
+ let value;
1858
+ if (lhs != null) {
1859
+ rhs = right(scope, locals, assign, inputs);
1860
+ rhs = getStringValue(rhs);
1861
+ if (create && create !== 1) {
1862
+ if (lhs && !lhs[rhs]) {
1863
+ lhs[rhs] = {};
1864
+ }
1865
+ }
1866
+ value = lhs[rhs];
1867
+ }
1868
+ if (context) {
1869
+ return { context: lhs, name: rhs, value };
1870
+ }
1871
+ return value;
1872
+ };
1873
+ },
1874
+ nonComputedMember(left, right, context, create) {
1875
+ return function (scope, locals, assign, inputs) {
1876
+ const lhs = left(scope, locals, assign, inputs);
1877
+ if (create && create !== 1) {
1878
+ if (lhs && lhs[right] == null) {
1879
+ lhs[right] = {};
1880
+ }
1881
+ }
1882
+ const value = lhs != null ? lhs[right] : undefined;
1883
+ if (context) {
1884
+ return { context: lhs, name: right, value };
1885
+ }
1886
+ return value;
1887
+ };
1888
+ },
1889
+ inputs(input, watchId) {
1890
+ return function (scope, value, locals, inputs) {
1891
+ if (inputs) return inputs[watchId];
1892
+ return input(scope, value, locals);
1893
+ };
1894
+ },
1895
+ };
1896
+
1897
+ /**
1898
+ * @constructor
1899
+ */
1900
+ class Parser {
1901
+ constructor(lexer, $filter, options) {
1902
+ this.ast = new AST(lexer, options);
1903
+ this.astCompiler = options.csp
1904
+ ? new ASTInterpreter($filter)
1905
+ : new ASTCompiler($filter);
1906
+ }
1907
+
1908
+ parse(text) {
1909
+ const { ast, oneTime } = this.getAst(text);
1910
+ const fn = this.astCompiler.compile(ast);
1911
+ fn.literal = isLiteral(ast);
1912
+ fn.constant = isConstant(ast);
1913
+ fn.oneTime = oneTime;
1914
+ return fn;
1915
+ }
1916
+
1917
+ getAst(exp) {
1918
+ let oneTime = false;
1919
+ exp = exp.trim();
1920
+
1921
+ if (exp.startsWith("::")) {
1922
+ oneTime = true;
1923
+ exp = exp.substring(2);
1924
+ }
1925
+ return {
1926
+ ast: this.ast.ast(exp),
1927
+ oneTime,
1928
+ };
1929
+ }
1930
+ }
1931
+
1932
+ function getValueOf(value) {
1933
+ return isFunction(value.valueOf)
1934
+ ? value.valueOf()
1935
+ : objectValueOf.call(value);
1936
+ }
1937
+
1938
+ /// ////////////////////////////////
1939
+
1940
+ /**
1941
+ * @ngdoc service
1942
+ * @name $parse
1943
+ * @kind function
1944
+ *
1945
+ * @description
1946
+ *
1947
+ * Converts AngularJS {@link guide/expression expression} into a function.
1948
+ *
1949
+ * ```js
1950
+ * let getter = $parse('user.name');
1951
+ * let setter = getter.assign;
1952
+ * let context = {user:{name:'AngularJS'}};
1953
+ * let locals = {user:{name:'local'}};
1954
+ *
1955
+ * expect(getter(context)).toEqual('AngularJS');
1956
+ * setter(context, 'newValue');
1957
+ * expect(context.user.name).toEqual('newValue');
1958
+ * expect(getter(context, locals)).toEqual('local');
1959
+ * ```
1960
+ *
1961
+ *
1962
+ * @param {string} expression String expression to compile.
1963
+ * @returns {function(context, locals)} a function which represents the compiled expression:
1964
+ *
1965
+ * * `context` – `{object}` – an object against which any expressions embedded in the strings
1966
+ * are evaluated against (typically a scope object).
1967
+ * * `locals` – `{object=}` – local variables context object, useful for overriding values in
1968
+ * `context`.
1969
+ *
1970
+ * The returned function also has the following properties:
1971
+ * * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript
1972
+ * literal.
1973
+ * * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
1974
+ * constant literals.
1975
+ * * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
1976
+ * set to a function to change its value on the given context.
1977
+ *
1978
+ */
1979
+
1980
+ export const literals = {
1981
+ true: true,
1982
+ false: false,
1983
+ null: null,
1984
+ undefined,
1985
+ };
1986
+
1987
+ /**
1988
+ * @ngdoc provider
1989
+ * @name $parseProvider
1990
+ *
1991
+ *
1992
+ * @description
1993
+ * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
1994
+ * service.
1995
+ */
1996
+ export function $ParseProvider() {
1997
+ const cache = createMap();
1998
+ const literals = {
1999
+ true: true,
2000
+ false: false,
2001
+ null: null,
2002
+ undefined: undefined,
2003
+ };
2004
+ var identStart, identContinue;
2005
+
2006
+ /**
2007
+ * @ngdoc method
2008
+ * @name $parseProvider#addLiteral
2009
+ * @description
2010
+ *
2011
+ * Configure $parse service to add literal values that will be present as literal at expressions.
2012
+ *
2013
+ * @param {string} literalName Token for the literal value. The literal name value must be a valid literal name.
2014
+ * @param {*} literalValue Value for this literal. All literal values must be primitives or `undefined`.
2015
+ *
2016
+ **/
2017
+ this.addLiteral = function (literalName, literalValue) {
2018
+ literals[literalName] = literalValue;
2019
+ };
2020
+
2021
+ /**
2022
+ * @ngdoc method
2023
+ * @name $parseProvider#setIdentifierFns
2024
+ *
2025
+ * @description
2026
+ *
2027
+ * Allows defining the set of characters that are allowed in AngularJS expressions. The function
2028
+ * `identifierStart` will get called to know if a given character is a valid character to be the
2029
+ * first character for an identifier. The function `identifierContinue` will get called to know if
2030
+ * a given character is a valid character to be a follow-up identifier character. The functions
2031
+ * `identifierStart` and `identifierContinue` will receive as arguments the single character to be
2032
+ * identifier and the character code point. These arguments will be `string` and `numeric`. Keep in
2033
+ * mind that the `string` parameter can be two characters long depending on the character
2034
+ * representation. It is expected for the function to return `true` or `false`, whether that
2035
+ * character is allowed or not.
2036
+ *
2037
+ * Since this function will be called extensively, keep the implementation of these functions fast,
2038
+ * as the performance of these functions have a direct impact on the expressions parsing speed.
2039
+ *
2040
+ * @param {function=} identifierStart The function that will decide whether the given character is
2041
+ * a valid identifier start character.
2042
+ * @param {function=} identifierContinue The function that will decide whether the given character is
2043
+ * a valid identifier continue character.
2044
+ */
2045
+ this.setIdentifierFns = function (identifierStart, identifierContinue) {
2046
+ identStart = identifierStart;
2047
+ identContinue = identifierContinue;
2048
+ return this;
2049
+ };
2050
+
2051
+ this.$get = [
2052
+ "$filter",
2053
+ function ($filter) {
2054
+ var noUnsafeEval = csp().noUnsafeEval;
2055
+ var $parseOptions = {
2056
+ csp: noUnsafeEval,
2057
+ literals: structuredClone(literals),
2058
+ isIdentifierStart: isFunction(identStart) && identStart,
2059
+ isIdentifierContinue: isFunction(identContinue) && identContinue,
2060
+ };
2061
+ $parse.$$getAst = $$getAst;
2062
+ return $parse;
2063
+
2064
+ function $parse(exp, interceptorFn) {
2065
+ var parsedExpression, cacheKey;
2066
+
2067
+ switch (typeof exp) {
2068
+ case "string":
2069
+ exp = exp.trim();
2070
+ cacheKey = exp;
2071
+
2072
+ parsedExpression = cache[cacheKey];
2073
+
2074
+ if (!parsedExpression) {
2075
+ var lexer = new Lexer($parseOptions);
2076
+ var parser = new Parser(lexer, $filter, $parseOptions);
2077
+ parsedExpression = parser.parse(exp);
2078
+
2079
+ cache[cacheKey] = addWatchDelegate(parsedExpression);
2080
+ }
2081
+ return addInterceptor(parsedExpression, interceptorFn);
2082
+
2083
+ case "function":
2084
+ return addInterceptor(exp, interceptorFn);
2085
+
2086
+ default:
2087
+ return addInterceptor(() => {}, interceptorFn);
2088
+ }
2089
+ }
2090
+
2091
+ function $$getAst(exp) {
2092
+ var lexer = new Lexer($parseOptions);
2093
+ var parser = new Parser(lexer, $filter, $parseOptions);
2094
+ return parser.getAst(exp).ast;
2095
+ }
2096
+
2097
+ function expressionInputDirtyCheck(
2098
+ newValue,
2099
+ oldValueOfValue,
2100
+ compareObjectIdentity,
2101
+ ) {
2102
+ if (newValue == null || oldValueOfValue == null) {
2103
+ // null/undefined
2104
+ return newValue === oldValueOfValue;
2105
+ }
2106
+
2107
+ if (typeof newValue === "object") {
2108
+ // attempt to convert the value to a primitive type
2109
+ // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
2110
+ // be cheaply dirty-checked
2111
+ newValue = getValueOf(newValue);
2112
+
2113
+ if (typeof newValue === "object" && !compareObjectIdentity) {
2114
+ // objects/arrays are not supported - deep-watching them would be too expensive
2115
+ return false;
2116
+ }
2117
+
2118
+ // fall-through to the primitive equality check
2119
+ }
2120
+
2121
+ //Primitive or NaN
2122
+ // eslint-disable-next-line no-self-compare
2123
+ return (
2124
+ newValue === oldValueOfValue ||
2125
+ (newValue !== newValue && oldValueOfValue !== oldValueOfValue)
2126
+ );
2127
+ }
2128
+
2129
+ function inputsWatchDelegate(
2130
+ scope,
2131
+ listener,
2132
+ objectEquality,
2133
+ parsedExpression,
2134
+ ) {
2135
+ var inputExpressions = parsedExpression.inputs;
2136
+ var lastResult;
2137
+
2138
+ if (inputExpressions.length === 1) {
2139
+ var oldInputValueOf = expressionInputDirtyCheck; // init to something unique so that equals check fails
2140
+ inputExpressions = inputExpressions[0];
2141
+ return scope.$watch(
2142
+ function expressionInputWatch(scope) {
2143
+ var newInputValue = inputExpressions(scope);
2144
+ if (
2145
+ !expressionInputDirtyCheck(
2146
+ newInputValue,
2147
+ oldInputValueOf,
2148
+ inputExpressions.isPure,
2149
+ )
2150
+ ) {
2151
+ lastResult = parsedExpression(scope, undefined, undefined, [
2152
+ newInputValue,
2153
+ ]);
2154
+ oldInputValueOf = newInputValue && getValueOf(newInputValue);
2155
+ }
2156
+ return lastResult;
2157
+ },
2158
+ listener,
2159
+ objectEquality,
2160
+ );
2161
+ }
2162
+
2163
+ var oldInputValueOfValues = [];
2164
+ var oldInputValues = [];
2165
+ for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
2166
+ oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails
2167
+ oldInputValues[i] = null;
2168
+ }
2169
+
2170
+ return scope.$watch(
2171
+ function expressionInputsWatch(scope) {
2172
+ var changed = false;
2173
+
2174
+ for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
2175
+ var newInputValue = inputExpressions[i](scope);
2176
+ if (
2177
+ changed ||
2178
+ (changed = !expressionInputDirtyCheck(
2179
+ newInputValue,
2180
+ oldInputValueOfValues[i],
2181
+ inputExpressions[i].isPure,
2182
+ ))
2183
+ ) {
2184
+ oldInputValues[i] = newInputValue;
2185
+ oldInputValueOfValues[i] =
2186
+ newInputValue && getValueOf(newInputValue);
2187
+ }
2188
+ }
2189
+
2190
+ if (changed) {
2191
+ lastResult = parsedExpression(
2192
+ scope,
2193
+ undefined,
2194
+ undefined,
2195
+ oldInputValues,
2196
+ );
2197
+ }
2198
+
2199
+ return lastResult;
2200
+ },
2201
+ listener,
2202
+ objectEquality,
2203
+ );
2204
+ }
2205
+
2206
+ function oneTimeWatchDelegate(
2207
+ scope,
2208
+ listener,
2209
+ objectEquality,
2210
+ parsedExpression,
2211
+ ) {
2212
+ var isDone = parsedExpression.literal ? isAllDefined : isDefined;
2213
+ var unwatch, lastValue;
2214
+
2215
+ var exp = parsedExpression.$$intercepted || parsedExpression;
2216
+ var post = parsedExpression.$$interceptor || identity;
2217
+
2218
+ var useInputs = parsedExpression.inputs && !exp.inputs;
2219
+
2220
+ // Propagate the literal/inputs/constant attributes
2221
+ // ... but not oneTime since we are handling it
2222
+ oneTimeWatch.literal = parsedExpression.literal;
2223
+ oneTimeWatch.constant = parsedExpression.constant;
2224
+ oneTimeWatch.inputs = parsedExpression.inputs;
2225
+
2226
+ // Allow other delegates to run on this wrapped expression
2227
+ addWatchDelegate(oneTimeWatch);
2228
+
2229
+ unwatch = scope.$watch(oneTimeWatch, listener, objectEquality);
2230
+
2231
+ return unwatch;
2232
+
2233
+ function unwatchIfDone() {
2234
+ if (isDone(lastValue)) {
2235
+ unwatch();
2236
+ }
2237
+ }
2238
+
2239
+ function oneTimeWatch(scope, locals, assign, inputs) {
2240
+ lastValue =
2241
+ useInputs && inputs
2242
+ ? inputs[0]
2243
+ : exp(scope, locals, assign, inputs);
2244
+ if (isDone(lastValue)) {
2245
+ scope.$$postDigest(unwatchIfDone);
2246
+ }
2247
+ return post(lastValue);
2248
+ }
2249
+ }
2250
+
2251
+ function isAllDefined(value) {
2252
+ var allDefined = true;
2253
+ forEach(value, function (val) {
2254
+ if (!isDefined(val)) allDefined = false;
2255
+ });
2256
+ return allDefined;
2257
+ }
2258
+
2259
+ function constantWatchDelegate(
2260
+ scope,
2261
+ listener,
2262
+ objectEquality,
2263
+ parsedExpression,
2264
+ ) {
2265
+ var unwatch = scope.$watch(
2266
+ function constantWatch(scope) {
2267
+ unwatch();
2268
+ return parsedExpression(scope);
2269
+ },
2270
+ listener,
2271
+ objectEquality,
2272
+ );
2273
+ return unwatch;
2274
+ }
2275
+
2276
+ function addWatchDelegate(parsedExpression) {
2277
+ if (parsedExpression.constant) {
2278
+ parsedExpression.$$watchDelegate = constantWatchDelegate;
2279
+ } else if (parsedExpression.oneTime) {
2280
+ parsedExpression.$$watchDelegate = oneTimeWatchDelegate;
2281
+ } else if (parsedExpression.inputs) {
2282
+ parsedExpression.$$watchDelegate = inputsWatchDelegate;
2283
+ }
2284
+
2285
+ return parsedExpression;
2286
+ }
2287
+
2288
+ function chainInterceptors(first, second) {
2289
+ function chainedInterceptor(value) {
2290
+ return second(first(value));
2291
+ }
2292
+ chainedInterceptor.$stateful = first.$stateful || second.$stateful;
2293
+ chainedInterceptor.$$pure = first.$$pure && second.$$pure;
2294
+
2295
+ return chainedInterceptor;
2296
+ }
2297
+
2298
+ function addInterceptor(parsedExpression, interceptorFn) {
2299
+ if (!interceptorFn) return parsedExpression;
2300
+
2301
+ // Extract any existing interceptors out of the parsedExpression
2302
+ // to ensure the original parsedExpression is always the $$intercepted
2303
+ if (parsedExpression.$$interceptor) {
2304
+ interceptorFn = chainInterceptors(
2305
+ parsedExpression.$$interceptor,
2306
+ interceptorFn,
2307
+ );
2308
+ parsedExpression = parsedExpression.$$intercepted;
2309
+ }
2310
+
2311
+ var useInputs = false;
2312
+
2313
+ var fn = function interceptedExpression(scope, locals, assign, inputs) {
2314
+ var value =
2315
+ useInputs && inputs
2316
+ ? inputs[0]
2317
+ : parsedExpression(scope, locals, assign, inputs);
2318
+ return interceptorFn(value);
2319
+ };
2320
+
2321
+ // Maintain references to the interceptor/intercepted
2322
+ fn.$$intercepted = parsedExpression;
2323
+ fn.$$interceptor = interceptorFn;
2324
+
2325
+ // Propagate the literal/oneTime/constant attributes
2326
+ fn.literal = parsedExpression.literal;
2327
+ fn.oneTime = parsedExpression.oneTime;
2328
+ fn.constant = parsedExpression.constant;
2329
+
2330
+ // Treat the interceptor like filters.
2331
+ // If it is not $stateful then only watch its inputs.
2332
+ // If the expression itself has no inputs then use the full expression as an input.
2333
+ if (!interceptorFn.$stateful) {
2334
+ useInputs = !parsedExpression.inputs;
2335
+ fn.inputs = parsedExpression.inputs
2336
+ ? parsedExpression.inputs
2337
+ : [parsedExpression];
2338
+
2339
+ if (!interceptorFn.$$pure) {
2340
+ fn.inputs = fn.inputs.map(function (e) {
2341
+ // Remove the isPure flag of inputs when it is not absolute because they are now wrapped in a
2342
+ // non-pure interceptor function.
2343
+ if (e.isPure === PURITY_RELATIVE) {
2344
+ return function depurifier(s) {
2345
+ return e(s);
2346
+ };
2347
+ }
2348
+ return e;
2349
+ });
2350
+ }
2351
+ }
2352
+
2353
+ return addWatchDelegate(fn);
2354
+ }
2355
+ },
2356
+ ];
2357
+ }
2358
+
2359
+ function constantWatchDelegate(
2360
+ scope,
2361
+ listener,
2362
+ objectEquality,
2363
+ parsedExpression,
2364
+ ) {
2365
+ const unwatch = scope.$watch(
2366
+ ($scope) => {
2367
+ unwatch();
2368
+ return parsedExpression($scope);
2369
+ },
2370
+ listener,
2371
+ objectEquality,
2372
+ );
2373
+ return unwatch;
2374
+ }
2375
+
2376
+ function addWatchDelegate(parsedExpression) {
2377
+ if (parsedExpression.constant) {
2378
+ parsedExpression.$$watchDelegate = constantWatchDelegate;
2379
+ } else if (parsedExpression.oneTime) {
2380
+ parsedExpression.$$watchDelegate = oneTimeWatchDelegate;
2381
+ } else if (parsedExpression.inputs) {
2382
+ parsedExpression.$$watchDelegate = inputsWatchDelegate;
2383
+ }
2384
+
2385
+ return parsedExpression;
2386
+ }
2387
+
2388
+ export function inputsWatchDelegate(
2389
+ scope,
2390
+ listener,
2391
+ objectEquality,
2392
+ parsedExpression,
2393
+ ) {
2394
+ let inputExpressions = parsedExpression.inputs;
2395
+ let lastResult;
2396
+
2397
+ if (inputExpressions.length === 1) {
2398
+ let oldInputValueOf = expressionInputDirtyCheck; // init to something unique so that equals check fails
2399
+ // eslint-disable-next-line prefer-destructuring
2400
+ inputExpressions = inputExpressions[0];
2401
+ return scope.$watch(
2402
+ ($scope) => {
2403
+ const newInputValue = inputExpressions($scope);
2404
+ if (
2405
+ !expressionInputDirtyCheck(
2406
+ newInputValue,
2407
+ oldInputValueOf,
2408
+ inputExpressions.isPure,
2409
+ )
2410
+ ) {
2411
+ lastResult = parsedExpression($scope, undefined, undefined, [
2412
+ newInputValue,
2413
+ ]);
2414
+ oldInputValueOf = newInputValue && getValueOf(newInputValue);
2415
+ }
2416
+ return lastResult;
2417
+ },
2418
+ listener,
2419
+ objectEquality,
2420
+ );
2421
+ }
2422
+
2423
+ const oldInputValueOfValues = [];
2424
+ const oldInputValues = [];
2425
+ for (let i = 0, ii = inputExpressions.length; i < ii; i++) {
2426
+ oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails
2427
+ oldInputValues[i] = null;
2428
+ }
2429
+
2430
+ return scope.$watch(
2431
+ (scope) => {
2432
+ let changed = false;
2433
+
2434
+ // eslint-disable-next-line no-plusplus
2435
+ for (let i = 0, ii = inputExpressions.length; i < ii; i++) {
2436
+ const newInputValue = inputExpressions[i](scope);
2437
+ if (
2438
+ changed ||
2439
+ // eslint-disable-next-line no-cond-assign
2440
+ (changed = !expressionInputDirtyCheck(
2441
+ newInputValue,
2442
+ oldInputValueOfValues[i],
2443
+ inputExpressions[i].isPure,
2444
+ ))
2445
+ ) {
2446
+ oldInputValues[i] = newInputValue;
2447
+ oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue);
2448
+ }
2449
+ }
2450
+
2451
+ if (changed) {
2452
+ lastResult = parsedExpression(
2453
+ scope,
2454
+ undefined,
2455
+ undefined,
2456
+ oldInputValues,
2457
+ );
2458
+ }
2459
+
2460
+ return lastResult;
2461
+ },
2462
+ listener,
2463
+ objectEquality,
2464
+ );
2465
+ }
2466
+
2467
+ export function oneTimeWatchDelegate(
2468
+ scope,
2469
+ listener,
2470
+ objectEquality,
2471
+ parsedExpression,
2472
+ ) {
2473
+ const isDone = parsedExpression.literal ? isAllDefined : isDefined;
2474
+
2475
+ let unwatch;
2476
+ let lastValue;
2477
+
2478
+ const exp = parsedExpression.$$intercepted || parsedExpression;
2479
+ const post = parsedExpression.$$interceptor || identity;
2480
+
2481
+ const useInputs = parsedExpression.inputs && !exp.inputs;
2482
+
2483
+ // Propagate the literal/inputs/constant attributes
2484
+ // ... but not oneTime since we are handling it
2485
+ oneTimeWatch.literal = parsedExpression.literal;
2486
+ oneTimeWatch.constant = parsedExpression.constant;
2487
+ oneTimeWatch.inputs = parsedExpression.inputs;
2488
+
2489
+ // Allow other delegates to run on this wrapped expression
2490
+ addWatchDelegate(oneTimeWatch);
2491
+
2492
+ function unwatchIfDone() {
2493
+ if (isDone(lastValue)) {
2494
+ unwatch();
2495
+ }
2496
+ }
2497
+
2498
+ function oneTimeWatch(scope, locals, assign, inputs) {
2499
+ lastValue =
2500
+ useInputs && inputs ? inputs[0] : exp(scope, locals, assign, inputs);
2501
+ if (isDone(lastValue)) {
2502
+ scope.$$postDigest(unwatchIfDone);
2503
+ }
2504
+ return post(lastValue);
2505
+ }
2506
+
2507
+ unwatch = scope.$watch(oneTimeWatch, listener, objectEquality);
2508
+
2509
+ return unwatch;
2510
+ }
2511
+
2512
+ export function chainInterceptors(first, second) {
2513
+ function chainedInterceptor(value) {
2514
+ return second(first(value));
2515
+ }
2516
+ chainedInterceptor.$stateful = first.$stateful || second.$stateful;
2517
+ chainedInterceptor.$$pure = first.$$pure && second.$$pure;
2518
+
2519
+ return chainedInterceptor;
2520
+ }
2521
+
2522
+ export function expressionInputDirtyCheck(
2523
+ newValue,
2524
+ oldValueOfValue,
2525
+ compareObjectIdentity,
2526
+ ) {
2527
+ if (newValue == null || oldValueOfValue == null) {
2528
+ // null/undefined
2529
+ return newValue === oldValueOfValue;
2530
+ }
2531
+
2532
+ if (typeof newValue === "object") {
2533
+ // attempt to convert the value to a primitive type
2534
+ // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
2535
+ // be cheaply dirty-checked
2536
+ newValue = getValueOf(newValue);
2537
+
2538
+ if (typeof newValue === "object" && !compareObjectIdentity) {
2539
+ // objects/arrays are not supported - deep-watching them would be too expensive
2540
+ return false;
2541
+ }
2542
+
2543
+ // fall-through to the primitive equality check
2544
+ }
2545
+
2546
+ // Primitive or NaN
2547
+ // eslint-disable-next-line no-self-compare
2548
+ return (
2549
+ newValue === oldValueOfValue ||
2550
+ // eslint-disable-next-line no-self-compare
2551
+ (newValue !== newValue && oldValueOfValue !== oldValueOfValue)
2552
+ );
2553
+ }
2554
+
2555
+ // eslint-disable-next-line class-methods-use-this
2556
+ export function isAllDefined(value) {
2557
+ let allDefined = true;
2558
+ forEach(value, (val) => {
2559
+ if (!isDefined(val)) allDefined = false;
2560
+ });
2561
+ return allDefined;
2562
+ }