@angular-wave/angular.ts 0.0.11 → 0.0.12

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 (88) hide show
  1. package/dist/angular-ts.esm.js +1 -1
  2. package/dist/angular-ts.umd.js +1 -1
  3. package/package.json +4 -1
  4. package/src/exts/messages.md +30 -30
  5. package/src/public.js +2 -0
  6. package/src/router/adapter/directives/stateDirectives.js +695 -0
  7. package/src/router/adapter/directives/viewDirective.js +514 -0
  8. package/src/router/adapter/injectables.js +314 -0
  9. package/src/router/adapter/interface.js +1 -0
  10. package/src/router/adapter/locationServices.js +86 -0
  11. package/src/router/adapter/services.js +132 -0
  12. package/src/router/adapter/stateFilters.js +43 -0
  13. package/src/router/adapter/stateProvider.js +137 -0
  14. package/src/router/adapter/statebuilders/onEnterExitRetain.js +30 -0
  15. package/src/router/adapter/statebuilders/views.js +146 -0
  16. package/src/router/adapter/templateFactory.js +218 -0
  17. package/src/router/adapter/urlRouterProvider.js +196 -0
  18. package/src/router/adapter/viewScroll.js +31 -0
  19. package/src/router/core/common/common.js +506 -0
  20. package/src/router/core/common/coreservices.js +15 -0
  21. package/src/router/core/common/glob.js +75 -0
  22. package/src/router/core/common/hof.js +194 -0
  23. package/src/router/core/common/predicates.js +44 -0
  24. package/src/router/core/common/queue.js +41 -0
  25. package/src/router/core/common/safeConsole.js +38 -0
  26. package/src/router/core/common/strings.js +141 -0
  27. package/src/router/core/common/trace.js +232 -0
  28. package/src/router/core/globals.js +29 -0
  29. package/src/router/core/hooks/coreResolvables.js +33 -0
  30. package/src/router/core/hooks/ignoredTransition.js +25 -0
  31. package/src/router/core/hooks/invalidTransition.js +14 -0
  32. package/src/router/core/hooks/lazyLoad.js +102 -0
  33. package/src/router/core/hooks/onEnterExitRetain.js +55 -0
  34. package/src/router/core/hooks/redirectTo.js +36 -0
  35. package/src/router/core/hooks/resolve.js +57 -0
  36. package/src/router/core/hooks/updateGlobals.js +30 -0
  37. package/src/router/core/hooks/url.js +25 -0
  38. package/src/router/core/hooks/views.js +39 -0
  39. package/src/router/core/interface.js +3 -0
  40. package/src/router/core/params/README.md +8 -0
  41. package/src/router/core/params/param.js +232 -0
  42. package/src/router/core/params/paramType.js +139 -0
  43. package/src/router/core/params/paramTypes.js +163 -0
  44. package/src/router/core/params/stateParams.js +35 -0
  45. package/src/router/core/path/pathNode.js +77 -0
  46. package/src/router/core/path/pathUtils.js +200 -0
  47. package/src/router/core/resolve/interface.js +10 -0
  48. package/src/router/core/resolve/resolvable.js +124 -0
  49. package/src/router/core/resolve/resolveContext.js +211 -0
  50. package/src/router/core/router.js +201 -0
  51. package/src/router/core/state/README.md +21 -0
  52. package/src/router/core/state/stateBuilder.js +333 -0
  53. package/src/router/core/state/stateMatcher.js +66 -0
  54. package/src/router/core/state/stateObject.js +116 -0
  55. package/src/router/core/state/stateQueueManager.js +89 -0
  56. package/src/router/core/state/stateRegistry.js +175 -0
  57. package/src/router/core/state/stateService.js +592 -0
  58. package/src/router/core/state/targetState.js +159 -0
  59. package/src/router/core/transition/hookBuilder.js +127 -0
  60. package/src/router/core/transition/hookRegistry.js +182 -0
  61. package/src/router/core/transition/interface.js +14 -0
  62. package/src/router/core/transition/rejectFactory.js +122 -0
  63. package/src/router/core/transition/transition.js +739 -0
  64. package/src/router/core/transition/transitionEventType.js +27 -0
  65. package/src/router/core/transition/transitionHook.js +199 -0
  66. package/src/router/core/transition/transitionService.js +311 -0
  67. package/src/router/core/url/interface.js +1 -0
  68. package/src/router/core/url/urlConfig.js +165 -0
  69. package/src/router/core/url/urlMatcher.js +548 -0
  70. package/src/router/core/url/urlMatcherFactory.js +123 -0
  71. package/src/router/core/url/urlRouter.js +115 -0
  72. package/src/router/core/url/urlRule.js +202 -0
  73. package/src/router/core/url/urlRules.js +348 -0
  74. package/src/router/core/url/urlService.js +268 -0
  75. package/src/router/core/vanilla/baseLocationService.js +31 -0
  76. package/src/router/core/vanilla/browserLocationConfig.js +42 -0
  77. package/src/router/core/vanilla/hashLocationService.js +19 -0
  78. package/src/router/core/vanilla/injector.js +98 -0
  79. package/src/router/core/vanilla/interface.js +1 -0
  80. package/src/router/core/vanilla/memoryLocationConfig.js +20 -0
  81. package/src/router/core/vanilla/memoryLocationService.js +13 -0
  82. package/src/router/core/vanilla/plugins.js +35 -0
  83. package/src/router/core/vanilla/pushStateLocationService.js +69 -0
  84. package/src/router/core/vanilla/q.js +54 -0
  85. package/src/router/core/vanilla/utils.js +63 -0
  86. package/src/router/core/view/interface.js +1 -0
  87. package/src/router/core/view/view.js +312 -0
  88. package/src/router/router.js +52 -0
