@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,670 @@
1
+ import { jqLite, jqLiteRemove, startingTag } from "../jqLite";
2
+ import {
3
+ equals,
4
+ forEach,
5
+ hashKey,
6
+ includes,
7
+ isArray,
8
+ isArrayLike,
9
+ isDefined,
10
+ minErr,
11
+ } from "../core/utils";
12
+
13
+ const ngOptionsMinErr = minErr("ngOptions");
14
+
15
+ /**
16
+ * @ngdoc directive
17
+ * @name ngOptions
18
+ * @restrict A
19
+ *
20
+ * @description
21
+ *
22
+ * The `ngOptions` attribute can be used to dynamically generate a list of `<option>`
23
+ * elements for the `<select>` element using the array or object obtained by evaluating the
24
+ * `ngOptions` comprehension expression.
25
+ *
26
+ * In many cases, {@link ng.directive:ngRepeat ngRepeat} can be used on `<option>` elements instead of
27
+ * `ngOptions` to achieve a similar result. However, `ngOptions` provides some benefits:
28
+ * - more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
29
+ * comprehension expression
30
+ * - reduced memory consumption by not creating a new scope for each repeated instance
31
+ * - increased render speed by creating the options in a documentFragment instead of individually
32
+ *
33
+ * When an item in the `<select>` menu is selected, the array element or object property
34
+ * represented by the selected option will be bound to the model identified by the `ngModel`
35
+ * directive.
36
+ *
37
+ * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
38
+ * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
39
+ * option. See example below for demonstration.
40
+ *
41
+ * ## Complex Models (objects or collections)
42
+ *
43
+ * By default, `ngModel` watches the model by reference, not value. This is important to know when
44
+ * binding the select to a model that is an object or a collection.
45
+ *
46
+ * One issue occurs if you want to preselect an option. For example, if you set
47
+ * the model to an object that is equal to an object in your collection, `ngOptions` won't be able to set the selection,
48
+ * because the objects are not identical. So by default, you should always reference the item in your collection
49
+ * for preselections, e.g.: `$scope.selected = $scope.collection[3]`.
50
+ *
51
+ * Another solution is to use a `track by` clause, because then `ngOptions` will track the identity
52
+ * of the item not by reference, but by the result of the `track by` expression. For example, if your
53
+ * collection items have an id property, you would `track by item.id`.
54
+ *
55
+ * A different issue with objects or collections is that ngModel won't detect if an object property or
56
+ * a collection item changes. For that reason, `ngOptions` additionally watches the model using
57
+ * `$watchCollection`, when the expression contains a `track by` clause or the the select has the `multiple` attribute.
58
+ * This allows ngOptions to trigger a re-rendering of the options even if the actual object/collection
59
+ * has not changed identity, but only a property on the object or an item in the collection changes.
60
+ *
61
+ * Note that `$watchCollection` does a shallow comparison of the properties of the object (or the items in the collection
62
+ * if the model is an array). This means that changing a property deeper than the first level inside the
63
+ * object/collection will not trigger a re-rendering.
64
+ *
65
+ * ## `select` **`as`**
66
+ *
67
+ * Using `select` **`as`** will bind the result of the `select` expression to the model, but
68
+ * the value of the `<select>` and `<option>` html elements will be either the index (for array data sources)
69
+ * or property name (for object data sources) of the value within the collection. If a **`track by`** expression
70
+ * is used, the result of that expression will be set as the value of the `option` and `select` elements.
71
+ *
72
+ *
73
+ * ### `select` **`as`** and **`track by`**
74
+ *
75
+ * <div class="alert alert-warning">
76
+ * Be careful when using `select` **`as`** and **`track by`** in the same expression.
77
+ * </div>
78
+ *
79
+ * Given this array of items on the $scope:
80
+ *
81
+ * ```js
82
+ * $scope.items = [{
83
+ * id: 1,
84
+ * label: 'aLabel',
85
+ * subItem: { name: 'aSubItem' }
86
+ * }, {
87
+ * id: 2,
88
+ * label: 'bLabel',
89
+ * subItem: { name: 'bSubItem' }
90
+ * }];
91
+ * ```
92
+ *
93
+ * This will work:
94
+ *
95
+ * ```html
96
+ * <select ng-options="item as item.label for item in items track by item.id" ng-model="selected"></select>
97
+ * ```
98
+ * ```js
99
+ * $scope.selected = $scope.items[0];
100
+ * ```
101
+ *
102
+ * but this will not work:
103
+ *
104
+ * ```html
105
+ * <select ng-options="item.subItem as item.label for item in items track by item.id" ng-model="selected"></select>
106
+ * ```
107
+ * ```js
108
+ * $scope.selected = $scope.items[0].subItem;
109
+ * ```
110
+ *
111
+ * In both examples, the **`track by`** expression is applied successfully to each `item` in the
112
+ * `items` array. Because the selected option has been set programmatically in the controller, the
113
+ * **`track by`** expression is also applied to the `ngModel` value. In the first example, the
114
+ * `ngModel` value is `items[0]` and the **`track by`** expression evaluates to `items[0].id` with
115
+ * no issue. In the second example, the `ngModel` value is `items[0].subItem` and the **`track by`**
116
+ * expression evaluates to `items[0].subItem.id` (which is undefined). As a result, the model value
117
+ * is not matched against any `<option>` and the `<select>` appears as having no selected value.
118
+ *
119
+ *
120
+ * @param {string} ngModel Assignable AngularJS expression to data-bind to.
121
+ * @param {comprehension_expression} ngOptions in one of the following forms:
122
+ *
123
+ * * for array data sources:
124
+ * * `label` **`for`** `value` **`in`** `array`
125
+ * * `select` **`as`** `label` **`for`** `value` **`in`** `array`
126
+ * * `label` **`group by`** `group` **`for`** `value` **`in`** `array`
127
+ * * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array`
128
+ * * `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
129
+ * * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
130
+ * * `label` **`for`** `value` **`in`** `array` | orderBy:`orderexpr` **`track by`** `trackexpr`
131
+ * (for including a filter with `track by`)
132
+ * * for object data sources:
133
+ * * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
134
+ * * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
135
+ * * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object`
136
+ * * `label` **`disable when`** `disable` **`for (`**`key`**`,`** `value`**`) in`** `object`
137
+ * * `select` **`as`** `label` **`group by`** `group`
138
+ * **`for` `(`**`key`**`,`** `value`**`) in`** `object`
139
+ * * `select` **`as`** `label` **`disable when`** `disable`
140
+ * **`for` `(`**`key`**`,`** `value`**`) in`** `object`
141
+ *
142
+ * Where:
143
+ *
144
+ * * `array` / `object`: an expression which evaluates to an array / object to iterate over.
145
+ * * `value`: local variable which will refer to each item in the `array` or each property value
146
+ * of `object` during iteration.
147
+ * * `key`: local variable which will refer to a property name in `object` during iteration.
148
+ * * `label`: The result of this expression will be the label for `<option>` element. The
149
+ * `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`).
150
+ * * `select`: The result of this expression will be bound to the model of the parent `<select>`
151
+ * element. If not specified, `select` expression will default to `value`.
152
+ * * `group`: The result of this expression will be used to group options using the `<optgroup>`
153
+ * DOM element.
154
+ * * `disable`: The result of this expression will be used to disable the rendered `<option>`
155
+ * element. Return `true` to disable.
156
+ * * `trackexpr`: Used when working with an array of objects. The result of this expression will be
157
+ * used to identify the objects in the array. The `trackexpr` will most likely refer to the
158
+ * `value` variable (e.g. `value.propertyName`). With this the selection is preserved
159
+ * even when the options are recreated (e.g. reloaded from the server).
160
+ * @param {string=} name Property name of the form under which the control is published.
161
+ * @param {string=} required The control is considered valid only if value is entered.
162
+ * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
163
+ * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
164
+ * `required` when you want to data-bind to the `required` attribute.
165
+ * @param {string=} ngAttrSize sets the size of the select element dynamically. Uses the
166
+ * {@link guide/interpolation#-ngattr-for-binding-to-arbitrary-attributes ngAttr} directive.
167
+ *
168
+ */
169
+
170
+ const NG_OPTIONS_REGEXP =
171
+ /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?(?:\s+disable\s+when\s+([\s\S]+?))?\s+for\s+(?:([$\w][$\w]*)|(?:\(\s*([$\w][$\w]*)\s*,\s*([$\w][$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/;
172
+ // 1: value expression (valueFn)
173
+ // 2: label expression (displayFn)
174
+ // 3: group by expression (groupByFn)
175
+ // 4: disable when expression (disableWhenFn)
176
+ // 5: array item variable name
177
+ // 6: object item key variable name
178
+ // 7: object item value variable name
179
+ // 8: collection expression
180
+ // 9: track by expression
181
+
182
+ export const ngOptionsDirective = [
183
+ "$compile",
184
+ "$document",
185
+ "$parse",
186
+ function ($compile, $document, $parse) {
187
+ function parseOptionsExpression(optionsExp, selectElement, scope) {
188
+ const match = optionsExp.match(NG_OPTIONS_REGEXP);
189
+ if (!match) {
190
+ throw ngOptionsMinErr(
191
+ "iexp",
192
+ "Expected expression in form of " +
193
+ "'_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" +
194
+ " but got '{0}'. Element: {1}",
195
+ optionsExp,
196
+ startingTag(selectElement),
197
+ );
198
+ }
199
+
200
+ // Extract the parts from the ngOptions expression
201
+
202
+ // The variable name for the value of the item in the collection
203
+ const valueName = match[5] || match[7];
204
+ // The variable name for the key of the item in the collection
205
+ const keyName = match[6];
206
+
207
+ // An expression that generates the viewValue for an option if there is a label expression
208
+ const selectAs = / as /.test(match[0]) && match[1];
209
+ // An expression that is used to track the id of each object in the options collection
210
+ const trackBy = match[9];
211
+ // An expression that generates the viewValue for an option if there is no label expression
212
+ const valueFn = $parse(match[2] ? match[1] : valueName);
213
+ const selectAsFn = selectAs && $parse(selectAs);
214
+ const viewValueFn = selectAsFn || valueFn;
215
+ const trackByFn = trackBy && $parse(trackBy);
216
+
217
+ // Get the value by which we are going to track the option
218
+ // if we have a trackFn then use that (passing scope and locals)
219
+ // otherwise just hash the given viewValue
220
+ const getTrackByValueFn = trackBy
221
+ ? function (value, locals) {
222
+ return trackByFn(scope, locals);
223
+ }
224
+ : function getHashOfValue(value) {
225
+ return hashKey(value);
226
+ };
227
+ const getTrackByValue = function (value, key) {
228
+ return getTrackByValueFn(value, getLocals(value, key));
229
+ };
230
+
231
+ const displayFn = $parse(match[2] || match[1]);
232
+ const groupByFn = $parse(match[3] || "");
233
+ const disableWhenFn = $parse(match[4] || "");
234
+ const valuesFn = $parse(match[8]);
235
+
236
+ const locals = {};
237
+ let getLocals = keyName
238
+ ? function (value, key) {
239
+ locals[keyName] = key;
240
+ locals[valueName] = value;
241
+ return locals;
242
+ }
243
+ : function (value) {
244
+ locals[valueName] = value;
245
+ return locals;
246
+ };
247
+
248
+ function Option(selectValue, viewValue, label, group, disabled) {
249
+ this.selectValue = selectValue;
250
+ this.viewValue = viewValue;
251
+ this.label = label;
252
+ this.group = group;
253
+ this.disabled = disabled;
254
+ }
255
+
256
+ function getOptionValuesKeys(optionValues) {
257
+ let optionValuesKeys;
258
+
259
+ if (!keyName && isArrayLike(optionValues)) {
260
+ optionValuesKeys = optionValues;
261
+ } else {
262
+ // if object, extract keys, in enumeration order, unsorted
263
+ optionValuesKeys = [];
264
+ for (const itemKey in optionValues) {
265
+ if (
266
+ Object.prototype.hasOwnProperty.call(optionValues, itemKey) &&
267
+ itemKey.charAt(0) !== "$"
268
+ ) {
269
+ optionValuesKeys.push(itemKey);
270
+ }
271
+ }
272
+ }
273
+ return optionValuesKeys;
274
+ }
275
+
276
+ return {
277
+ trackBy,
278
+ getTrackByValue,
279
+ getWatchables: $parse(valuesFn, (optionValues) => {
280
+ // Create a collection of things that we would like to watch (watchedArray)
281
+ // so that they can all be watched using a single $watchCollection
282
+ // that only runs the handler once if anything changes
283
+ const watchedArray = [];
284
+ optionValues = optionValues || [];
285
+
286
+ const optionValuesKeys = getOptionValuesKeys(optionValues);
287
+ const optionValuesLength = optionValuesKeys.length;
288
+ for (let index = 0; index < optionValuesLength; index++) {
289
+ const key =
290
+ optionValues === optionValuesKeys
291
+ ? index
292
+ : optionValuesKeys[index];
293
+ const value = optionValues[key];
294
+
295
+ const locals = getLocals(value, key);
296
+ const selectValue = getTrackByValueFn(value, locals);
297
+ watchedArray.push(selectValue);
298
+
299
+ // Only need to watch the displayFn if there is a specific label expression
300
+ if (match[2] || match[1]) {
301
+ const label = displayFn(scope, locals);
302
+ watchedArray.push(label);
303
+ }
304
+
305
+ // Only need to watch the disableWhenFn if there is a specific disable expression
306
+ if (match[4]) {
307
+ const disableWhen = disableWhenFn(scope, locals);
308
+ watchedArray.push(disableWhen);
309
+ }
310
+ }
311
+ return watchedArray;
312
+ }),
313
+
314
+ getOptions() {
315
+ const optionItems = [];
316
+ const selectValueMap = {};
317
+
318
+ // The option values were already computed in the `getWatchables` fn,
319
+ // which must have been called to trigger `getOptions`
320
+ const optionValues = valuesFn(scope) || [];
321
+ const optionValuesKeys = getOptionValuesKeys(optionValues);
322
+ const optionValuesLength = optionValuesKeys.length;
323
+
324
+ for (let index = 0; index < optionValuesLength; index++) {
325
+ const key =
326
+ optionValues === optionValuesKeys
327
+ ? index
328
+ : optionValuesKeys[index];
329
+ const value = optionValues[key];
330
+ const locals = getLocals(value, key);
331
+ const viewValue = viewValueFn(scope, locals);
332
+ const selectValue = getTrackByValueFn(viewValue, locals);
333
+ const label = displayFn(scope, locals);
334
+ const group = groupByFn(scope, locals);
335
+ const disabled = disableWhenFn(scope, locals);
336
+ const optionItem = new Option(
337
+ selectValue,
338
+ viewValue,
339
+ label,
340
+ group,
341
+ disabled,
342
+ );
343
+
344
+ optionItems.push(optionItem);
345
+ selectValueMap[selectValue] = optionItem;
346
+ }
347
+
348
+ return {
349
+ items: optionItems,
350
+ selectValueMap,
351
+ getOptionFromViewValue(value) {
352
+ return selectValueMap[getTrackByValue(value)];
353
+ },
354
+ getViewValueFromOption(option) {
355
+ // If the viewValue could be an object that may be mutated by the application,
356
+ // we need to make a copy and not return the reference to the value on the option.
357
+ return trackBy
358
+ ? structuredClone(option.viewValue)
359
+ : option.viewValue;
360
+ },
361
+ };
362
+ },
363
+ };
364
+ }
365
+
366
+ // Support: IE 9 only
367
+ // We can't just jqLite('<option>') since jqLite is not smart enough
368
+ // to create it in <select> and IE barfs otherwise.
369
+ const optionTemplate = window.document.createElement("option");
370
+ const optGroupTemplate = window.document.createElement("optgroup");
371
+
372
+ function ngOptionsPostLink(scope, selectElement, attr, ctrls) {
373
+ const selectCtrl = ctrls[0];
374
+ const ngModelCtrl = ctrls[1];
375
+ const { multiple } = attr;
376
+
377
+ const children = selectElement.childNodes;
378
+
379
+ // The emptyOption allows the application developer to provide their own custom "empty"
380
+ // option when the viewValue does not match any of the option values.
381
+ for (let i = 0, ii = children.length; i < ii; i++) {
382
+ if (children[i].value === "") {
383
+ selectCtrl.hasEmptyOption = true;
384
+ selectCtrl.emptyOption = children[i];
385
+ break;
386
+ }
387
+ }
388
+
389
+ // The empty option will be compiled and rendered before we first generate the options
390
+ selectElement.empty();
391
+
392
+ const providedEmptyOption = !!selectCtrl.emptyOption;
393
+
394
+ const unknownOption = jqLite(optionTemplate.cloneNode(false));
395
+ unknownOption.val("?");
396
+
397
+ let options;
398
+ const ngOptions = parseOptionsExpression(
399
+ attr.ngOptions,
400
+ selectElement,
401
+ scope,
402
+ );
403
+ // This stores the newly created options before they are appended to the select.
404
+ // Since the contents are removed from the fragment when it is appended,
405
+ // we only need to create it once.
406
+ const listFragment = $document[0].createDocumentFragment();
407
+
408
+ // Overwrite the implementation. ngOptions doesn't use hashes
409
+ selectCtrl.generateUnknownOptionValue = () => "?";
410
+
411
+ // Update the controller methods for multiple selectable options
412
+ if (!multiple) {
413
+ selectCtrl.writeValue = function writeNgOptionsValue(value) {
414
+ // The options might not be defined yet when ngModel tries to render
415
+ if (!options) return;
416
+
417
+ const selectedOption =
418
+ selectElement[0].options[selectElement[0].selectedIndex];
419
+ const option = options.getOptionFromViewValue(value);
420
+
421
+ // Make sure to remove the selected attribute from the previously selected option
422
+ // Otherwise, screen readers might get confused
423
+ if (selectedOption) selectedOption.removeAttribute("selected");
424
+
425
+ if (option) {
426
+ // Don't update the option when it is already selected.
427
+ // For example, the browser will select the first option by default. In that case,
428
+ // most properties are set automatically - except the `selected` attribute, which we
429
+ // set always
430
+
431
+ if (selectElement[0].value !== option.selectValue) {
432
+ selectCtrl.removeUnknownOption();
433
+
434
+ selectElement[0].value = option.selectValue;
435
+ option.element.selected = true;
436
+ }
437
+
438
+ option.element.setAttribute("selected", "selected");
439
+ } else {
440
+ selectCtrl.selectUnknownOrEmptyOption(value);
441
+ }
442
+ };
443
+
444
+ selectCtrl.readValue = function readNgOptionsValue() {
445
+ const selectedOption = options.selectValueMap[selectElement.val()];
446
+
447
+ if (selectedOption && !selectedOption.disabled) {
448
+ selectCtrl.unselectEmptyOption();
449
+ selectCtrl.removeUnknownOption();
450
+ return options.getViewValueFromOption(selectedOption);
451
+ }
452
+ return null;
453
+ };
454
+
455
+ // If we are using `track by` then we must watch the tracked value on the model
456
+ // since ngModel only watches for object identity change
457
+ // FIXME: When a user selects an option, this watch will fire needlessly
458
+ if (ngOptions.trackBy) {
459
+ scope.$watch(
460
+ () => ngOptions.getTrackByValue(ngModelCtrl.$viewValue),
461
+ () => {
462
+ ngModelCtrl.$render();
463
+ },
464
+ );
465
+ }
466
+ } else {
467
+ selectCtrl.writeValue = function writeNgOptionsMultiple(values) {
468
+ // The options might not be defined yet when ngModel tries to render
469
+ if (!options) return;
470
+
471
+ // Only set `<option>.selected` if necessary, in order to prevent some browsers from
472
+ // scrolling to `<option>` elements that are outside the `<select>` element's viewport.
473
+ const selectedOptions =
474
+ (values && values.map(getAndUpdateSelectedOption)) || [];
475
+
476
+ options.items.forEach((option) => {
477
+ if (option.element.selected && !includes(selectedOptions, option)) {
478
+ option.element.selected = false;
479
+ }
480
+ });
481
+ };
482
+
483
+ selectCtrl.readValue = function readNgOptionsMultiple() {
484
+ const selectedValues = selectElement.val() || [];
485
+ const selections = [];
486
+
487
+ forEach(selectedValues, (value) => {
488
+ const option = options.selectValueMap[value];
489
+ if (option && !option.disabled)
490
+ selections.push(options.getViewValueFromOption(option));
491
+ });
492
+
493
+ return selections;
494
+ };
495
+
496
+ // If we are using `track by` then we must watch these tracked values on the model
497
+ // since ngModel only watches for object identity change
498
+ if (ngOptions.trackBy) {
499
+ scope.$watchCollection(
500
+ () => {
501
+ if (isArray(ngModelCtrl.$viewValue)) {
502
+ return ngModelCtrl.$viewValue.map((value) =>
503
+ ngOptions.getTrackByValue(value),
504
+ );
505
+ }
506
+ },
507
+ () => {
508
+ ngModelCtrl.$render();
509
+ },
510
+ );
511
+ }
512
+ }
513
+
514
+ if (providedEmptyOption) {
515
+ // compile the element since there might be bindings in it
516
+ $compile(selectCtrl.emptyOption)(scope);
517
+
518
+ selectElement.prepend(selectCtrl.emptyOption);
519
+
520
+ if (selectCtrl.emptyOption[0].nodeType === Node.COMMENT_NODE) {
521
+ // This means the empty option has currently no actual DOM node, probably because
522
+ // it has been modified by a transclusion directive.
523
+ selectCtrl.hasEmptyOption = false;
524
+
525
+ // Redefine the registerOption function, which will catch
526
+ // options that are added by ngIf etc. (rendering of the node is async because of
527
+ // lazy transclusion)
528
+ selectCtrl.registerOption = function (optionScope, optionEl) {
529
+ if (optionEl.val() === "") {
530
+ selectCtrl.hasEmptyOption = true;
531
+ selectCtrl.emptyOption = optionEl;
532
+ // This ensures the new empty option is selected if previously no option was selected
533
+ ngModelCtrl.$render();
534
+
535
+ optionEl.on("$destroy", () => {
536
+ const needsRerender = selectCtrl.$isEmptyOptionSelected();
537
+
538
+ selectCtrl.hasEmptyOption = false;
539
+ selectCtrl.emptyOption = undefined;
540
+
541
+ if (needsRerender) ngModelCtrl.$render();
542
+ });
543
+ }
544
+ };
545
+ }
546
+ }
547
+
548
+ // We will re-render the option elements if the option values or labels change
549
+ scope.$watchCollection(ngOptions.getWatchables, updateOptions);
550
+
551
+ // ------------------------------------------------------------------ //
552
+
553
+ function addOptionElement(option, parent) {
554
+ const optionElement = optionTemplate.cloneNode(false);
555
+ parent.appendChild(optionElement);
556
+ updateOptionElement(option, optionElement);
557
+ }
558
+
559
+ function getAndUpdateSelectedOption(viewValue) {
560
+ const option = options.getOptionFromViewValue(viewValue);
561
+ const element = option && option.element;
562
+
563
+ if (element && !element.selected) element.selected = true;
564
+
565
+ return option;
566
+ }
567
+
568
+ function updateOptionElement(option, element) {
569
+ option.element = element;
570
+ element.disabled = option.disabled;
571
+ // Support: IE 11 only, Edge 12-13 only
572
+ // NOTE: The label must be set before the value, otherwise IE 11 & Edge create unresponsive
573
+ // selects in certain circumstances when multiple selects are next to each other and display
574
+ // the option list in listbox style, i.e. the select is [multiple], or specifies a [size].
575
+ // See https://github.com/angular/angular.js/issues/11314 for more info.
576
+ // This is unfortunately untestable with unit / e2e tests
577
+ if (option.label !== element.label) {
578
+ element.label = option.label;
579
+ element.textContent = option.label;
580
+ }
581
+ element.value = option.selectValue;
582
+ }
583
+
584
+ function updateOptions() {
585
+ const previousValue = options && selectCtrl.readValue();
586
+
587
+ // We must remove all current options, but cannot simply set innerHTML = null
588
+ // since the providedEmptyOption might have an ngIf on it that inserts comments which we
589
+ // must preserve.
590
+ // Instead, iterate over the current option elements and remove them or their optgroup
591
+ // parents
592
+ if (options) {
593
+ for (let i = options.items.length - 1; i >= 0; i--) {
594
+ const option = options.items[i];
595
+ if (isDefined(option.group)) {
596
+ jqLiteRemove(option.element.parentNode);
597
+ } else {
598
+ jqLiteRemove(option.element);
599
+ }
600
+ }
601
+ }
602
+
603
+ options = ngOptions.getOptions();
604
+
605
+ const groupElementMap = {};
606
+
607
+ options.items.forEach((option) => {
608
+ let groupElement;
609
+
610
+ if (isDefined(option.group)) {
611
+ // This option is to live in a group
612
+ // See if we have already created this group
613
+ groupElement = groupElementMap[option.group];
614
+
615
+ if (!groupElement) {
616
+ groupElement = optGroupTemplate.cloneNode(false);
617
+ listFragment.appendChild(groupElement);
618
+
619
+ // Update the label on the group element
620
+ // "null" is special cased because of Safari
621
+ groupElement.label =
622
+ option.group === null ? "null" : option.group;
623
+
624
+ // Store it for use later
625
+ groupElementMap[option.group] = groupElement;
626
+ }
627
+
628
+ addOptionElement(option, groupElement);
629
+ } else {
630
+ // This option is not in a group
631
+ addOptionElement(option, listFragment);
632
+ }
633
+ });
634
+
635
+ selectElement[0].appendChild(listFragment);
636
+
637
+ ngModelCtrl.$render();
638
+
639
+ // Check to see if the value has changed due to the update to the options
640
+ if (!ngModelCtrl.$isEmpty(previousValue)) {
641
+ const nextValue = selectCtrl.readValue();
642
+ const isNotPrimitive = ngOptions.trackBy || multiple;
643
+ if (
644
+ isNotPrimitive
645
+ ? !equals(previousValue, nextValue)
646
+ : previousValue !== nextValue
647
+ ) {
648
+ ngModelCtrl.$setViewValue(nextValue);
649
+ ngModelCtrl.$render();
650
+ }
651
+ }
652
+ }
653
+ }
654
+
655
+ return {
656
+ restrict: "A",
657
+ terminal: true,
658
+ require: ["select", "ngModel"],
659
+ link: {
660
+ pre: function ngOptionsPreLink(scope, selectElement, attr, ctrls) {
661
+ // Deactivate the SelectController.register method to prevent
662
+ // option directives from accidentally registering themselves
663
+ // (and unwanted $destroy handlers etc.)
664
+ ctrls[0].registerOption = () => {};
665
+ },
666
+ post: ngOptionsPostLink,
667
+ },
668
+ };
669
+ },
670
+ ];