@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,725 @@
1
+ import {
2
+ valueFn,
3
+ assertNotHasOwnProperty,
4
+ shallowCopy,
5
+ arrayRemove,
6
+ isBoolean,
7
+ snakeCase,
8
+ forEach,
9
+ extend,
10
+ isUndefined,
11
+ } from "../core/utils";
12
+ import {
13
+ PRISTINE_CLASS,
14
+ DIRTY_CLASS,
15
+ VALID_CLASS,
16
+ INVALID_CLASS,
17
+ } from "../constants";
18
+
19
+ export const nullFormCtrl = {
20
+ $addControl: () => {},
21
+ $getControls: valueFn([]),
22
+ $$renameControl: nullFormRenameControl,
23
+ $removeControl: () => {},
24
+ $setValidity: () => {},
25
+ $setDirty: () => {},
26
+ $setPristine: () => {},
27
+ $setSubmitted: () => {},
28
+ $$setSubmitted: () => {},
29
+ };
30
+ const PENDING_CLASS = "ng-pending";
31
+ const SUBMITTED_CLASS = "ng-submitted";
32
+
33
+ function nullFormRenameControl(control, name) {
34
+ control.$name = name;
35
+ }
36
+
37
+ /**
38
+ * @ngdoc type
39
+ * @name form.FormController
40
+ *
41
+ * @property {boolean} $pristine True if user has not interacted with the form yet.
42
+ * @property {boolean} $dirty True if user has already interacted with the form.
43
+ * @property {boolean} $valid True if all of the containing forms and controls are valid.
44
+ * @property {boolean} $invalid True if at least one containing control or form is invalid.
45
+ * @property {boolean} $submitted True if user has submitted the form even if its invalid.
46
+ *
47
+ * @property {Object} $pending An object hash, containing references to controls or forms with
48
+ * pending validators, where:
49
+ *
50
+ * - keys are validations tokens (error names).
51
+ * - values are arrays of controls or forms that have a pending validator for the given error name.
52
+ *
53
+ * See {@link form.FormController#$error $error} for a list of built-in validation tokens.
54
+ *
55
+ * @property {Object} $error An object hash, containing references to controls or forms with failing
56
+ * validators, where:
57
+ *
58
+ * - keys are validation tokens (error names),
59
+ * - values are arrays of controls or forms that have a failing validator for the given error name.
60
+ *
61
+ * Built-in validation tokens:
62
+ * - `email`
63
+ * - `max`
64
+ * - `maxlength`
65
+ * - `min`
66
+ * - `minlength`
67
+ * - `number`
68
+ * - `pattern`
69
+ * - `required`
70
+ * - `url`
71
+ * - `date`
72
+ * - `datetimelocal`
73
+ * - `time`
74
+ * - `week`
75
+ * - `month`
76
+ *
77
+ * @description
78
+ * `FormController` keeps track of all its controls and nested forms as well as the state of them,
79
+ * such as being valid/invalid or dirty/pristine.
80
+ *
81
+ * Each {@link ng.directive:form form} directive creates an instance
82
+ * of `FormController`.
83
+ *
84
+ */
85
+ // asks for $scope to fool the BC controller module
86
+ FormController.$inject = [
87
+ "$element",
88
+ "$attrs",
89
+ "$scope",
90
+ "$animate",
91
+ "$interpolate",
92
+ ];
93
+ export function FormController(
94
+ $element,
95
+ $attrs,
96
+ $scope,
97
+ $animate,
98
+ $interpolate,
99
+ ) {
100
+ this.$$controls = [];
101
+
102
+ // init state
103
+ this.$error = {};
104
+ this.$$success = {};
105
+ this.$pending = undefined;
106
+ this.$name = $interpolate($attrs.name || $attrs.ngForm || "")($scope);
107
+ this.$dirty = false;
108
+ this.$pristine = true;
109
+ this.$valid = true;
110
+ this.$invalid = false;
111
+ this.$submitted = false;
112
+ this.$$parentForm = nullFormCtrl;
113
+
114
+ this.$$element = $element;
115
+ this.$$animate = $animate;
116
+
117
+ setupValidity(this);
118
+ }
119
+
120
+ FormController.prototype = {
121
+ /**
122
+ * @ngdoc method
123
+ * @name form.FormController#$rollbackViewValue
124
+ *
125
+ * @description
126
+ * Rollback all form controls pending updates to the `$modelValue`.
127
+ *
128
+ * Updates may be pending by a debounced event or because the input is waiting for a some future
129
+ * event defined in `ng-model-options`. This method is typically needed by the reset button of
130
+ * a form that uses `ng-model-options` to pend updates.
131
+ */
132
+ $rollbackViewValue() {
133
+ this.$$controls.forEach((control) => {
134
+ control.$rollbackViewValue();
135
+ });
136
+ },
137
+
138
+ /**
139
+ * @ngdoc method
140
+ * @name form.FormController#$commitViewValue
141
+ *
142
+ * @description
143
+ * Commit all form controls pending updates to the `$modelValue`.
144
+ *
145
+ * Updates may be pending by a debounced event or because the input is waiting for a some future
146
+ * event defined in `ng-model-options`. This method is rarely needed as `NgModelController`
147
+ * usually handles calling this in response to input events.
148
+ */
149
+ $commitViewValue() {
150
+ this.$$controls.forEach((control) => {
151
+ control.$commitViewValue();
152
+ });
153
+ },
154
+
155
+ /**
156
+ * @ngdoc method
157
+ * @name form.FormController#$addControl
158
+ * @param {object} control control object, either a {@link form.FormController} or an
159
+ * {@link ngModel.NgModelController}
160
+ *
161
+ * @description
162
+ * Register a control with the form. Input elements using ngModelController do this automatically
163
+ * when they are linked.
164
+ *
165
+ * Note that the current state of the control will not be reflected on the new parent form. This
166
+ * is not an issue with normal use, as freshly compiled and linked controls are in a `$pristine`
167
+ * state.
168
+ *
169
+ * However, if the method is used programmatically, for example by adding dynamically created controls,
170
+ * or controls that have been previously removed without destroying their corresponding DOM element,
171
+ * it's the developers responsibility to make sure the current state propagates to the parent form.
172
+ *
173
+ * For example, if an input control is added that is already `$dirty` and has `$error` properties,
174
+ * calling `$setDirty()` and `$validate()` afterwards will propagate the state to the parent form.
175
+ */
176
+ $addControl(control) {
177
+ // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored
178
+ // and not added to the scope. Now we throw an error.
179
+ assertNotHasOwnProperty(control.$name, "input");
180
+ this.$$controls.push(control);
181
+
182
+ if (control.$name) {
183
+ this[control.$name] = control;
184
+ }
185
+
186
+ control.$$parentForm = this;
187
+ },
188
+
189
+ /**
190
+ * @ngdoc method
191
+ * @name form.FormController#$getControls
192
+ * @returns {Array} the controls that are currently part of this form
193
+ *
194
+ * @description
195
+ * This method returns a **shallow copy** of the controls that are currently part of this form.
196
+ * The controls can be instances of {@link form.FormController `FormController`}
197
+ * ({@link ngForm "child-forms"}) and of {@link ngModel.NgModelController `NgModelController`}.
198
+ * If you need access to the controls of child-forms, you have to call `$getControls()`
199
+ * recursively on them.
200
+ * This can be used for example to iterate over all controls to validate them.
201
+ *
202
+ * The controls can be accessed normally, but adding to, or removing controls from the array has
203
+ * no effect on the form. Instead, use {@link form.FormController#$addControl `$addControl()`} and
204
+ * {@link form.FormController#$removeControl `$removeControl()`} for this use-case.
205
+ * Likewise, adding a control to, or removing a control from the form is not reflected
206
+ * in the shallow copy. That means you should get a fresh copy from `$getControls()` every time
207
+ * you need access to the controls.
208
+ */
209
+ $getControls() {
210
+ return shallowCopy(this.$$controls);
211
+ },
212
+
213
+ // Private API: rename a form control
214
+ $$renameControl(control, newName) {
215
+ const oldName = control.$name;
216
+
217
+ if (this[oldName] === control) {
218
+ delete this[oldName];
219
+ }
220
+ this[newName] = control;
221
+ control.$name = newName;
222
+ },
223
+
224
+ /**
225
+ * @ngdoc method
226
+ * @name form.FormController#$removeControl
227
+ * @param {object} control control object, either a {@link form.FormController} or an
228
+ * {@link ngModel.NgModelController}
229
+ *
230
+ * @description
231
+ * Deregister a control from the form.
232
+ *
233
+ * Input elements using ngModelController do this automatically when they are destroyed.
234
+ *
235
+ * Note that only the removed control's validation state (`$errors`etc.) will be removed from the
236
+ * form. `$dirty`, `$submitted` states will not be changed, because the expected behavior can be
237
+ * different from case to case. For example, removing the only `$dirty` control from a form may or
238
+ * may not mean that the form is still `$dirty`.
239
+ */
240
+ $removeControl(control) {
241
+ if (control.$name && this[control.$name] === control) {
242
+ delete this[control.$name];
243
+ }
244
+ forEach(
245
+ this.$pending,
246
+ function (value, name) {
247
+ // eslint-disable-next-line no-invalid-this
248
+ this.$setValidity(name, null, control);
249
+ },
250
+ this,
251
+ );
252
+ forEach(
253
+ this.$error,
254
+ function (value, name) {
255
+ // eslint-disable-next-line no-invalid-this
256
+ this.$setValidity(name, null, control);
257
+ },
258
+ this,
259
+ );
260
+ forEach(
261
+ this.$$success,
262
+ function (value, name) {
263
+ // eslint-disable-next-line no-invalid-this
264
+ this.$setValidity(name, null, control);
265
+ },
266
+ this,
267
+ );
268
+
269
+ arrayRemove(this.$$controls, control);
270
+ control.$$parentForm = nullFormCtrl;
271
+ },
272
+
273
+ /**
274
+ * @ngdoc method
275
+ * @name form.FormController#$setDirty
276
+ *
277
+ * @description
278
+ * Sets the form to a dirty state.
279
+ *
280
+ * This method can be called to add the 'ng-dirty' class and set the form to a dirty
281
+ * state (ng-dirty class). This method will also propagate to parent forms.
282
+ */
283
+ $setDirty() {
284
+ this.$$animate.removeClass(this.$$element, PRISTINE_CLASS);
285
+ this.$$animate.addClass(this.$$element, DIRTY_CLASS);
286
+ this.$dirty = true;
287
+ this.$pristine = false;
288
+ this.$$parentForm.$setDirty();
289
+ },
290
+
291
+ /**
292
+ * @ngdoc method
293
+ * @name form.FormController#$setPristine
294
+ *
295
+ * @description
296
+ * Sets the form to its pristine state.
297
+ *
298
+ * This method sets the form's `$pristine` state to true, the `$dirty` state to false, removes
299
+ * the `ng-dirty` class and adds the `ng-pristine` class. Additionally, it sets the `$submitted`
300
+ * state to false.
301
+ *
302
+ * This method will also propagate to all the controls contained in this form.
303
+ *
304
+ * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
305
+ * saving or resetting it.
306
+ */
307
+ $setPristine() {
308
+ this.$$animate.setClass(
309
+ this.$$element,
310
+ PRISTINE_CLASS,
311
+ `${DIRTY_CLASS} ${SUBMITTED_CLASS}`,
312
+ );
313
+ this.$dirty = false;
314
+ this.$pristine = true;
315
+ this.$submitted = false;
316
+ this.$$controls.forEach((control) => {
317
+ control.$setPristine();
318
+ });
319
+ },
320
+
321
+ /**
322
+ * @ngdoc method
323
+ * @name form.FormController#$setUntouched
324
+ *
325
+ * @description
326
+ * Sets the form to its untouched state.
327
+ *
328
+ * This method can be called to remove the 'ng-touched' class and set the form controls to their
329
+ * untouched state (ng-untouched class).
330
+ *
331
+ * Setting a form controls back to their untouched state is often useful when setting the form
332
+ * back to its pristine state.
333
+ */
334
+ $setUntouched() {
335
+ this.$$controls.forEach((control) => {
336
+ control.$setUntouched();
337
+ });
338
+ },
339
+
340
+ /**
341
+ * @ngdoc method
342
+ * @name form.FormController#$setSubmitted
343
+ *
344
+ * @description
345
+ * Sets the form to its `$submitted` state. This will also set `$submitted` on all child and
346
+ * parent forms of the form.
347
+ */
348
+ $setSubmitted() {
349
+ let rootForm = this;
350
+ while (rootForm.$$parentForm && rootForm.$$parentForm !== nullFormCtrl) {
351
+ rootForm = rootForm.$$parentForm;
352
+ }
353
+ rootForm.$$setSubmitted();
354
+ },
355
+
356
+ $$setSubmitted() {
357
+ this.$$animate.addClass(this.$$element, SUBMITTED_CLASS);
358
+ this.$submitted = true;
359
+ this.$$controls.forEach((control) => {
360
+ if (control.$$setSubmitted) {
361
+ control.$$setSubmitted();
362
+ }
363
+ });
364
+ },
365
+ };
366
+
367
+ /**
368
+ * @ngdoc method
369
+ * @name form.FormController#$setValidity
370
+ *
371
+ * @description
372
+ * Change the validity state of the form, and notify the parent form (if any).
373
+ *
374
+ * Application developers will rarely need to call this method directly. It is used internally, by
375
+ * {@link ngModel.NgModelController#$setValidity NgModelController.$setValidity()}, to propagate a
376
+ * control's validity state to the parent `FormController`.
377
+ *
378
+ * @param {string} validationErrorKey Name of the validator. The `validationErrorKey` will be
379
+ * assigned to either `$error[validationErrorKey]` or `$pending[validationErrorKey]` (for
380
+ * unfulfilled `$asyncValidators`), so that it is available for data-binding. The
381
+ * `validationErrorKey` should be in camelCase and will get converted into dash-case for
382
+ * class name. Example: `myError` will result in `ng-valid-my-error` and
383
+ * `ng-invalid-my-error` classes and can be bound to as `{{ someForm.$error.myError }}`.
384
+ * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending
385
+ * (undefined), or skipped (null). Pending is used for unfulfilled `$asyncValidators`.
386
+ * Skipped is used by AngularJS when validators do not run because of parse errors and when
387
+ * `$asyncValidators` do not run because any of the `$validators` failed.
388
+ * @param {NgModelController | FormController} controller - The controller whose validity state is
389
+ * triggering the change.
390
+ */
391
+ addSetValidityMethod({
392
+ clazz: FormController,
393
+ set(object, property, controller) {
394
+ const list = object[property];
395
+ if (!list) {
396
+ object[property] = [controller];
397
+ } else {
398
+ const index = list.indexOf(controller);
399
+ if (index === -1) {
400
+ list.push(controller);
401
+ }
402
+ }
403
+ },
404
+ unset(object, property, controller) {
405
+ const list = object[property];
406
+ if (!list) {
407
+ return;
408
+ }
409
+ arrayRemove(list, controller);
410
+ if (list.length === 0) {
411
+ delete object[property];
412
+ }
413
+ },
414
+ });
415
+
416
+ /**
417
+ * @ngdoc directive
418
+ * @name ngForm
419
+ * @restrict EAC
420
+ *
421
+ * @description
422
+ * Helper directive that makes it possible to create control groups inside a
423
+ * {@link ng.directive:form `form`} directive.
424
+ * These "child forms" can be used, for example, to determine the validity of a sub-group of
425
+ * controls.
426
+ *
427
+ * <div class="alert alert-danger">
428
+ * **Note**: `ngForm` cannot be used as a replacement for `<form>`, because it lacks its
429
+ * [built-in HTML functionality](https://html.spec.whatwg.org/#the-form-element).
430
+ * Specifically, you cannot submit `ngForm` like a `<form>` tag. That means,
431
+ * you cannot send data to the server with `ngForm`, or integrate it with
432
+ * {@link ng.directive:ngSubmit `ngSubmit`}.
433
+ * </div>
434
+ *
435
+ * @param {string=} ngForm|name Name of the form. If specified, the form controller will
436
+ * be published into the related scope, under this name.
437
+ *
438
+ */
439
+
440
+ /**
441
+ * @ngdoc directive
442
+ * @name form
443
+ * @restrict E
444
+ *
445
+ * @description
446
+ * Directive that instantiates
447
+ * {@link form.FormController FormController}.
448
+ *
449
+ * If the `name` attribute is specified, the form controller is published onto the current scope under
450
+ * this name.
451
+ *
452
+ * ## Alias: {@link ng.directive:ngForm `ngForm`}
453
+ *
454
+ * In AngularJS, forms can be nested. This means that the outer form is valid when all of the child
455
+ * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
456
+ * AngularJS provides the {@link ng.directive:ngForm `ngForm`} directive, which behaves identically to
457
+ * `form` but can be nested. Nested forms can be useful, for example, if the validity of a sub-group
458
+ * of controls needs to be determined.
459
+ *
460
+ * ## CSS classes
461
+ * - `ng-valid` is set if the form is valid.
462
+ * - `ng-invalid` is set if the form is invalid.
463
+ * - `ng-pending` is set if the form is pending.
464
+ * - `ng-pristine` is set if the form is pristine.
465
+ * - `ng-dirty` is set if the form is dirty.
466
+ * - `ng-submitted` is set if the form was submitted.
467
+ *
468
+ * Keep in mind that ngAnimate can detect each of these classes when added and removed.
469
+ *
470
+ *
471
+ * ## Submitting a form and preventing the default action
472
+ *
473
+ * Since the role of forms in client-side AngularJS applications is different than in classical
474
+ * roundtrip apps, it is desirable for the browser not to translate the form submission into a full
475
+ * page reload that sends the data to the server. Instead some javascript logic should be triggered
476
+ * to handle the form submission in an application-specific way.
477
+ *
478
+ * For this reason, AngularJS prevents the default action (form submission to the server) unless the
479
+ * `<form>` element has an `action` attribute specified.
480
+ *
481
+ * You can use one of the following two ways to specify what javascript method should be called when
482
+ * a form is submitted:
483
+ *
484
+ * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element
485
+ * - {@link ng.directive:ngClick ngClick} directive on the first
486
+ * button or input field of type submit (input[type=submit])
487
+ *
488
+ * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit}
489
+ * or {@link ng.directive:ngClick ngClick} directives.
490
+ * This is because of the following form submission rules in the HTML specification:
491
+ *
492
+ * - If a form has only one input field then hitting enter in this field triggers form submit
493
+ * (`ngSubmit`)
494
+ * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter
495
+ * doesn't trigger submit
496
+ * - if a form has one or more input fields and one or more buttons or input[type=submit] then
497
+ * hitting enter in any of the input fields will trigger the click handler on the *first* button or
498
+ * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)
499
+ *
500
+ * Any pending `ngModelOptions` changes will take place immediately when an enclosing form is
501
+ * submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
502
+ * to have access to the updated model.
503
+ *
504
+ * @animations
505
+ * Animations in ngForm are triggered when any of the associated CSS classes are added and removed.
506
+ * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any
507
+ * other validations that are performed within the form. Animations in ngForm are similar to how
508
+ * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well
509
+ * as JS animations.
510
+ *
511
+ * @param {string=} isNgForm Name of the form. If specified, the form controller will be published into
512
+ * related scope, under this name.
513
+ */
514
+ const formDirectiveFactory = function (isNgForm) {
515
+ return [
516
+ "$timeout",
517
+ "$parse",
518
+ function ($timeout, $parse) {
519
+ const formDirective = {
520
+ name: "form",
521
+ restrict: isNgForm ? "EAC" : "E",
522
+ require: ["form", "^^?form"], // first is the form's own ctrl, second is an optional parent form
523
+ controller: FormController,
524
+ compile: function ngFormCompile(formElement, attr) {
525
+ // Setup initial state of the control
526
+ formElement[0].classList.add(PRISTINE_CLASS, VALID_CLASS);
527
+
528
+ const nameAttr = attr.name
529
+ ? "name"
530
+ : isNgForm && attr.ngForm
531
+ ? "ngForm"
532
+ : false;
533
+
534
+ return {
535
+ pre: function ngFormPreLink(scope, formElement, attr, ctrls) {
536
+ const controller = ctrls[0];
537
+
538
+ // if `action` attr is not present on the form, prevent the default action (submission)
539
+ if (!("action" in attr)) {
540
+ // we can't use jq events because if a form is destroyed during submission the default
541
+ // action is not prevented. see #1238
542
+ //
543
+ // IE 9 is not affected because it doesn't fire a submit event and try to do a full
544
+ // page reload if the form was destroyed by submission of the form via a click handler
545
+ // on a button in the form. Looks like an IE9 specific bug.
546
+ const handleFormSubmission = function (event) {
547
+ scope.$apply(() => {
548
+ controller.$commitViewValue();
549
+ controller.$setSubmitted();
550
+ });
551
+
552
+ event.preventDefault();
553
+ };
554
+
555
+ formElement[0].addEventListener("submit", handleFormSubmission);
556
+
557
+ // unregister the preventDefault listener so that we don't not leak memory but in a
558
+ // way that will achieve the prevention of the default action.
559
+ formElement.on("$destroy", () => {
560
+ $timeout(
561
+ () => {
562
+ formElement[0].removeEventListener(
563
+ "submit",
564
+ handleFormSubmission,
565
+ );
566
+ },
567
+ 0,
568
+ false,
569
+ );
570
+ });
571
+ }
572
+
573
+ const parentFormCtrl = ctrls[1] || controller.$$parentForm;
574
+ parentFormCtrl.$addControl(controller);
575
+
576
+ let setter = nameAttr ? getSetter(controller.$name) : () => {};
577
+
578
+ if (nameAttr) {
579
+ setter(scope, controller);
580
+ attr.$observe(nameAttr, (newValue) => {
581
+ if (controller.$name === newValue) return;
582
+ setter(scope, undefined);
583
+ controller.$$parentForm.$$renameControl(controller, newValue);
584
+ setter = getSetter(controller.$name);
585
+ setter(scope, controller);
586
+ });
587
+ }
588
+ formElement.on("$destroy", () => {
589
+ controller.$$parentForm.$removeControl(controller);
590
+ setter(scope, undefined);
591
+ extend(controller, nullFormCtrl); // stop propagating child destruction handlers upwards
592
+ });
593
+ },
594
+ };
595
+ },
596
+ };
597
+
598
+ return formDirective;
599
+
600
+ function getSetter(expression) {
601
+ if (expression === "") {
602
+ // create an assignable expression, so forms with an empty name can be renamed later
603
+ return $parse('this[""]').assign;
604
+ }
605
+ return $parse(expression).assign || (() => {});
606
+ }
607
+ },
608
+ ];
609
+ };
610
+
611
+ export const formDirective = formDirectiveFactory();
612
+ export const ngFormDirective = formDirectiveFactory(true);
613
+
614
+ // helper methods
615
+ export function setupValidity(instance) {
616
+ instance.$$classCache = {};
617
+ instance.$$classCache[INVALID_CLASS] = !(instance.$$classCache[VALID_CLASS] =
618
+ instance.$$element[0].classList.contains(VALID_CLASS));
619
+ }
620
+
621
+ export function addSetValidityMethod(context) {
622
+ const { clazz } = context;
623
+ const { set } = context;
624
+ const { unset } = context;
625
+
626
+ clazz.prototype.$setValidity = function (
627
+ validationErrorKey,
628
+ state,
629
+ controller,
630
+ ) {
631
+ if (isUndefined(state)) {
632
+ createAndSet(this, "$pending", validationErrorKey, controller);
633
+ } else {
634
+ unsetAndCleanup(this, "$pending", validationErrorKey, controller);
635
+ }
636
+ if (!isBoolean(state)) {
637
+ unset(this.$error, validationErrorKey, controller);
638
+ unset(this.$$success, validationErrorKey, controller);
639
+ } else if (state) {
640
+ unset(this.$error, validationErrorKey, controller);
641
+ set(this.$$success, validationErrorKey, controller);
642
+ } else {
643
+ set(this.$error, validationErrorKey, controller);
644
+ unset(this.$$success, validationErrorKey, controller);
645
+ }
646
+ if (this.$pending) {
647
+ cachedToggleClass(this, PENDING_CLASS, true);
648
+ this.$valid = this.$invalid = undefined;
649
+ toggleValidationCss(this, "", null);
650
+ } else {
651
+ cachedToggleClass(this, PENDING_CLASS, false);
652
+ this.$valid = isObjectEmpty(this.$error);
653
+ this.$invalid = !this.$valid;
654
+ toggleValidationCss(this, "", this.$valid);
655
+ }
656
+
657
+ // re-read the state as the set/unset methods could have
658
+ // combined state in this.$error[validationError] (used for forms),
659
+ // where setting/unsetting only increments/decrements the value,
660
+ // and does not replace it.
661
+ let combinedState;
662
+ if (this.$pending && this.$pending[validationErrorKey]) {
663
+ combinedState = undefined;
664
+ } else if (this.$error[validationErrorKey]) {
665
+ combinedState = false;
666
+ } else if (this.$$success[validationErrorKey]) {
667
+ combinedState = true;
668
+ } else {
669
+ combinedState = null;
670
+ }
671
+
672
+ toggleValidationCss(this, validationErrorKey, combinedState);
673
+ this.$$parentForm.$setValidity(validationErrorKey, combinedState, this);
674
+ };
675
+
676
+ function createAndSet(ctrl, name, value, controller) {
677
+ if (!ctrl[name]) {
678
+ ctrl[name] = {};
679
+ }
680
+ set(ctrl[name], value, controller);
681
+ }
682
+
683
+ function unsetAndCleanup(ctrl, name, value, controller) {
684
+ if (ctrl[name]) {
685
+ unset(ctrl[name], value, controller);
686
+ }
687
+ if (isObjectEmpty(ctrl[name])) {
688
+ ctrl[name] = undefined;
689
+ }
690
+ }
691
+
692
+ function cachedToggleClass(ctrl, className, switchValue) {
693
+ if (switchValue && !ctrl.$$classCache[className]) {
694
+ ctrl.$$animate.addClass(ctrl.$$element, className);
695
+ ctrl.$$classCache[className] = true;
696
+ } else if (!switchValue && ctrl.$$classCache[className]) {
697
+ ctrl.$$animate.removeClass(ctrl.$$element, className);
698
+ ctrl.$$classCache[className] = false;
699
+ }
700
+ }
701
+
702
+ function toggleValidationCss(ctrl, validationErrorKey, isValid) {
703
+ validationErrorKey = validationErrorKey
704
+ ? `-${snakeCase(validationErrorKey, "-")}`
705
+ : "";
706
+
707
+ cachedToggleClass(ctrl, VALID_CLASS + validationErrorKey, isValid === true);
708
+ cachedToggleClass(
709
+ ctrl,
710
+ INVALID_CLASS + validationErrorKey,
711
+ isValid === false,
712
+ );
713
+ }
714
+ }
715
+
716
+ function isObjectEmpty(obj) {
717
+ if (obj) {
718
+ for (const prop in obj) {
719
+ if (Object.prototype.hasOwnProperty.call(obj, prop)) {
720
+ return false;
721
+ }
722
+ }
723
+ }
724
+ return true;
725
+ }