@@ -0,0 +1,695 @@
1
+ /**
2
+ * # Angular 1 Directives
3
+ *
4
+ * These are the directives included in UI-Router for Angular 1.
5
+ * These directives are used in templates to create viewports and link/navigate to states.
6
+ *
7
+ * @preferred @publicapi @module directives
8
+ */ /** */
9
+ import {
10
+ extend,
11
+ forEach,
12
+ tail,
13
+ isString,
14
+ isObject,
15
+ isArray,
16
+ parse,
17
+ noop,
18
+ unnestR,
19
+ identity,
20
+ uniqR,
21
+ inArray,
22
+ removeFrom,
23
+ } from "../../core/index";
24
+ /** @hidden */
25
+ function parseStateRef(ref) {
26
+ const paramsOnly = ref.match(/^\s*({[^}]*})\s*$/);
27
+ if (paramsOnly) ref = "(" + paramsOnly[1] + ")";
28
+ const parsed = ref
29
+ .replace(/\n/g, " ")
30
+ .match(/^\s*([^(]*?)\s*(\((.*)\))?\s*$/);
31
+ if (!parsed || parsed.length !== 4)
32
+ throw new Error("Invalid state ref '" + ref + "'");
33
+ return { state: parsed[1] || null, paramExpr: parsed[3] || null };
34
+ }
35
+ /** @hidden */
36
+ function stateContext(el) {
37
+ const $uiView = el.parent().inheritedData("$uiView");
38
+ const path = parse("$cfg.path")($uiView);
39
+ return path ? tail(path).state.name : undefined;
40
+ }
41
+ /** @hidden */
42
+ function processedDef($state, $element, def) {
43
+ const uiState = def.uiState || $state.current.name;
44
+ const uiStateOpts = extend(
45
+ defaultOpts($element, $state),
46
+ def.uiStateOpts || {},
47
+ );
48
+ const href = $state.href(uiState, def.uiStateParams, uiStateOpts);
49
+ return { uiState, uiStateParams: def.uiStateParams, uiStateOpts, href };
50
+ }
51
+ /** @hidden */
52
+ function getTypeInfo(el) {
53
+ // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
54
+ const isSvg =
55
+ Object.prototype.toString.call(el.prop("href")) ===
56
+ "[object SVGAnimatedString]";
57
+ const isForm = el[0].nodeName === "FORM";
58
+ return {
59
+ attr: isForm ? "action" : isSvg ? "xlink:href" : "href",
60
+ isAnchor: el.prop("tagName").toUpperCase() === "A",
61
+ clickable: !isForm,
62
+ };
63
+ }
64
+ /** @hidden */
65
+ function clickHook(el, $state, $timeout, type, getDef) {
66
+ return function (e) {
67
+ const button = e.which || e.button,
68
+ target = getDef();
69
+ if (
70
+ !(
71
+ button > 1 ||
72
+ e.ctrlKey ||
73
+ e.metaKey ||
74
+ e.shiftKey ||
75
+ e.altKey ||
76
+ el.attr("target")
77
+ )
78
+ ) {
79
+ // HACK: This is to allow ng-clicks to be processed before the transition is initiated:
80
+ const transition = $timeout(function () {
81
+ if (!el.attr("disabled")) {
82
+ $state.go(target.uiState, target.uiStateParams, target.uiStateOpts);
83
+ }
84
+ });
85
+ e.preventDefault();
86
+ // if the state has no URL, ignore one preventDefault from the <a> directive.
87
+ let ignorePreventDefaultCount = type.isAnchor && !target.href ? 1 : 0;
88
+ e.preventDefault = function () {
89
+ if (ignorePreventDefaultCount-- <= 0) $timeout.cancel(transition);
90
+ };
91
+ }
92
+ };
93
+ }
94
+ /** @hidden */
95
+ function defaultOpts(el, $state) {
96
+ return {
97
+ relative: stateContext(el) || $state.$current,
98
+ inherit: true,
99
+ source: "sref",
100
+ };
101
+ }
102
+ /** @hidden */
103
+ function bindEvents(element, scope, hookFn, uiStateOpts) {
104
+ let events;
105
+ if (uiStateOpts) {
106
+ events = uiStateOpts.events;
107
+ }
108
+ if (!isArray(events)) {
109
+ events = ["click"];
110
+ }
111
+ const on = element.on ? "on" : "bind";
112
+ for (const event of events) {
113
+ element[on](event, hookFn);
114
+ }
115
+ scope.$on("$destroy", function () {
116
+ const off = element.off ? "off" : "unbind";
117
+ for (const event of events) {
118
+ element[off](event, hookFn);
119
+ }
120
+ });
121
+ }
122
+ /**
123
+ * `ui-sref`: A directive for linking to a state
124
+ *
125
+ * A directive which links to a state (and optionally, parameters).
126
+ * When clicked, this directive activates the linked state with the supplied parameter values.
127
+ *
128
+ * ### Linked State
129
+ * The attribute value of the `ui-sref` is the name of the state to link to.
130
+ *
131
+ * #### Example:
132
+ * This will activate the `home` state when the link is clicked.
133
+ * ```html
134
+ * <a ui-sref="home">Home</a>
135
+ * ```
136
+ *
137
+ * ### Relative Links
138
+ * You can also use relative state paths within `ui-sref`, just like a relative path passed to `$state.go()` ([[StateService.go]]).
139
+ * You just need to be aware that the path is relative to the state that *created* the link.
140
+ * This allows a state to create a relative `ui-sref` which always targets the same destination.
141
+ *
142
+ * #### Example:
143
+ * Both these links are relative to the parent state, even when a child state is currently active.
144
+ * ```html
145
+ * <a ui-sref=".child1">child 1 state</a>
146
+ * <a ui-sref=".child2">child 2 state</a>
147
+ * ```
148
+ *
149
+ * This link activates the parent state.
150
+ * ```html
151
+ * <a ui-sref="^">Return</a>
152
+ * ```
153
+ *
154
+ * ### hrefs
155
+ * If the linked state has a URL, the directive will automatically generate and
156
+ * update the `href` attribute (using the [[StateService.href]] method).
157
+ *
158
+ * #### Example:
159
+ * Assuming the `users` state has a url of `/users/`
160
+ * ```html
161
+ * <a ui-sref="users" href="/users/">Users</a>
162
+ * ```
163
+ *
164
+ * ### Parameter Values
165
+ * In addition to the state name, a `ui-sref` can include parameter values which are applied when activating the state.
166
+ * Param values can be provided in the `ui-sref` value after the state name, enclosed by parentheses.
167
+ * The content inside the parentheses is an expression, evaluated to the parameter values.
168
+ *
169
+ * #### Example:
170
+ * This example renders a list of links to users.
171
+ * The state's `userId` parameter value comes from each user's `user.id` property.
172
+ * ```html
173
+ * <li ng-repeat="user in users">
174
+ * <a ui-sref="users.detail({ userId: user.id })">{{ user.displayName }}</a>
175
+ * </li>
176
+ * ```
177
+ *
178
+ * Note:
179
+ * The parameter values expression is `$watch`ed for updates.
180
+ *
181
+ * ### Transition Options
182
+ * You can specify [[TransitionOptions]] to pass to [[StateService.go]] by using the `ui-sref-opts` attribute.
183
+ * Options are restricted to `location`, `inherit`, and `reload`.
184
+ *
185
+ * #### Example:
186
+ * ```html
187
+ * <a ui-sref="home" ui-sref-opts="{ reload: true }">Home</a>
188
+ * ```
189
+ *
190
+ * ### Other DOM Events
191
+ *
192
+ * You can also customize which DOM events to respond to (instead of `click`) by
193
+ * providing an `events` array in the `ui-sref-opts` attribute.
194
+ *
195
+ * #### Example:
196
+ * ```html
197
+ * <input type="text" ui-sref="contacts" ui-sref-opts="{ events: ['change', 'blur'] }">
198
+ * ```
199
+ *
200
+ * ### Highlighting the active link
201
+ * This directive can be used in conjunction with [[uiSrefActive]] to highlight the active link.
202
+ *
203
+ * ### Examples
204
+ * If you have the following template:
205
+ *
206
+ * ```html
207
+ * <a ui-sref="home">Home</a>
208
+ * <a ui-sref="about">About</a>
209
+ * <a ui-sref="{page: 2}">Next page</a>
210
+ *
211
+ * <ul>
212
+ * <li ng-repeat="contact in contacts">
213
+ * <a ui-sref="contacts.detail({ id: contact.id })">{{ contact.name }}</a>
214
+ * </li>
215
+ * </ul>
216
+ * ```
217
+ *
218
+ * Then (assuming the current state is `contacts`) the rendered html including hrefs would be:
219
+ *
220
+ * ```html
221
+ * <a href="#/home" ui-sref="home">Home</a>
222
+ * <a href="#/about" ui-sref="about">About</a>
223
+ * <a href="#/contacts?page=2" ui-sref="{page: 2}">Next page</a>
224
+ *
225
+ * <ul>
226
+ * <li ng-repeat="contact in contacts">
227
+ * <a href="#/contacts/1" ui-sref="contacts.detail({ id: contact.id })">Joe</a>
228
+ * </li>
229
+ * <li ng-repeat="contact in contacts">
230
+ * <a href="#/contacts/2" ui-sref="contacts.detail({ id: contact.id })">Alice</a>
231
+ * </li>
232
+ * <li ng-repeat="contact in contacts">
233
+ * <a href="#/contacts/3" ui-sref="contacts.detail({ id: contact.id })">Bob</a>
234
+ * </li>
235
+ * </ul>
236
+ *
237
+ * <a href="#/home" ui-sref="home" ui-sref-opts="{reload: true}">Home</a>
238
+ * ```
239
+ *
240
+ * ### Notes
241
+ *
242
+ * - You can use `ui-sref` to change **only the parameter values** by omitting the state name and parentheses.
243
+ * #### Example:
244
+ * Sets the `lang` parameter to `en` and remains on the same state.
245
+ *
246
+ * ```html
247
+ * <a ui-sref="{ lang: 'en' }">English</a>
248
+ * ```
249
+ *
250
+ * - A middle-click, right-click, or ctrl-click is handled (natively) by the browser to open the href in a new window, for example.
251
+ *
252
+ * - Unlike the parameter values expression, the state name is not `$watch`ed (for performance reasons).
253
+ * If you need to dynamically update the state being linked to, use the fully dynamic [[uiState]] directive.
254
+ */
255
+ let uiSrefDirective;
256
+ uiSrefDirective = [
257
+ "$uiRouter",
258
+ "$timeout",
259
+ function $StateRefDirective($uiRouter, $timeout) {
260
+ const $state = $uiRouter.stateService;
261
+ return {
262
+ restrict: "A",
263
+ require: ["?^uiSrefActive", "?^uiSrefActiveEq"],
264
+ link: function (scope, element, attrs, uiSrefActive) {
265
+ const type = getTypeInfo(element);
266
+ const active = uiSrefActive[1] || uiSrefActive[0];
267
+ let unlinkInfoFn = null;
268
+ const rawDef = {};
269
+ const getDef = () => processedDef($state, element, rawDef);
270
+ const ref = parseStateRef(attrs.uiSref);
271
+ rawDef.uiState = ref.state;
272
+ rawDef.uiStateOpts = attrs.uiSrefOpts
273
+ ? scope.$eval(attrs.uiSrefOpts)
274
+ : {};
275
+ function update() {
276
+ const def = getDef();
277
+ if (unlinkInfoFn) unlinkInfoFn();
278
+ if (active)
279
+ unlinkInfoFn = active.$$addStateInfo(
280
+ def.uiState,
281
+ def.uiStateParams,
282
+ );
283
+ if (def.href != null) attrs.$set(type.attr, def.href);
284
+ }
285
+ if (ref.paramExpr) {
286
+ scope.$watch(
287
+ ref.paramExpr,
288
+ function (val) {
289
+ rawDef.uiStateParams = extend({}, val);
290
+ update();
291
+ },
292
+ true,
293
+ );
294
+ rawDef.uiStateParams = extend({}, scope.$eval(ref.paramExpr));
295
+ }
296
+ update();
297
+ scope.$on("$destroy", $uiRouter.stateRegistry.onStatesChanged(update));
298
+ scope.$on(
299
+ "$destroy",
300
+ $uiRouter.transitionService.onSuccess({}, update),
301
+ );
302
+ if (!type.clickable) return;
303
+ const hookFn = clickHook(element, $state, $timeout, type, getDef);
304
+ bindEvents(element, scope, hookFn, rawDef.uiStateOpts);
305
+ },
306
+ };
307
+ },
308
+ ];
309
+ /**
310
+ * `ui-state`: A fully dynamic directive for linking to a state
311
+ *
312
+ * A directive which links to a state (and optionally, parameters).
313
+ * When clicked, this directive activates the linked state with the supplied parameter values.
314
+ *
315
+ * **This directive is very similar to [[uiSref]], but it `$observe`s and `$watch`es/evaluates all its inputs.**
316
+ *
317
+ * A directive which links to a state (and optionally, parameters).
318
+ * When clicked, this directive activates the linked state with the supplied parameter values.
319
+ *
320
+ * ### Linked State
321
+ * The attribute value of `ui-state` is an expression which is `$watch`ed and evaluated as the state to link to.
322
+ * **This is in contrast with `ui-sref`, which takes a state name as a string literal.**
323
+ *
324
+ * #### Example:
325
+ * Create a list of links.
326
+ * ```html
327
+ * <li ng-repeat="link in navlinks">
328
+ * <a ui-state="link.state">{{ link.displayName }}</a>
329
+ * </li>
330
+ * ```
331
+ *
332
+ * ### Relative Links
333
+ * If the expression evaluates to a relative path, it is processed like [[uiSref]].
334
+ * You just need to be aware that the path is relative to the state that *created* the link.
335
+ * This allows a state to create relative `ui-state` which always targets the same destination.
336
+ *
337
+ * ### hrefs
338
+ * If the linked state has a URL, the directive will automatically generate and
339
+ * update the `href` attribute (using the [[StateService.href]] method).
340
+ *
341
+ * ### Parameter Values
342
+ * In addition to the state name expression, a `ui-state` can include parameter values which are applied when activating the state.
343
+ * Param values should be provided using the `ui-state-params` attribute.
344
+ * The `ui-state-params` attribute value is `$watch`ed and evaluated as an expression.
345
+ *
346
+ * #### Example:
347
+ * This example renders a list of links with param values.
348
+ * The state's `userId` parameter value comes from each user's `user.id` property.
349
+ * ```html
350
+ * <li ng-repeat="link in navlinks">
351
+ * <a ui-state="link.state" ui-state-params="link.params">{{ link.displayName }}</a>
352
+ * </li>
353
+ * ```
354
+ *
355
+ * ### Transition Options
356
+ * You can specify [[TransitionOptions]] to pass to [[StateService.go]] by using the `ui-state-opts` attribute.
357
+ * Options are restricted to `location`, `inherit`, and `reload`.
358
+ * The value of the `ui-state-opts` is `$watch`ed and evaluated as an expression.
359
+ *
360
+ * #### Example:
361
+ * ```html
362
+ * <a ui-state="returnto.state" ui-state-opts="{ reload: true }">Home</a>
363
+ * ```
364
+ *
365
+ * ### Other DOM Events
366
+ *
367
+ * You can also customize which DOM events to respond to (instead of `click`) by
368
+ * providing an `events` array in the `ui-state-opts` attribute.
369
+ *
370
+ * #### Example:
371
+ * ```html
372
+ * <input type="text" ui-state="contacts" ui-state-opts="{ events: ['change', 'blur'] }">
373
+ * ```
374
+ *
375
+ * ### Highlighting the active link
376
+ * This directive can be used in conjunction with [[uiSrefActive]] to highlight the active link.
377
+ *
378
+ * ### Notes
379
+ *
380
+ * - You can use `ui-params` to change **only the parameter values** by omitting the state name and supplying only `ui-state-params`.
381
+ * However, it might be simpler to use [[uiSref]] parameter-only links.
382
+ *
383
+ * #### Example:
384
+ * Sets the `lang` parameter to `en` and remains on the same state.
385
+ *
386
+ * ```html
387
+ * <a ui-state="" ui-state-params="{ lang: 'en' }">English</a>
388
+ * ```
389
+ *
390
+ * - A middle-click, right-click, or ctrl-click is handled (natively) by the browser to open the href in a new window, for example.
391
+ * ```
392
+ */
393
+ let uiStateDirective;
394
+ uiStateDirective = [
395
+ "$uiRouter",
396
+ "$timeout",
397
+ function $StateRefDynamicDirective($uiRouter, $timeout) {
398
+ const $state = $uiRouter.stateService;
399
+ return {
400
+ restrict: "A",
401
+ require: ["?^uiSrefActive", "?^uiSrefActiveEq"],
402
+ link: function (scope, element, attrs, uiSrefActive) {
403
+ const type = getTypeInfo(element);
404
+ const active = uiSrefActive[1] || uiSrefActive[0];
405
+ let unlinkInfoFn = null;
406
+ let hookFn;
407
+ const rawDef = {};
408
+ const getDef = () => processedDef($state, element, rawDef);
409
+ const inputAttrs = ["uiState", "uiStateParams", "uiStateOpts"];
410
+ const watchDeregFns = inputAttrs.reduce(
411
+ (acc, attr) => ((acc[attr] = noop), acc),
412
+ {},
413
+ );
414
+ function update() {
415
+ const def = getDef();
416
+ if (unlinkInfoFn) unlinkInfoFn();
417
+ if (active)
418
+ unlinkInfoFn = active.$$addStateInfo(
419
+ def.uiState,
420
+ def.uiStateParams,
421
+ );
422
+ if (def.href != null) attrs.$set(type.attr, def.href);
423
+ }
424
+ inputAttrs.forEach((field) => {
425
+ rawDef[field] = attrs[field] ? scope.$eval(attrs[field]) : null;
426
+ attrs.$observe(field, (expr) => {
427
+ watchDeregFns[field]();
428
+ watchDeregFns[field] = scope.$watch(
429
+ expr,
430
+ (newval) => {
431
+ rawDef[field] = newval;
432
+ update();
433
+ },
434
+ true,
435
+ );
436
+ });
437
+ });
438
+ update();
439
+ scope.$on("$destroy", $uiRouter.stateRegistry.onStatesChanged(update));
440
+ scope.$on(
441
+ "$destroy",
442
+ $uiRouter.transitionService.onSuccess({}, update),
443
+ );
444
+ if (!type.clickable) return;
445
+ hookFn = clickHook(element, $state, $timeout, type, getDef);
446
+ bindEvents(element, scope, hookFn, rawDef.uiStateOpts);
447
+ },
448
+ };
449
+ },
450
+ ];
451
+ /**
452
+ * `ui-sref-active` and `ui-sref-active-eq`: A directive that adds a CSS class when a `ui-sref` is active
453
+ *
454
+ * A directive working alongside [[uiSref]] and [[uiState]] to add classes to an element when the
455
+ * related directive's state is active (and remove them when it is inactive).
456
+ *
457
+ * The primary use-case is to highlight the active link in navigation menus,
458
+ * distinguishing it from the inactive menu items.
459
+ *
460
+ * ### Linking to a `ui-sref` or `ui-state`
461
+ * `ui-sref-active` can live on the same element as `ui-sref`/`ui-state`, or it can be on a parent element.
462
+ * If a `ui-sref-active` is a parent to more than one `ui-sref`/`ui-state`, it will apply the CSS class when **any of the links are active**.
463
+ *
464
+ * ### Matching
465
+ *
466
+ * The `ui-sref-active` directive applies the CSS class when the `ui-sref`/`ui-state`'s target state **or any child state is active**.
467
+ * This is a "fuzzy match" which uses [[StateService.includes]].
468
+ *
469
+ * The `ui-sref-active-eq` directive applies the CSS class when the `ui-sref`/`ui-state`'s target state is directly active (not when child states are active).
470
+ * This is an "exact match" which uses [[StateService.is]].
471
+ *
472
+ * ### Parameter values
473
+ * If the `ui-sref`/`ui-state` includes parameter values, the current parameter values must match the link's values for the link to be highlighted.
474
+ * This allows a list of links to the same state with different parameters to be rendered, and the correct one highlighted.
475
+ *
476
+ * #### Example:
477
+ * ```html
478
+ * <li ng-repeat="user in users" ui-sref-active="active">
479
+ * <a ui-sref="user.details({ userId: user.id })">{{ user.lastName }}</a>
480
+ * </li>
481
+ * ```
482
+ *
483
+ * ### Examples
484
+ *
485
+ * Given the following template:
486
+ * #### Example:
487
+ * ```html
488
+ * <ul>
489
+ * <li ui-sref-active="active" class="item">
490
+ * <a href ui-sref="app.user({user: 'bilbobaggins'})">@bilbobaggins</a>
491
+ * </li>
492
+ * </ul>
493
+ * ```
494
+ *
495
+ * When the app state is `app.user` (or any child state),
496
+ * and contains the state parameter "user" with value "bilbobaggins",
497
+ * the resulting HTML will appear as (note the 'active' class):
498
+ *
499
+ * ```html
500
+ * <ul>
501
+ * <li ui-sref-active="active" class="item active">
502
+ * <a ui-sref="app.user({user: 'bilbobaggins'})" href="/users/bilbobaggins">@bilbobaggins</a>
503
+ * </li>
504
+ * </ul>
505
+ * ```
506
+ *
507
+ * ### Glob mode
508
+ *
509
+ * It is possible to pass `ui-sref-active` an expression that evaluates to an object.
510
+ * The objects keys represent active class names and values represent the respective state names/globs.
511
+ * `ui-sref-active` will match if the current active state **includes** any of
512
+ * the specified state names/globs, even the abstract ones.
513
+ *
514
+ * #### Example:
515
+ * Given the following template, with "admin" being an abstract state:
516
+ * ```html
517
+ * <div ui-sref-active="{'active': 'admin.**'}">
518
+ * <a ui-sref-active="active" ui-sref="admin.roles">Roles</a>
519
+ * </div>
520
+ * ```
521
+ *
522
+ * Arrays are also supported as values in the `ngClass`-like interface.
523
+ * This allows multiple states to add `active` class.
524
+ *
525
+ * #### Example:
526
+ * Given the following template, with "admin.roles" being the current state, the class will be added too:
527
+ * ```html
528
+ * <div ui-sref-active="{'active': ['owner.**', 'admin.**']}">
529
+ * <a ui-sref-active="active" ui-sref="admin.roles">Roles</a>
530
+ * </div>
531
+ * ```
532
+ *
533
+ * When the current state is "admin.roles" the "active" class will be applied to both the `<div>` and `<a>` elements.
534
+ * It is important to note that the state names/globs passed to `ui-sref-active` override any state provided by a linked `ui-sref`.
535
+ *
536
+ * ### Notes:
537
+ *
538
+ * - The class name is interpolated **once** during the directives link time (any further changes to the
539
+ * interpolated value are ignored).
540
+ *
541
+ * - Multiple classes may be specified in a space-separated format: `ui-sref-active='class1 class2 class3'`
542
+ */
543
+ let uiSrefActiveDirective;
544
+ uiSrefActiveDirective = [
545
+ "$state",
546
+ "$stateParams",
547
+ "$interpolate",
548
+ "$uiRouter",
549
+ function $StateRefActiveDirective(
550
+ $state,
551
+ $stateParams,
552
+ $interpolate,
553
+ $uiRouter,
554
+ ) {
555
+ return {
556
+ restrict: "A",
557
+ controller: [
558
+ "$scope",
559
+ "$element",
560
+ "$attrs",
561
+ function ($scope, $element, $attrs) {
562
+ let states = [];
563
+ let activeEqClass;
564
+ let uiSrefActive;
565
+ // There probably isn't much point in $observing this
566
+ // uiSrefActive and uiSrefActiveEq share the same directive object with some
567
+ // slight difference in logic routing
568
+ activeEqClass = $interpolate(
569
+ $attrs.uiSrefActiveEq || "",
570
+ false,
571
+ )($scope);
572
+ try {
573
+ uiSrefActive = $scope.$eval($attrs.uiSrefActive);
574
+ } catch (e) {
575
+ // Do nothing. uiSrefActive is not a valid expression.
576
+ // Fall back to using $interpolate below
577
+ }
578
+ uiSrefActive =
579
+ uiSrefActive ||
580
+ $interpolate($attrs.uiSrefActive || "", false)($scope);
581
+ setStatesFromDefinitionObject(uiSrefActive);
582
+ // Allow uiSref to communicate with uiSrefActive[Equals]
583
+ this.$$addStateInfo = function (newState, newParams) {
584
+ // we already got an explicit state provided by ui-sref-active, so we
585
+ // shadow the one that comes from ui-sref
586
+ if (isObject(uiSrefActive) && states.length > 0) {
587
+ return;
588
+ }
589
+ const deregister = addState(newState, newParams, uiSrefActive);
590
+ update();
591
+ return deregister;
592
+ };
593
+ function updateAfterTransition(trans) {
594
+ trans.promise.then(update, noop);
595
+ }
596
+ $scope.$on("$destroy", setupEventListeners());
597
+ if ($uiRouter.globals.transition) {
598
+ updateAfterTransition($uiRouter.globals.transition);
599
+ }
600
+ function setupEventListeners() {
601
+ const deregisterStatesChangedListener =
602
+ $uiRouter.stateRegistry.onStatesChanged(handleStatesChanged);
603
+ const deregisterOnStartListener =
604
+ $uiRouter.transitionService.onStart({}, updateAfterTransition);
605
+ const deregisterStateChangeSuccessListener = $scope.$on(
606
+ "$stateChangeSuccess",
607
+ update,
608
+ );
609
+ return function cleanUp() {
610
+ deregisterStatesChangedListener();
611
+ deregisterOnStartListener();
612
+ deregisterStateChangeSuccessListener();
613
+ };
614
+ }
615
+ function handleStatesChanged() {
616
+ setStatesFromDefinitionObject(uiSrefActive);
617
+ }
618
+ function setStatesFromDefinitionObject(statesDefinition) {
619
+ if (isObject(statesDefinition)) {
620
+ states = [];
621
+ forEach(statesDefinition, function (stateOrName, activeClass) {
622
+ // Helper function to abstract adding state.
623
+ const addStateForClass = function (stateOrName, activeClass) {
624
+ const ref = parseStateRef(stateOrName);
625
+ addState(ref.state, $scope.$eval(ref.paramExpr), activeClass);
626
+ };
627
+ if (isString(stateOrName)) {
628
+ // If state is string, just add it.
629
+ addStateForClass(stateOrName, activeClass);
630
+ } else if (isArray(stateOrName)) {
631
+ // If state is an array, iterate over it and add each array item individually.
632
+ forEach(stateOrName, function (stateOrName) {
633
+ addStateForClass(stateOrName, activeClass);
634
+ });
635
+ }
636
+ });
637
+ }
638
+ }
639
+ function addState(stateName, stateParams, activeClass) {
640
+ const state = $state.get(stateName, stateContext($element));
641
+ const stateInfo = {
642
+ state: state || { name: stateName },
643
+ params: stateParams,
644
+ activeClass: activeClass,
645
+ };
646
+ states.push(stateInfo);
647
+ return function removeState() {
648
+ removeFrom(states)(stateInfo);
649
+ };
650
+ }
651
+ // Update route state
652
+ function update() {
653
+ const splitClasses = (str) => str.split(/\s/).filter(identity);
654
+ const getClasses = (stateList) =>
655
+ stateList
656
+ .map((x) => x.activeClass)
657
+ .map(splitClasses)
658
+ .reduce(unnestR, []);
659
+ const allClasses = getClasses(states)
660
+ .concat(splitClasses(activeEqClass))
661
+ .reduce(uniqR, []);
662
+ const fuzzyClasses = getClasses(
663
+ states.filter((x) => $state.includes(x.state.name, x.params)),
664
+ );
665
+ const exactlyMatchesAny = !!states.filter((x) =>
666
+ $state.is(x.state.name, x.params),
667
+ ).length;
668
+ const exactClasses = exactlyMatchesAny
669
+ ? splitClasses(activeEqClass)
670
+ : [];
671
+ const addClasses = fuzzyClasses
672
+ .concat(exactClasses)
673
+ .reduce(uniqR, []);
674
+ const removeClasses = allClasses.filter(
675
+ (cls) => !inArray(addClasses, cls),
676
+ );
677
+ $scope.$evalAsync(() => {
678
+ addClasses.forEach((className) => $element.addClass(className));
679
+ removeClasses.forEach((className) =>
680
+ $element.removeClass(className),
681
+ );
682
+ });
683
+ }
684
+ update();
685
+ },
686
+ ],
687
+ };
688
+ },
689
+ ];
690
+ window.angular
691
+ .module("ui.router.state")
692
+ .directive("uiSref", uiSrefDirective)
693
+ .directive("uiSrefActive", uiSrefActiveDirective)
694
+ .directive("uiSrefActiveEq", uiSrefActiveDirective)
695
+ .directive("uiState", uiStateDirective);