@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,852 @@
1
+ import { forEach, isArray, isString } from "../core/utils";
2
+ import { jqLite } from "../jqLite";
3
+ /**
4
+ * @ngdoc module
5
+ * @name ngMessages
6
+ * @description
7
+ *
8
+ * The `ngMessages` module provides enhanced support for displaying messages within templates
9
+ * (typically within forms or when rendering message objects that return key/value data).
10
+ * Instead of relying on JavaScript code and/or complex ng-if statements within your form template to
11
+ * show and hide error messages specific to the state of an input field, the `ngMessages` and
12
+ * `ngMessage` directives are designed to handle the complexity, inheritance and priority
13
+ * sequencing based on the order of how the messages are defined in the template.
14
+ *
15
+ * Currently, the ngMessages module only contains the code for the `ngMessages`, `ngMessagesInclude`
16
+ * `ngMessage`, `ngMessageExp` and `ngMessageDefault` directives.
17
+ *
18
+ * ## Usage
19
+ * The `ngMessages` directive allows keys in a key/value collection to be associated with a child element
20
+ * (or 'message') that will show or hide based on the truthiness of that key's value in the collection. A common use
21
+ * case for `ngMessages` is to display error messages for inputs using the `$error` object exposed by the
22
+ * {@link ngModel ngModel} directive.
23
+ *
24
+ * The child elements of the `ngMessages` directive are matched to the collection keys by a `ngMessage` or
25
+ * `ngMessageExp` directive. The value of these attributes must match a key in the collection that is provided by
26
+ * the `ngMessages` directive.
27
+ *
28
+ * Consider the following example, which illustrates a typical use case of `ngMessages`. Within the form `myForm` we
29
+ * have a text input named `myField` which is bound to the scope variable `field` using the {@link ngModel ngModel}
30
+ * directive.
31
+ *
32
+ * The `myField` field is a required input of type `email` with a maximum length of 15 characters.
33
+ *
34
+ * ```html
35
+ * <form name="myForm">
36
+ * <label>
37
+ * Enter text:
38
+ * <input type="email" ng-model="field" name="myField" required maxlength="15" />
39
+ * </label>
40
+ * <div ng-messages="myForm.myField.$error" role="alert">
41
+ * <div ng-message="required">Please enter a value for this field.</div>
42
+ * <div ng-message="email">This field must be a valid email address.</div>
43
+ * <div ng-message="maxlength">This field can be at most 15 characters long.</div>
44
+ * </div>
45
+ * </form>
46
+ * ```
47
+ *
48
+ * In order to show error messages corresponding to `myField` we first create an element with an `ngMessages` attribute
49
+ * set to the `$error` object owned by the `myField` input in our `myForm` form.
50
+ *
51
+ * Within this element we then create separate elements for each of the possible errors that `myField` could have.
52
+ * The `ngMessage` attribute is used to declare which element(s) will appear for which error - for example,
53
+ * setting `ng-message="required"` specifies that this particular element should be displayed when there
54
+ * is no value present for the required field `myField` (because the key `required` will be `true` in the object
55
+ * `myForm.myField.$error`).
56
+ *
57
+ * ### Message order
58
+ *
59
+ * By default, `ngMessages` will only display one message for a particular key/value collection at any time. If more
60
+ * than one message (or error) key is currently true, then which message is shown is determined by the order of messages
61
+ * in the HTML template code (messages declared first are prioritised). This mechanism means the developer does not have
62
+ * to prioritize messages using custom JavaScript code.
63
+ *
64
+ * Given the following error object for our example (which informs us that the field `myField` currently has both the
65
+ * `required` and `email` errors):
66
+ *
67
+ * ```javascript
68
+ * <!-- keep in mind that ngModel automatically sets these error flags -->
69
+ * myField.$error = { required : true, email: true, maxlength: false };
70
+ * ```
71
+ * The `required` message will be displayed to the user since it appears before the `email` message in the DOM.
72
+ * Once the user types a single character, the `required` message will disappear (since the field now has a value)
73
+ * but the `email` message will be visible because it is still applicable.
74
+ *
75
+ * ### Displaying multiple messages at the same time
76
+ *
77
+ * While `ngMessages` will by default only display one error element at a time, the `ng-messages-multiple` attribute can
78
+ * be applied to the `ngMessages` container element to cause it to display all applicable error messages at once:
79
+ *
80
+ * ```html
81
+ * <!-- attribute-style usage -->
82
+ * <div ng-messages="myForm.myField.$error" ng-messages-multiple>...</div>
83
+ *
84
+ * <!-- element-style usage -->
85
+ * <ng-messages for="myForm.myField.$error" multiple>...</ng-messages>
86
+ * ```
87
+ *
88
+ * ## Reusing and Overriding Messages
89
+ * In addition to prioritization, ngMessages also allows for including messages from a remote or an inline
90
+ * template. This allows for generic collection of messages to be reused across multiple parts of an
91
+ * application.
92
+ *
93
+ * ```html
94
+ * <script type="text/ng-template" id="error-messages">
95
+ * <div ng-message="required">This field is required</div>
96
+ * <div ng-message="minlength">This field is too short</div>
97
+ * </script>
98
+ *
99
+ * <div ng-messages="myForm.myField.$error" role="alert">
100
+ * <div ng-messages-include="error-messages"></div>
101
+ * </div>
102
+ * ```
103
+ *
104
+ * However, including generic messages may not be useful enough to match all input fields, therefore,
105
+ * `ngMessages` provides the ability to override messages defined in the remote template by redefining
106
+ * them within the directive container.
107
+ *
108
+ * ```html
109
+ * <!-- a generic template of error messages known as "my-custom-messages" -->
110
+ * <script type="text/ng-template" id="my-custom-messages">
111
+ * <div ng-message="required">This field is required</div>
112
+ * <div ng-message="minlength">This field is too short</div>
113
+ * </script>
114
+ *
115
+ * <form name="myForm">
116
+ * <label>
117
+ * Email address
118
+ * <input type="email"
119
+ * id="email"
120
+ * name="myEmail"
121
+ * ng-model="email"
122
+ * minlength="5"
123
+ * required />
124
+ * </label>
125
+ * <!-- any ng-message elements that appear BEFORE the ng-messages-include will
126
+ * override the messages present in the ng-messages-include template -->
127
+ * <div ng-messages="myForm.myEmail.$error" role="alert">
128
+ * <!-- this required message has overridden the template message -->
129
+ * <div ng-message="required">You did not enter your email address</div>
130
+ *
131
+ * <!-- this is a brand new message and will appear last in the prioritization -->
132
+ * <div ng-message="email">Your email address is invalid</div>
133
+ *
134
+ * <!-- and here are the generic error messages -->
135
+ * <div ng-messages-include="my-custom-messages"></div>
136
+ * </div>
137
+ * </form>
138
+ * ```
139
+ *
140
+ * In the example HTML code above the message that is set on required will override the corresponding
141
+ * required message defined within the remote template. Therefore, with particular input fields (such
142
+ * email addresses, date fields, autocomplete inputs, etc...), specialized error messages can be applied
143
+ * while more generic messages can be used to handle other, more general input errors.
144
+ *
145
+ * ## Dynamic Messaging
146
+ * ngMessages also supports using expressions to dynamically change key values. Using arrays and
147
+ * repeaters to list messages is also supported. This means that the code below will be able to
148
+ * fully adapt itself and display the appropriate message when any of the expression data changes:
149
+ *
150
+ * ```html
151
+ * <form name="myForm">
152
+ * <label>
153
+ * Email address
154
+ * <input type="email"
155
+ * name="myEmail"
156
+ * ng-model="email"
157
+ * minlength="5"
158
+ * required />
159
+ * </label>
160
+ * <div ng-messages="myForm.myEmail.$error" role="alert">
161
+ * <div ng-message="required">You did not enter your email address</div>
162
+ * <div ng-repeat="errorMessage in errorMessages">
163
+ * <!-- use ng-message-exp for a message whose key is given by an expression -->
164
+ * <div ng-message-exp="errorMessage.type">{{ errorMessage.text }}</div>
165
+ * </div>
166
+ * </div>
167
+ * </form>
168
+ * ```
169
+ *
170
+ * The `errorMessage.type` expression can be a string value or it can be an array so
171
+ * that multiple errors can be associated with a single error message:
172
+ *
173
+ * ```html
174
+ * <label>
175
+ * Email address
176
+ * <input type="email"
177
+ * ng-model="data.email"
178
+ * name="myEmail"
179
+ * ng-minlength="5"
180
+ * ng-maxlength="100"
181
+ * required />
182
+ * </label>
183
+ * <div ng-messages="myForm.myEmail.$error" role="alert">
184
+ * <div ng-message-exp="'required'">You did not enter your email address</div>
185
+ * <div ng-message-exp="['minlength', 'maxlength']">
186
+ * Your email must be between 5 and 100 characters long
187
+ * </div>
188
+ * </div>
189
+ * ```
190
+ *
191
+ * Feel free to use other structural directives such as ng-if and ng-switch to further control
192
+ * what messages are active and when. Be careful, if you place ng-message on the same element
193
+ * as these structural directives, AngularJS may not be able to determine if a message is active
194
+ * or not. Therefore it is best to place the ng-message on a child element of the structural
195
+ * directive.
196
+ *
197
+ * ```html
198
+ * <div ng-messages="myForm.myEmail.$error" role="alert">
199
+ * <div ng-if="showRequiredError">
200
+ * <div ng-message="required">Please enter something</div>
201
+ * </div>
202
+ * </div>
203
+ * ```
204
+ *
205
+ * ## Animations
206
+ * If the `ngAnimate` module is active within the application then the `ngMessages`, `ngMessage` and
207
+ * `ngMessageExp` directives will trigger animations whenever any messages are added and removed from
208
+ * the DOM by the `ngMessages` directive.
209
+ *
210
+ * Whenever the `ngMessages` directive contains one or more visible messages then the `.ng-active` CSS
211
+ * class will be added to the element. The `.ng-inactive` CSS class will be applied when there are no
212
+ * messages present. Therefore, CSS transitions and keyframes as well as JavaScript animations can
213
+ * hook into the animations whenever these classes are added/removed.
214
+ *
215
+ * Let's say that our HTML code for our messages container looks like so:
216
+ *
217
+ * ```html
218
+ * <div ng-messages="myMessages" class="my-messages" role="alert">
219
+ * <div ng-message="alert" class="some-message">...</div>
220
+ * <div ng-message="fail" class="some-message">...</div>
221
+ * </div>
222
+ * ```
223
+ *
224
+ * Then the CSS animation code for the message container looks like so:
225
+ *
226
+ * ```css
227
+ * .my-messages {
228
+ * transition:1s linear all;
229
+ * }
230
+ * .my-messages.ng-active {
231
+ * // messages are visible
232
+ * }
233
+ * .my-messages.ng-inactive {
234
+ * // messages are hidden
235
+ * }
236
+ * ```
237
+ *
238
+ * Whenever an inner message is attached (becomes visible) or removed (becomes hidden) then the enter
239
+ * and leave animation is triggered for each particular element bound to the `ngMessage` directive.
240
+ *
241
+ * Therefore, the CSS code for the inner messages looks like so:
242
+ *
243
+ * ```css
244
+ * .some-message {
245
+ * transition:1s linear all;
246
+ * }
247
+ *
248
+ * .some-message.ng-enter {}
249
+ * .some-message.ng-enter.ng-enter-active {}
250
+ *
251
+ * .some-message.ng-leave {}
252
+ * .some-message.ng-leave.ng-leave-active {}
253
+ * ```
254
+ *
255
+ * {@link ngAnimate See the ngAnimate docs} to learn how to use JavaScript animations or to learn
256
+ * more about ngAnimate.
257
+ *
258
+ * ## Displaying a default message
259
+ * If the ngMessages renders no inner ngMessage directive (i.e. when none of the truthy
260
+ * keys are matched by a defined message), then it will render a default message
261
+ * using the {@link ngMessageDefault} directive.
262
+ * Note that matched messages will always take precedence over unmatched messages. That means
263
+ * the default message will not be displayed when another message is matched. This is also
264
+ * true for `ng-messages-multiple`.
265
+ *
266
+ * ```html
267
+ * <div ng-messages="myForm.myField.$error" role="alert">
268
+ * <div ng-message="required">This field is required</div>
269
+ * <div ng-message="minlength">This field is too short</div>
270
+ * <div ng-message-default>This field has an input error</div>
271
+ * </div>
272
+ * ```
273
+ *
274
+
275
+ */
276
+
277
+ export function initMessageModule() {
278
+ window.angular
279
+ .module("ngMessages", [])
280
+
281
+ /**
282
+ * @ngdoc directive
283
+ * @module ngMessages
284
+ * @name ngMessages
285
+ * @restrict AE
286
+ *
287
+ * @description
288
+ * `ngMessages` is a directive that is designed to show and hide messages based on the state
289
+ * of a key/value object that it listens on. The directive itself complements error message
290
+ * reporting with the `ngModel` $error object (which stores a key/value state of validation errors).
291
+ *
292
+ * `ngMessages` manages the state of internal messages within its container element. The internal
293
+ * messages use the `ngMessage` directive and will be inserted/removed from the page depending
294
+ * on if they're present within the key/value object. By default, only one message will be displayed
295
+ * at a time and this depends on the prioritization of the messages within the template. (This can
296
+ * be changed by using the `ng-messages-multiple` or `multiple` attribute on the directive container.)
297
+ *
298
+ * A remote template can also be used (With {@link ngMessagesInclude}) to promote message
299
+ * reusability and messages can also be overridden.
300
+ *
301
+ * A default message can also be displayed when no `ngMessage` directive is inserted, using the
302
+ * {@link ngMessageDefault} directive.
303
+ *
304
+ * {@link module:ngMessages Click here} to learn more about `ngMessages` and `ngMessage`.
305
+ *
306
+ * @usage
307
+ * ```html
308
+ * <!-- using attribute directives -->
309
+ * <ANY ng-messages="expression" role="alert">
310
+ * <ANY ng-message="stringValue">...</ANY>
311
+ * <ANY ng-message="stringValue1, stringValue2, ...">...</ANY>
312
+ * <ANY ng-message-exp="expressionValue">...</ANY>
313
+ * <ANY ng-message-default>...</ANY>
314
+ * </ANY>
315
+ *
316
+ * <!-- or by using element directives -->
317
+ * <ng-messages for="expression" role="alert">
318
+ * <ng-message when="stringValue">...</ng-message>
319
+ * <ng-message when="stringValue1, stringValue2, ...">...</ng-message>
320
+ * <ng-message when-exp="expressionValue">...</ng-message>
321
+ * <ng-message-default>...</ng-message-default>
322
+ * </ng-messages>
323
+ * ```
324
+ *
325
+ * @param {string} ngMessages an AngularJS expression evaluating to a key/value object
326
+ * (this is typically the $error object on an ngModel instance).
327
+ * @param {string=} ngMessagesMultiple|multiple when set, all messages will be displayed with true
328
+ *
329
+ * @example
330
+ * <example name="ngMessages-directive" module="ngMessagesExample"
331
+ * deps="angular-messages.js"
332
+ * animations="true" fixBase="true">
333
+ * <file name="index.html">
334
+ * <form name="myForm">
335
+ * <label>
336
+ * Enter your name:
337
+ * <input type="text"
338
+ * name="myName"
339
+ * ng-model="name"
340
+ * ng-minlength="5"
341
+ * ng-maxlength="20"
342
+ * required />
343
+ * </label>
344
+ * <pre>myForm.myName.$error = {{ myForm.myName.$error | json }}</pre>
345
+ *
346
+ * <div ng-messages="myForm.myName.$error" style="color:maroon" role="alert">
347
+ * <div ng-message="required">You did not enter a field</div>
348
+ * <div ng-message="minlength">Your field is too short</div>
349
+ * <div ng-message="maxlength">Your field is too long</div>
350
+ * <div ng-message-default>This field has an input error</div>
351
+ * </div>
352
+ * </form>
353
+ * </file>
354
+ * <file name="script.js">
355
+ * angular.module('ngMessagesExample', ['ngMessages']);
356
+ * </file>
357
+ * </example>
358
+ */
359
+ .directive("ngMessages", [
360
+ "$animate",
361
+ function ($animate) {
362
+ const ACTIVE_CLASS = "ng-active";
363
+ const INACTIVE_CLASS = "ng-inactive";
364
+
365
+ return {
366
+ require: "ngMessages",
367
+ restrict: "AE",
368
+ controller: [
369
+ "$element",
370
+ "$scope",
371
+ "$attrs",
372
+ function NgMessagesCtrl($element, $scope, $attrs) {
373
+ const ctrl = this;
374
+ let latestKey = 0;
375
+ let nextAttachId = 0;
376
+
377
+ this.getAttachId = function getAttachId() {
378
+ return nextAttachId++;
379
+ };
380
+
381
+ const messages = (this.messages = {});
382
+ let renderLater;
383
+ let cachedCollection;
384
+
385
+ this.render = function (collection) {
386
+ collection = collection || {};
387
+
388
+ renderLater = false;
389
+ cachedCollection = collection;
390
+
391
+ // this is true if the attribute is empty or if the attribute value is truthy
392
+ const multiple =
393
+ isAttrTruthy($scope, $attrs.ngMessagesMultiple) ||
394
+ isAttrTruthy($scope, $attrs.multiple);
395
+
396
+ const unmatchedMessages = [];
397
+ const matchedKeys = {};
398
+ let truthyKeys = 0;
399
+ let messageItem = ctrl.head;
400
+ let messageFound = false;
401
+ let totalMessages = 0;
402
+
403
+ // we use != instead of !== to allow for both undefined and null values
404
+ while (messageItem != null) {
405
+ totalMessages++;
406
+ const messageCtrl = messageItem.message;
407
+
408
+ let messageUsed = false;
409
+ if (!messageFound) {
410
+ forEach(collection, (value, key) => {
411
+ if (truthy(value) && !messageUsed) {
412
+ truthyKeys++;
413
+
414
+ if (messageCtrl.test(key)) {
415
+ // this is to prevent the same error name from showing up twice
416
+ if (matchedKeys[key]) return;
417
+ matchedKeys[key] = true;
418
+
419
+ messageUsed = true;
420
+ messageCtrl.attach();
421
+ }
422
+ }
423
+ });
424
+ }
425
+
426
+ if (messageUsed) {
427
+ // unless we want to display multiple messages then we should
428
+ // set a flag here to avoid displaying the next message in the list
429
+ messageFound = !multiple;
430
+ } else {
431
+ unmatchedMessages.push(messageCtrl);
432
+ }
433
+
434
+ messageItem = messageItem.next;
435
+ }
436
+
437
+ forEach(unmatchedMessages, (messageCtrl) => {
438
+ messageCtrl.detach();
439
+ });
440
+
441
+ const messageMatched =
442
+ unmatchedMessages.length !== totalMessages;
443
+ const attachDefault =
444
+ ctrl.default && !messageMatched && truthyKeys > 0;
445
+
446
+ if (attachDefault) {
447
+ ctrl.default.attach();
448
+ } else if (ctrl.default) {
449
+ ctrl.default.detach();
450
+ }
451
+
452
+ if (messageMatched || attachDefault) {
453
+ $animate.setClass($element, ACTIVE_CLASS, INACTIVE_CLASS);
454
+ } else {
455
+ $animate.setClass($element, INACTIVE_CLASS, ACTIVE_CLASS);
456
+ }
457
+ };
458
+
459
+ $scope.$watchCollection(
460
+ $attrs.ngMessages || $attrs.for,
461
+ ctrl.render,
462
+ );
463
+
464
+ this.reRender = function () {
465
+ if (!renderLater) {
466
+ renderLater = true;
467
+ $scope.$evalAsync(() => {
468
+ if (renderLater && cachedCollection) {
469
+ ctrl.render(cachedCollection);
470
+ }
471
+ });
472
+ }
473
+ };
474
+
475
+ this.register = function (comment, messageCtrl, isDefault) {
476
+ if (isDefault) {
477
+ ctrl.default = messageCtrl;
478
+ } else {
479
+ const nextKey = latestKey.toString();
480
+ messages[nextKey] = {
481
+ message: messageCtrl,
482
+ };
483
+ insertMessageNode($element[0], comment, nextKey);
484
+ comment.$$ngMessageNode = nextKey;
485
+ latestKey++;
486
+ }
487
+
488
+ ctrl.reRender();
489
+ };
490
+
491
+ this.deregister = function (comment, isDefault) {
492
+ if (isDefault) {
493
+ delete ctrl.default;
494
+ } else {
495
+ const key = comment.$$ngMessageNode;
496
+ delete comment.$$ngMessageNode;
497
+ removeMessageNode($element[0], comment, key);
498
+ delete messages[key];
499
+ }
500
+ ctrl.reRender();
501
+ };
502
+
503
+ function findPreviousMessage(parent, comment) {
504
+ let prevNode = comment;
505
+ const parentLookup = [];
506
+
507
+ while (prevNode && prevNode !== parent) {
508
+ const prevKey = prevNode.$$ngMessageNode;
509
+ if (prevKey && prevKey.length) {
510
+ return messages[prevKey];
511
+ }
512
+
513
+ // dive deeper into the DOM and examine its children for any ngMessage
514
+ // comments that may be in an element that appears deeper in the list
515
+ if (
516
+ prevNode.childNodes.length &&
517
+ parentLookup.indexOf(prevNode) === -1
518
+ ) {
519
+ parentLookup.push(prevNode);
520
+ prevNode =
521
+ prevNode.childNodes[prevNode.childNodes.length - 1];
522
+ } else if (prevNode.previousSibling) {
523
+ prevNode = prevNode.previousSibling;
524
+ } else {
525
+ prevNode = prevNode.parentNode;
526
+ parentLookup.push(prevNode);
527
+ }
528
+ }
529
+ }
530
+
531
+ function insertMessageNode(parent, comment, key) {
532
+ const messageNode = messages[key];
533
+ if (!ctrl.head) {
534
+ ctrl.head = messageNode;
535
+ } else {
536
+ const match = findPreviousMessage(parent, comment);
537
+ if (match) {
538
+ messageNode.next = match.next;
539
+ match.next = messageNode;
540
+ } else {
541
+ messageNode.next = ctrl.head;
542
+ ctrl.head = messageNode;
543
+ }
544
+ }
545
+ }
546
+
547
+ function removeMessageNode(parent, comment, key) {
548
+ const messageNode = messages[key];
549
+
550
+ // This message node may have already been removed by a call to deregister()
551
+ if (!messageNode) return;
552
+
553
+ const match = findPreviousMessage(parent, comment);
554
+ if (match) {
555
+ match.next = messageNode.next;
556
+ } else {
557
+ ctrl.head = messageNode.next;
558
+ }
559
+ }
560
+ },
561
+ ],
562
+ };
563
+
564
+ function isAttrTruthy(scope, attr) {
565
+ return (
566
+ (isString(attr) && attr.length === 0) || // empty attribute
567
+ truthy(scope.$eval(attr))
568
+ );
569
+ }
570
+
571
+ function truthy(val) {
572
+ return isString(val) ? val.length : !!val;
573
+ }
574
+ },
575
+ ])
576
+
577
+ /**
578
+ * @ngdoc directive
579
+ * @name ngMessagesInclude
580
+ * @restrict AE
581
+ * @scope
582
+ *
583
+ * @description
584
+ * `ngMessagesInclude` is a directive with the purpose to import existing ngMessage template
585
+ * code from a remote template and place the downloaded template code into the exact spot
586
+ * that the ngMessagesInclude directive is placed within the ngMessages container. This allows
587
+ * for a series of pre-defined messages to be reused and also allows for the developer to
588
+ * determine what messages are overridden due to the placement of the ngMessagesInclude directive.
589
+ *
590
+ * @usage
591
+ * ```html
592
+ * <!-- using attribute directives -->
593
+ * <ANY ng-messages="expression" role="alert">
594
+ * <ANY ng-messages-include="remoteTplString">...</ANY>
595
+ * </ANY>
596
+ *
597
+ * <!-- or by using element directives -->
598
+ * <ng-messages for="expression" role="alert">
599
+ * <ng-messages-include src="expressionValue1">...</ng-messages-include>
600
+ * </ng-messages>
601
+ * ```
602
+ *
603
+ * {@link module:ngMessages Click here} to learn more about `ngMessages` and `ngMessage`.
604
+ *
605
+ * @param {string} ngMessagesInclude|src a string value corresponding to the remote template.
606
+ */
607
+ .directive("ngMessagesInclude", [
608
+ "$templateRequest",
609
+ "$document",
610
+ "$compile",
611
+ function ($templateRequest, $document, $compile) {
612
+ return {
613
+ restrict: "AE",
614
+ require: "^^ngMessages", // we only require this for validation sake
615
+ link($scope, element, attrs) {
616
+ const src = attrs.ngMessagesInclude || attrs.src;
617
+ $templateRequest(src).then((html) => {
618
+ if ($scope.$$destroyed) return;
619
+
620
+ if (isString(html) && !html.trim()) {
621
+ // Empty template - nothing to compile
622
+ replaceElementWithMarker(element, src);
623
+ } else {
624
+ // Non-empty template - compile and link
625
+ $compile(html)($scope, (contents) => {
626
+ element.after(contents);
627
+ replaceElementWithMarker(element, src);
628
+ });
629
+ }
630
+ });
631
+ },
632
+ };
633
+
634
+ // Helpers
635
+ function replaceElementWithMarker(element, src) {
636
+ // A comment marker is placed for debugging purposes
637
+ const comment = $compile.$$createComment
638
+ ? $compile.$$createComment("ngMessagesInclude", src)
639
+ : $document[0].createComment(` ngMessagesInclude: ${src} `);
640
+ const marker = jqLite(comment);
641
+ element.after(marker);
642
+
643
+ // Don't pollute the DOM anymore by keeping an empty directive element
644
+ element.remove();
645
+ }
646
+ },
647
+ ])
648
+
649
+ /**
650
+ * @ngdoc directive
651
+ * @name ngMessage
652
+ * @restrict AE
653
+ * @scope
654
+ * @priority 1
655
+ *
656
+ * @description
657
+ * `ngMessage` is a directive with the purpose to show and hide a particular message.
658
+ * For `ngMessage` to operate, a parent `ngMessages` directive on a parent DOM element
659
+ * must be situated since it determines which messages are visible based on the state
660
+ * of the provided key/value map that `ngMessages` listens on.
661
+ *
662
+ * More information about using `ngMessage` can be found in the
663
+ * {@link module:ngMessages `ngMessages` module documentation}.
664
+ *
665
+ * @usage
666
+ * ```html
667
+ * <!-- using attribute directives -->
668
+ * <ANY ng-messages="expression" role="alert">
669
+ * <ANY ng-message="stringValue">...</ANY>
670
+ * <ANY ng-message="stringValue1, stringValue2, ...">...</ANY>
671
+ * </ANY>
672
+ *
673
+ * <!-- or by using element directives -->
674
+ * <ng-messages for="expression" role="alert">
675
+ * <ng-message when="stringValue">...</ng-message>
676
+ * <ng-message when="stringValue1, stringValue2, ...">...</ng-message>
677
+ * </ng-messages>
678
+ * ```
679
+ *
680
+ * @param {string} ngMessage|when a string value corresponding to the message key.
681
+ */
682
+ .directive("ngMessage", ngMessageDirectiveFactory())
683
+
684
+ /**
685
+ * @ngdoc directive
686
+ * @name ngMessageExp
687
+ * @restrict AE
688
+ * @priority 1
689
+ * @scope
690
+ *
691
+ * @description
692
+ * `ngMessageExp` is the same as {@link directive:ngMessage `ngMessage`}, but instead of a static
693
+ * value, it accepts an expression to be evaluated for the message key.
694
+ *
695
+ * @usage
696
+ * ```html
697
+ * <!-- using attribute directives -->
698
+ * <ANY ng-messages="expression">
699
+ * <ANY ng-message-exp="expressionValue">...</ANY>
700
+ * </ANY>
701
+ *
702
+ * <!-- or by using element directives -->
703
+ * <ng-messages for="expression">
704
+ * <ng-message when-exp="expressionValue">...</ng-message>
705
+ * </ng-messages>
706
+ * ```
707
+ *
708
+ * {@link module:ngMessages Click here} to learn more about `ngMessages` and `ngMessage`.
709
+ *
710
+ * @param {string} ngMessageExp|whenExp an expression value corresponding to the message key.
711
+ */
712
+ .directive("ngMessageExp", ngMessageDirectiveFactory())
713
+
714
+ /**
715
+ * @ngdoc directive
716
+ * @name ngMessageDefault
717
+ * @restrict AE
718
+ * @scope
719
+ *
720
+ * @description
721
+ * `ngMessageDefault` is a directive with the purpose to show and hide a default message for
722
+ * {@link directive:ngMessages}, when none of provided messages matches.
723
+ *
724
+ * More information about using `ngMessageDefault` can be found in the
725
+ * {@link module:ngMessages `ngMessages` module documentation}.
726
+ *
727
+ * @usage
728
+ * ```html
729
+ * <!-- using attribute directives -->
730
+ * <ANY ng-messages="expression" role="alert">
731
+ * <ANY ng-message="stringValue">...</ANY>
732
+ * <ANY ng-message="stringValue1, stringValue2, ...">...</ANY>
733
+ * <ANY ng-message-default>...</ANY>
734
+ * </ANY>
735
+ *
736
+ * <!-- or by using element directives -->
737
+ * <ng-messages for="expression" role="alert">
738
+ * <ng-message when="stringValue">...</ng-message>
739
+ * <ng-message when="stringValue1, stringValue2, ...">...</ng-message>
740
+ * <ng-message-default>...</ng-message-default>
741
+ * </ng-messages>
742
+ *
743
+ */
744
+ .directive("ngMessageDefault", ngMessageDirectiveFactory(true));
745
+
746
+ function ngMessageDirectiveFactory(isDefault) {
747
+ return [
748
+ "$animate",
749
+ function ($animate) {
750
+ return {
751
+ restrict: "AE",
752
+ transclude: "element",
753
+ priority: 1, // must run before ngBind, otherwise the text is set on the comment
754
+ terminal: true,
755
+ require: "^^ngMessages",
756
+ link(scope, element, attrs, ngMessagesCtrl, $transclude) {
757
+ let commentNode;
758
+ let records;
759
+ let staticExp;
760
+ let dynamicExp;
761
+
762
+ if (!isDefault) {
763
+ commentNode = element[0];
764
+ staticExp = attrs.ngMessage || attrs.when;
765
+ dynamicExp = attrs.ngMessageExp || attrs.whenExp;
766
+
767
+ const assignRecords = function (items) {
768
+ records = items
769
+ ? isArray(items)
770
+ ? items
771
+ : items.split(/[\s,]+/)
772
+ : null;
773
+ ngMessagesCtrl.reRender();
774
+ };
775
+
776
+ if (dynamicExp) {
777
+ assignRecords(scope.$eval(dynamicExp));
778
+ scope.$watchCollection(dynamicExp, assignRecords);
779
+ } else {
780
+ assignRecords(staticExp);
781
+ }
782
+ }
783
+
784
+ let currentElement;
785
+ let messageCtrl;
786
+ ngMessagesCtrl.register(
787
+ commentNode,
788
+ (messageCtrl = {
789
+ test(name) {
790
+ return contains(records, name);
791
+ },
792
+ attach() {
793
+ if (!currentElement) {
794
+ $transclude((elm, newScope) => {
795
+ $animate.enter(elm, null, element);
796
+ currentElement = elm;
797
+
798
+ // Each time we attach this node to a message we get a new id that we can match
799
+ // when we are destroying the node later.
800
+ const $$attachId = (currentElement.$$attachId =
801
+ ngMessagesCtrl.getAttachId());
802
+
803
+ // in the event that the element or a parent element is destroyed
804
+ // by another structural directive then it's time
805
+ // to deregister the message from the controller
806
+ currentElement.on("$destroy", () => {
807
+ // If the message element was removed via a call to `detach` then `currentElement` will be null
808
+ // So this handler only handles cases where something else removed the message element.
809
+ if (
810
+ currentElement &&
811
+ currentElement.$$attachId === $$attachId
812
+ ) {
813
+ ngMessagesCtrl.deregister(commentNode, isDefault);
814
+ messageCtrl.detach();
815
+ }
816
+ newScope.$destroy();
817
+ });
818
+ });
819
+ }
820
+ },
821
+ detach() {
822
+ if (currentElement) {
823
+ const elm = currentElement;
824
+ currentElement = null;
825
+ $animate.leave(elm);
826
+ }
827
+ },
828
+ }),
829
+ isDefault,
830
+ );
831
+
832
+ // We need to ensure that this directive deregisters itself when it no longer exists
833
+ // Normally this is done when the attached element is destroyed; but if this directive
834
+ // gets removed before we attach the message to the DOM there is nothing to watch
835
+ // in which case we must deregister when the containing scope is destroyed.
836
+ scope.$on("$destroy", () => {
837
+ ngMessagesCtrl.deregister(commentNode, isDefault);
838
+ });
839
+ },
840
+ };
841
+ },
842
+ ];
843
+
844
+ function contains(collection, key) {
845
+ if (collection) {
846
+ return isArray(collection)
847
+ ? collection.indexOf(key) >= 0
848
+ : Object.prototype.hasOwnProperty.call(collection, key);
849
+ }
850
+ }
851
+ }
852
+ }