ember-source 5.11.0 → 5.11.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 (147) hide show
  1. package/build-metadata.json +3 -3
  2. package/dist/ember-template-compiler.js +48 -42
  3. package/dist/ember-testing.js +1 -1
  4. package/dist/ember.debug.js +23102 -23100
  5. package/dist/ember.prod.js +31562 -31563
  6. package/dist/packages/@ember/-internals/container/index.js +1 -1
  7. package/dist/packages/@ember/-internals/deprecations/index.js +2 -1
  8. package/dist/packages/@ember/-internals/glimmer/index.js +2 -1
  9. package/dist/packages/@ember/-internals/meta/lib/meta.js +4 -3
  10. package/dist/packages/@ember/-internals/metal/index.js +9 -8
  11. package/dist/packages/@ember/-internals/routing/index.js +6 -5
  12. package/dist/packages/@ember/-internals/runtime/lib/ext/rsvp.js +3 -2
  13. package/dist/packages/@ember/-internals/runtime/lib/mixins/-proxy.js +6 -5
  14. package/dist/packages/@ember/-internals/runtime/lib/mixins/action_handler.js +4 -3
  15. package/dist/packages/@ember/-internals/runtime/lib/mixins/container_proxy.js +1 -1
  16. package/dist/packages/@ember/-internals/runtime/lib/mixins/registry_proxy.js +2 -1
  17. package/dist/packages/@ember/-internals/runtime/lib/mixins/target_action_support.js +4 -3
  18. package/dist/packages/@ember/-internals/string/index.js +1 -1
  19. package/dist/packages/@ember/-internals/utils/index.js +4 -4
  20. package/dist/packages/@ember/-internals/views/index.js +1 -1
  21. package/dist/packages/@ember/-internals/views/lib/compat/fallback-view-registry.js +1 -1
  22. package/dist/packages/@ember/-internals/views/lib/component_lookup.js +1 -1
  23. package/dist/packages/@ember/-internals/views/lib/mixins/action_support.js +5 -3
  24. package/dist/packages/@ember/-internals/views/lib/mixins/child_views_support.js +3 -3
  25. package/dist/packages/@ember/-internals/views/lib/mixins/class_names_support.js +4 -3
  26. package/dist/packages/@ember/-internals/views/lib/mixins/view_support.js +4 -3
  27. package/dist/packages/@ember/-internals/views/lib/system/event_dispatcher.js +6 -12
  28. package/dist/packages/@ember/-internals/views/lib/system/utils.js +3 -2
  29. package/dist/packages/@ember/-internals/views/lib/views/core_view.js +76 -8
  30. package/dist/packages/@ember/-internals/views/lib/views/states.js +4 -3
  31. package/dist/packages/@ember/application/index.js +16 -7
  32. package/dist/packages/@ember/application/instance.js +13 -9
  33. package/dist/packages/@ember/application/namespace.js +7 -6
  34. package/dist/packages/@ember/array/index.js +617 -11
  35. package/dist/packages/@ember/array/make.js +1 -0
  36. package/dist/packages/@ember/array/mutable.js +1 -1
  37. package/dist/packages/@ember/array/proxy.js +8 -5
  38. package/dist/packages/@ember/component/helper.js +4 -4
  39. package/dist/packages/@ember/component/index.js +4 -4
  40. package/dist/packages/@ember/controller/index.js +6 -6
  41. package/dist/packages/@ember/debug/container-debug-adapter.js +5 -4
  42. package/dist/packages/@ember/debug/data-adapter.js +7 -4
  43. package/dist/packages/@ember/debug/index.js +213 -4
  44. package/dist/packages/@ember/debug/lib/assert.js +47 -0
  45. package/dist/packages/@ember/debug/lib/deprecate.js +194 -4
  46. package/dist/packages/@ember/debug/lib/inspect.js +120 -2
  47. package/dist/packages/@ember/debug/lib/warn.js +94 -3
  48. package/dist/packages/@ember/engine/index.js +440 -17
  49. package/dist/packages/@ember/engine/instance.js +175 -11
  50. package/dist/packages/@ember/engine/parent.js +1 -0
  51. package/dist/packages/@ember/helper/index.js +4 -4
  52. package/dist/packages/@ember/instrumentation/index.js +2 -1
  53. package/dist/packages/@ember/modifier/index.js +13 -5
  54. package/dist/packages/@ember/modifier/on.js +15 -0
  55. package/dist/packages/@ember/object/-internals.js +6 -5
  56. package/dist/packages/@ember/object/compat.js +4 -3
  57. package/dist/packages/@ember/object/computed.js +4 -4
  58. package/dist/packages/@ember/object/core.js +861 -14
  59. package/dist/packages/@ember/object/evented.js +4 -4
  60. package/dist/packages/@ember/object/events.js +3 -3
  61. package/dist/packages/@ember/object/index.js +260 -9
  62. package/dist/packages/@ember/object/internals.js +1 -1
  63. package/dist/packages/@ember/object/lib/computed/computed_macros.js +8 -6
  64. package/dist/packages/@ember/object/lib/computed/reduce_computed_macros.js +8 -4
  65. package/dist/packages/@ember/object/mixin.js +6 -5
  66. package/dist/packages/@ember/object/observable.js +103 -9
  67. package/dist/packages/@ember/object/observers.js +3 -3
  68. package/dist/packages/@ember/object/promise-proxy-mixin.js +5 -5
  69. package/dist/packages/@ember/renderer/index.js +4 -4
  70. package/dist/packages/@ember/routing/-internals.js +3 -1
  71. package/dist/packages/@ember/routing/hash-location.js +2 -2
  72. package/dist/packages/@ember/routing/history-location.js +3 -2
  73. package/dist/packages/@ember/routing/index.js +4 -4
  74. package/dist/packages/@ember/routing/lib/dsl.js +2 -1
  75. package/dist/packages/@ember/routing/lib/generate_controller.js +4 -3
  76. package/dist/packages/@ember/routing/lib/router_state.js +26 -1
  77. package/dist/packages/@ember/routing/lib/routing-service.js +107 -9
  78. package/dist/packages/@ember/routing/lib/utils.js +238 -7
  79. package/dist/packages/@ember/routing/none-location.js +3 -2
  80. package/dist/packages/@ember/routing/route.js +1618 -22
  81. package/dist/packages/@ember/routing/router-service.js +638 -12
  82. package/dist/packages/@ember/routing/router.js +1449 -14
  83. package/dist/packages/@ember/runloop/index.js +760 -6
  84. package/dist/packages/@ember/service/index.js +3 -3
  85. package/dist/packages/@ember/template/index.js +4 -4
  86. package/dist/packages/@ember/utils/index.js +2 -1
  87. package/dist/packages/@ember/utils/lib/compare.js +159 -4
  88. package/dist/packages/@ember/utils/lib/is_empty.js +4 -4
  89. package/dist/packages/@ember/utils/lib/type-of.js +110 -1
  90. package/dist/packages/@glimmer/tracking/index.js +3 -3
  91. package/dist/packages/@glimmer/tracking/primitives/cache.js +3 -3
  92. package/dist/packages/ember/barrel.js +28 -13
  93. package/dist/packages/ember/version.js +1 -1
  94. package/dist/packages/ember-testing/lib/adapters/adapter.js +1 -1
  95. package/dist/packages/ember-testing/lib/adapters/qunit.js +2 -1
  96. package/dist/packages/ember-testing/lib/ext/application.js +2 -1
  97. package/dist/packages/ember-testing/lib/ext/rsvp.js +1 -1
  98. package/dist/packages/ember-testing/lib/helpers/and_then.js +2 -1
  99. package/dist/packages/ember-testing/lib/helpers/current_path.js +8 -6
  100. package/dist/packages/ember-testing/lib/helpers/current_route_name.js +8 -6
  101. package/dist/packages/ember-testing/lib/helpers/current_url.js +6 -5
  102. package/dist/packages/ember-testing/lib/helpers/pause_test.js +2 -1
  103. package/dist/packages/ember-testing/lib/helpers/visit.js +4 -3
  104. package/dist/packages/ember-testing/lib/helpers/wait.js +4 -3
  105. package/dist/packages/ember-testing/lib/initializers.js +15 -8
  106. package/dist/packages/ember-testing/lib/setup_for_testing.js +1 -1
  107. package/dist/packages/ember-testing/lib/test/run.js +1 -1
  108. package/dist/packages/router_js/index.js +2 -1
  109. package/dist/packages/shared-chunks/{alias-By_2yu5c.js → alias-Dri0koi2.js} +5 -3
  110. package/dist/packages/shared-chunks/array-3xbmc_4J.js +119 -0
  111. package/dist/packages/shared-chunks/{cache-gDE3bkXq.js → cache-BESCGvbE.js} +667 -1529
  112. package/dist/packages/shared-chunks/{core_view-Cxne2_wu.js → chunk-3SQBS3Y5-Cj4eryg1.js} +1 -88
  113. package/dist/packages/shared-chunks/{index-BXPoca1S.js → index-Llq6dmgX.js} +40 -4660
  114. package/dist/packages/shared-chunks/{is_proxy-Dmis-70B.js → is_proxy-DjvCKvd5.js} +1 -1
  115. package/dist/packages/shared-chunks/{mandatory-setter-1UQhiJOb.js → mandatory-setter-BiXq-dpN.js} +2 -1
  116. package/dist/packages/shared-chunks/{name-z9D9Yibn.js → name-Dx2bGFVv.js} +1 -1
  117. package/dist/packages/shared-chunks/{namespace_search-CBgHTkDh.js → namespace_search-btMaPM-_.js} +2 -2
  118. package/dist/packages/shared-chunks/{property_set-CW4q-uo4.js → property_set-BapAkp3X.js} +5 -4
  119. package/dist/packages/shared-chunks/{registry-DzfcDwii.js → registry-B8WARvkP.js} +3 -2
  120. package/dist/packages/shared-chunks/{router-B-Q1aYBn.js → router-DrLZsJeE.js} +2 -482
  121. package/dist/packages/shared-chunks/{set_properties-DvalyQdu.js → set_properties-BScfxzvI.js} +2 -2
  122. package/dist/packages/shared-chunks/setup-registry-du4pSGZi.js +48 -0
  123. package/dist/packages/shared-chunks/{to-string-D8i3mjEU.js → to-string-B1BmwUkt.js} +1 -1
  124. package/dist/packages/shared-chunks/unrecognized-url-error-zpz-JEoG.js +484 -0
  125. package/docs/data.json +152 -142
  126. package/package.json +2 -2
  127. package/types/stable/@ember/-internals/metal/lib/array.d.ts +1 -2
  128. package/types/stable/@ember/-internals/metal/lib/object-at.d.ts +4 -0
  129. package/types/stable/@ember/-internals/metal/lib/observer.d.ts +2 -1
  130. package/types/stable/@ember/array/index.d.ts +1 -1
  131. package/types/stable/@ember/array/make.d.ts +3 -0
  132. package/types/stable/@ember/debug/index.d.ts +3 -7
  133. package/types/stable/@ember/debug/lib/assert.d.ts +8 -0
  134. package/types/stable/@ember/engine/index.d.ts +1 -1
  135. package/types/stable/@ember/engine/instance.d.ts +2 -2
  136. package/types/stable/@ember/engine/parent.d.ts +3 -0
  137. package/types/stable/@ember/modifier/index.d.ts +1 -3
  138. package/types/stable/@ember/modifier/on.d.ts +5 -0
  139. package/types/stable/@ember/routing/lib/routing-service.d.ts +1 -1
  140. package/types/stable/@ember/routing/route.d.ts +2 -3
  141. package/types/stable/@ember/routing/router-service.d.ts +1 -1
  142. package/types/stable/@ember/routing/router.d.ts +4 -4
  143. package/types/stable/ember/barrel.d.ts +1 -1
  144. package/types/stable/ember/index.d.ts +1 -1
  145. package/types/stable/index.d.ts +5 -0
  146. package/dist/packages/shared-chunks/index-DTxy4Zgx.js +0 -641
  147. package/dist/packages/shared-chunks/index-PYiGj1jp.js +0 -2071
@@ -1,20 +1,22 @@
1
- import '../../shared-chunks/registry-DzfcDwii.js';
2
- import '../../shared-chunks/index-PYiGj1jp.js';
3
- import '../owner/index.js';
4
- export { j as default, G as triggerEvent } from '../../shared-chunks/index-BXPoca1S.js';
5
- import '../../shared-chunks/cache-gDE3bkXq.js';
1
+ import { p as privatize } from '../../shared-chunks/registry-B8WARvkP.js';
2
+ import EmberObject from '../object/index.js';
3
+ import { getOwner } from '../owner/index.js';
4
+ import BucketCache from './lib/cache.js';
5
+ import DSLImpl from './lib/dsl.js';
6
+ import RouterState from './lib/router_state.js';
7
+ import { resemblesURL, extractRouteArgs, getActiveTargetName, calculateCacheKey } from './lib/utils.js';
8
+ import { A } from '../array/index.js';
9
+ import { g as get, c as computed } from '../../shared-chunks/cache-BESCGvbE.js';
6
10
  import '../-internals/meta/lib/meta.js';
7
- import '../../shared-chunks/index-DTxy4Zgx.js';
11
+ import { info } from '../debug/index.js';
8
12
  import '../../@glimmer/validator/index.js';
9
- import '../../shared-chunks/mandatory-setter-1UQhiJOb.js';
10
- import '@embroider/macros';
13
+ import '../../shared-chunks/mandatory-setter-BiXq-dpN.js';
14
+ import { isDevelopingApp } from '@embroider/macros';
11
15
  import '../../@glimmer/destroyable/index.js';
12
16
  import '../../@glimmer/manager/index.js';
13
- import '../../shared-chunks/property_set-CW4q-uo4.js';
17
+ import { s as set } from '../../shared-chunks/property_set-BapAkp3X.js';
14
18
  import '../../shared-chunks/env-BJLX2Arx.js';
15
- import '../controller/index.js';
16
- import './lib/cache.js';
17
- import './lib/dsl.js';
19
+ import typeOf from '../utils/lib/type-of.js';
18
20
  import '../-internals/runtime/lib/mixins/registry_proxy.js';
19
21
  import '../-internals/runtime/lib/mixins/container_proxy.js';
20
22
  import '../-internals/runtime/lib/mixins/comparable.js';
@@ -23,6 +25,1439 @@ import '../-internals/runtime/lib/mixins/-proxy.js';
23
25
  import '../enumerable/mutable.js';
24
26
  import '../-internals/runtime/lib/mixins/target_action_support.js';
25
27
  import '../-internals/runtime/lib/ext/rsvp.js';
26
- import '../object/evented.js';
27
- import '../../shared-chunks/router-B-Q1aYBn.js';
28
+ import Evented from '../object/evented.js';
29
+ import { run, once, scheduleOnce, cancel } from '../runloop/index.js';
30
+ import { getRenderState, getFullQueryParams, hasDefaultSerialize, defaultSerialize } from './route.js';
31
+ import { R as Router } from '../../shared-chunks/router-DrLZsJeE.js';
32
+ import { S as STATE_SYMBOL, l as logAbort } from '../../shared-chunks/unrecognized-url-error-zpz-JEoG.js';
28
33
  import '../../shared-chunks/rsvp-DaQAFb0W.js';
34
+ import EngineInstance from '../engine/instance.js';
35
+ import { assert } from '../debug/lib/assert.js';
36
+
37
+ /**
38
+ @module @ember/routing/router
39
+ */
40
+
41
+ function defaultDidTransition(infos) {
42
+ updatePaths(this);
43
+ this._cancelSlowTransitionTimer();
44
+ this.notifyPropertyChange('url');
45
+ this.set('currentState', this.targetState);
46
+ if (isDevelopingApp()) {
47
+ // @ts-expect-error namespace isn't public
48
+ if (this.namespace.LOG_TRANSITIONS) {
49
+ // eslint-disable-next-line no-console
50
+ console.log(`Transitioned into '${EmberRouter._routePath(infos)}'`);
51
+ }
52
+ }
53
+ }
54
+ function defaultWillTransition(oldInfos, newInfos) {
55
+ if (isDevelopingApp()) {
56
+ // @ts-expect-error namespace isn't public
57
+ if (this.namespace.LOG_TRANSITIONS) {
58
+ // eslint-disable-next-line no-console
59
+ console.log(`Preparing to transition from '${EmberRouter._routePath(oldInfos)}' to '${EmberRouter._routePath(newInfos)}'`);
60
+ }
61
+ }
62
+ }
63
+ let freezeRouteInfo;
64
+ if (isDevelopingApp()) {
65
+ freezeRouteInfo = transition => {
66
+ if (transition.from !== null && !Object.isFrozen(transition.from)) {
67
+ Object.freeze(transition.from);
68
+ }
69
+ if (transition.to !== null && !Object.isFrozen(transition.to)) {
70
+ Object.freeze(transition.to);
71
+ }
72
+ };
73
+ }
74
+ function K() {
75
+ return this;
76
+ }
77
+ const {
78
+ slice
79
+ } = Array.prototype;
80
+
81
+ /**
82
+ The `EmberRouter` class manages the application state and URLs. Refer to
83
+ the [routing guide](https://guides.emberjs.com/release/routing/) for documentation.
84
+
85
+ @class EmberRouter
86
+ @extends EmberObject
87
+ @uses Evented
88
+ @public
89
+ */
90
+ class EmberRouter extends EmberObject.extend(Evented) {
91
+ /**
92
+ Represents the URL of the root of the application, often '/'. This prefix is
93
+ assumed on all routes defined on this router.
94
+ @property rootURL
95
+ @default '/'
96
+ @public
97
+ */
98
+ // Set with reopen to allow overriding via extend
99
+
100
+ /**
101
+ The `location` property determines the type of URL's that your
102
+ application will use.
103
+ The following location types are currently available:
104
+ * `history` - use the browser's history API to make the URLs look just like any standard URL
105
+ * `hash` - use `#` to separate the server part of the URL from the Ember part: `/blog/#/posts/new`
106
+ * `none` - do not store the Ember URL in the actual browser URL (mainly used for testing)
107
+ * `auto` - use the best option based on browser capabilities: `history` if possible, then `hash` if possible, otherwise `none`
108
+ This value is defaulted to `history` by the `locationType` setting of `/config/environment.js`
109
+ @property location
110
+ @default 'hash'
111
+ @see {Location}
112
+ @public
113
+ */
114
+ // Set with reopen to allow overriding via extend
115
+
116
+ _routerMicrolib;
117
+ _didSetupRouter = false;
118
+ _initialTransitionStarted = false;
119
+ currentURL = null;
120
+ currentRouteName = null;
121
+ currentPath = null;
122
+ currentRoute = null;
123
+ _qpCache = Object.create(null);
124
+
125
+ // Set of QueryParam['urlKey']
126
+ _qpUpdates = new Set();
127
+ _queuedQPChanges = {};
128
+ _bucketCache;
129
+ _toplevelView = null;
130
+ _handledErrors = new Set();
131
+ _engineInstances = Object.create(null);
132
+ _engineInfoByRoute = Object.create(null);
133
+ _routerService;
134
+ _slowTransitionTimer = null;
135
+ namespace;
136
+
137
+ // Begin Evented
138
+
139
+ // End Evented
140
+
141
+ // Set with reopenClass
142
+ static dslCallbacks;
143
+
144
+ /**
145
+ The `Router.map` function allows you to define mappings from URLs to routes
146
+ in your application. These mappings are defined within the
147
+ supplied callback function using `this.route`.
148
+ The first parameter is the name of the route which is used by default as the
149
+ path name as well.
150
+ The second parameter is the optional options hash. Available options are:
151
+ * `path`: allows you to provide your own path as well as mark dynamic
152
+ segments.
153
+ * `resetNamespace`: false by default; when nesting routes, ember will
154
+ combine the route names to form the fully-qualified route name, which is
155
+ used with `{{link-to}}` or manually transitioning to routes. Setting
156
+ `resetNamespace: true` will cause the route not to inherit from its
157
+ parent route's names. This is handy for preventing extremely long route names.
158
+ Keep in mind that the actual URL path behavior is still retained.
159
+ The third parameter is a function, which can be used to nest routes.
160
+ Nested routes, by default, will have the parent route tree's route name and
161
+ path prepended to it's own.
162
+ ```app/router.js
163
+ Router.map(function(){
164
+ this.route('post', { path: '/post/:post_id' }, function() {
165
+ this.route('edit');
166
+ this.route('comments', { resetNamespace: true }, function() {
167
+ this.route('new');
168
+ });
169
+ });
170
+ });
171
+ ```
172
+ @method map
173
+ @param callback
174
+ @public
175
+ */
176
+ static map(callback) {
177
+ if (!this.dslCallbacks) {
178
+ this.dslCallbacks = [];
179
+ // FIXME: Can we remove this?
180
+ this.reopenClass({
181
+ dslCallbacks: this.dslCallbacks
182
+ });
183
+ }
184
+ this.dslCallbacks.push(callback);
185
+ return this;
186
+ }
187
+ static _routePath(routeInfos) {
188
+ let path = [];
189
+
190
+ // We have to handle coalescing resource names that
191
+ // are prefixed with their parent's names, e.g.
192
+ // ['foo', 'foo.bar.baz'] => 'foo.bar.baz', not 'foo.foo.bar.baz'
193
+
194
+ function intersectionMatches(a1, a2) {
195
+ for (let i = 0; i < a1.length; ++i) {
196
+ if (a1[i] !== a2[i]) {
197
+ return false;
198
+ }
199
+ }
200
+ return true;
201
+ }
202
+ let name, nameParts, oldNameParts;
203
+ for (let i = 1; i < routeInfos.length; i++) {
204
+ let routeInfo = routeInfos[i];
205
+ (isDevelopingApp() && !(routeInfo) && assert('has routeInfo', routeInfo));
206
+ name = routeInfo.name;
207
+ nameParts = name.split('.');
208
+ oldNameParts = slice.call(path);
209
+ while (oldNameParts.length) {
210
+ if (intersectionMatches(oldNameParts, nameParts)) {
211
+ break;
212
+ }
213
+ oldNameParts.shift();
214
+ }
215
+ path.push(...nameParts.slice(oldNameParts.length));
216
+ }
217
+ return path.join('.');
218
+ }
219
+
220
+ // Note that owner is actually required in this scenario, but since it is strictly
221
+ // optional in other contexts trying to make it required here confuses TS.
222
+ constructor(owner) {
223
+ super(owner);
224
+ (isDevelopingApp() && !(owner) && assert('BUG: Missing owner', owner));
225
+ this._resetQueuedQueryParameterChanges();
226
+ this.namespace = owner.lookup('application:main');
227
+ let bucketCache = owner.lookup(privatize`-bucket-cache:main`);
228
+ (isDevelopingApp() && !(bucketCache instanceof BucketCache) && assert('BUG: BucketCache should always be present', bucketCache instanceof BucketCache));
229
+ this._bucketCache = bucketCache;
230
+ let routerService = owner.lookup('service:router');
231
+ (isDevelopingApp() && !(routerService !== undefined) && assert('BUG: RouterService should always be present', routerService !== undefined));
232
+ this._routerService = routerService;
233
+ }
234
+ _initRouterJs() {
235
+ let location = get(this, 'location');
236
+ let router = this;
237
+ const owner = getOwner(this);
238
+ (isDevelopingApp() && !(owner) && assert('Router is unexpectedly missing an owner', owner));
239
+ let seen = Object.create(null);
240
+ class PrivateRouter extends Router {
241
+ getRoute(name) {
242
+ let routeName = name;
243
+ let routeOwner = owner;
244
+ let engineInfo = router._engineInfoByRoute[routeName];
245
+ if (engineInfo) {
246
+ let engineInstance = router._getEngineInstance(engineInfo);
247
+ routeOwner = engineInstance;
248
+ routeName = engineInfo.localFullName;
249
+ }
250
+ let fullRouteName = `route:${routeName}`;
251
+ (isDevelopingApp() && !(routeOwner) && assert('Route is unexpectedly missing an owner', routeOwner));
252
+ let route = routeOwner.lookup(fullRouteName);
253
+ if (seen[name]) {
254
+ (isDevelopingApp() && !(route) && assert('seen routes should exist', route));
255
+ return route;
256
+ }
257
+ seen[name] = true;
258
+ if (!route) {
259
+ // SAFETY: this is configured in `commonSetupRegistry` in the
260
+ // `@ember/application/lib` package.
261
+ let DefaultRoute = routeOwner.factoryFor('route:basic').class;
262
+ routeOwner.register(fullRouteName, DefaultRoute.extend());
263
+ route = routeOwner.lookup(fullRouteName);
264
+ if (isDevelopingApp()) {
265
+ if (router.namespace.LOG_ACTIVE_GENERATION) {
266
+ info(`generated -> ${fullRouteName}`, {
267
+ fullName: fullRouteName
268
+ });
269
+ }
270
+ }
271
+ }
272
+ route._setRouteName(routeName);
273
+ if (engineInfo && !hasDefaultSerialize(route)) {
274
+ throw new Error('Defining a custom serialize method on an Engine route is not supported.');
275
+ }
276
+ return route;
277
+ }
278
+ getSerializer(name) {
279
+ let engineInfo = router._engineInfoByRoute[name];
280
+
281
+ // If this is not an Engine route, we fall back to the handler for serialization
282
+ if (!engineInfo) {
283
+ return;
284
+ }
285
+ return engineInfo.serializeMethod || defaultSerialize;
286
+ }
287
+ updateURL(path) {
288
+ once(() => {
289
+ location.setURL(path);
290
+ set(router, 'currentURL', path);
291
+ });
292
+ }
293
+
294
+ // TODO: merge into routeDidChange
295
+ didTransition(infos) {
296
+ (isDevelopingApp() && !(router.didTransition === defaultDidTransition) && assert('You attempted to override the "didTransition" method which has been deprecated. Please inject the router service and listen to the "routeDidChange" event.', router.didTransition === defaultDidTransition));
297
+ router.didTransition(infos);
298
+ }
299
+
300
+ // TODO: merge into routeWillChange
301
+ willTransition(oldInfos, newInfos) {
302
+ (isDevelopingApp() && !(router.willTransition === defaultWillTransition) && assert('You attempted to override the "willTransition" method which has been deprecated. Please inject the router service and listen to the "routeWillChange" event.', router.willTransition === defaultWillTransition));
303
+ router.willTransition(oldInfos, newInfos);
304
+ }
305
+ triggerEvent(routeInfos, ignoreFailure, name, args) {
306
+ return triggerEvent.bind(router)(routeInfos, ignoreFailure, name, args);
307
+ }
308
+ routeWillChange(transition) {
309
+ router.trigger('routeWillChange', transition);
310
+ if (isDevelopingApp()) {
311
+ freezeRouteInfo(transition);
312
+ }
313
+ router._routerService.trigger('routeWillChange', transition);
314
+
315
+ // in case of intermediate transition we update the current route
316
+ // to make router.currentRoute.name consistent with router.currentRouteName
317
+ // see https://github.com/emberjs/ember.js/issues/19449
318
+ if (transition.isIntermediate) {
319
+ router.set('currentRoute', transition.to);
320
+ }
321
+ }
322
+ routeDidChange(transition) {
323
+ router.set('currentRoute', transition.to);
324
+ once(() => {
325
+ router.trigger('routeDidChange', transition);
326
+ if (isDevelopingApp()) {
327
+ freezeRouteInfo(transition);
328
+ }
329
+ router._routerService.trigger('routeDidChange', transition);
330
+ });
331
+ }
332
+ transitionDidError(error, transition) {
333
+ if (error.wasAborted || transition.isAborted) {
334
+ // If the error was a transition erorr or the transition aborted
335
+ // log the abort.
336
+ return logAbort(transition);
337
+ } else {
338
+ // Otherwise trigger the "error" event to attempt an intermediate
339
+ // transition into an error substate
340
+ transition.trigger(false, 'error', error.error, transition, error.route);
341
+ if (router._isErrorHandled(error.error)) {
342
+ // If we handled the error with a substate just roll the state back on
343
+ // the transition and send the "routeDidChange" event for landing on
344
+ // the error substate and return the error.
345
+ transition.rollback();
346
+ this.routeDidChange(transition);
347
+ return error.error;
348
+ } else {
349
+ // If it was not handled, abort the transition completely and return
350
+ // the error.
351
+ transition.abort();
352
+ return error.error;
353
+ }
354
+ }
355
+ }
356
+ replaceURL(url) {
357
+ if (location.replaceURL) {
358
+ let doReplaceURL = () => {
359
+ location.replaceURL(url);
360
+ set(router, 'currentURL', url);
361
+ };
362
+ once(doReplaceURL);
363
+ } else {
364
+ this.updateURL(url);
365
+ }
366
+ }
367
+ }
368
+ let routerMicrolib = this._routerMicrolib = new PrivateRouter();
369
+ let dslCallbacks = this.constructor.dslCallbacks || [K];
370
+ let dsl = this._buildDSL();
371
+ dsl.route('application', {
372
+ path: '/',
373
+ resetNamespace: true,
374
+ overrideNameAssertion: true
375
+ }, function () {
376
+ for (let i = 0; i < dslCallbacks.length; i++) {
377
+ dslCallbacks[i].call(this);
378
+ }
379
+ });
380
+ if (isDevelopingApp()) {
381
+ if (this.namespace.LOG_TRANSITIONS_INTERNAL) {
382
+ routerMicrolib.log = console.log.bind(console); // eslint-disable-line no-console
383
+ }
384
+ }
385
+ routerMicrolib.map(dsl.generate());
386
+ }
387
+ _buildDSL() {
388
+ let enableLoadingSubstates = this._hasModuleBasedResolver();
389
+ let router = this;
390
+ const owner = getOwner(this);
391
+ (isDevelopingApp() && !(owner) && assert('Router is unexpectedly missing an owner', owner));
392
+ let options = {
393
+ enableLoadingSubstates,
394
+ resolveRouteMap(name) {
395
+ return owner.factoryFor(`route-map:${name}`);
396
+ },
397
+ addRouteForEngine(name, engineInfo) {
398
+ if (!router._engineInfoByRoute[name]) {
399
+ router._engineInfoByRoute[name] = engineInfo;
400
+ }
401
+ }
402
+ };
403
+ return new DSLImpl(null, options);
404
+ }
405
+
406
+ /*
407
+ Resets all pending query parameter changes.
408
+ Called after transitioning to a new route
409
+ based on query parameter changes.
410
+ */
411
+ _resetQueuedQueryParameterChanges() {
412
+ this._queuedQPChanges = {};
413
+ }
414
+ _hasModuleBasedResolver() {
415
+ let owner = getOwner(this);
416
+ (isDevelopingApp() && !(owner) && assert('Router is unexpectedly missing an owner', owner));
417
+ let resolver = get(owner, 'application.__registry__.resolver.moduleBasedResolver');
418
+ return Boolean(resolver);
419
+ }
420
+
421
+ /**
422
+ Initializes the current router instance and sets up the change handling
423
+ event listeners used by the instances `location` implementation.
424
+ A property named `initialURL` will be used to determine the initial URL.
425
+ If no value is found `/` will be used.
426
+ @method startRouting
427
+ @private
428
+ */
429
+ startRouting() {
430
+ if (this.setupRouter()) {
431
+ let initialURL = get(this, 'initialURL');
432
+ if (initialURL === undefined) {
433
+ initialURL = get(this, 'location').getURL();
434
+ }
435
+ let initialTransition = this.handleURL(initialURL);
436
+ if (initialTransition && initialTransition.error) {
437
+ throw initialTransition.error;
438
+ }
439
+ }
440
+ }
441
+ setupRouter() {
442
+ if (this._didSetupRouter) {
443
+ return false;
444
+ }
445
+ this._didSetupRouter = true;
446
+ this._setupLocation();
447
+ let location = get(this, 'location');
448
+
449
+ // Allow the Location class to cancel the router setup while it refreshes
450
+ // the page
451
+ if (get(location, 'cancelRouterSetup')) {
452
+ return false;
453
+ }
454
+ this._initRouterJs();
455
+ location.onUpdateURL(url => {
456
+ this.handleURL(url);
457
+ });
458
+ return true;
459
+ }
460
+ _setOutlets() {
461
+ // This is triggered async during Route#willDestroy.
462
+ // If the router is also being destroyed we do not want to
463
+ // to create another this._toplevelView (and leak the renderer)
464
+ if (this.isDestroying || this.isDestroyed) {
465
+ return;
466
+ }
467
+ let routeInfos = this._routerMicrolib.currentRouteInfos;
468
+ if (!routeInfos) {
469
+ return;
470
+ }
471
+ let root = null;
472
+ let parent = null;
473
+ for (let routeInfo of routeInfos) {
474
+ let route = routeInfo.route;
475
+ let render = getRenderState(route);
476
+ if (render) {
477
+ let state = {
478
+ render,
479
+ outlets: {
480
+ main: undefined
481
+ }
482
+ };
483
+ if (parent) {
484
+ parent.outlets.main = state;
485
+ } else {
486
+ root = state;
487
+ }
488
+ parent = state;
489
+ } else {
490
+ // It used to be that we would create a stub entry and keep traversing,
491
+ // but I don't think that is necessary anymore – if a parent route did
492
+ // not render, then the child routes have nowhere to render into these
493
+ // days. That wasn't always the case since in the past any route can
494
+ // render into any other route's outlets.
495
+ break;
496
+ }
497
+ }
498
+
499
+ // when a transitionTo happens after the validation phase
500
+ // during the initial transition _setOutlets is called
501
+ // when no routes are active. However, it will get called
502
+ // again with the correct values during the next turn of
503
+ // the runloop
504
+ if (root === null) {
505
+ return;
506
+ }
507
+ if (!this._toplevelView) {
508
+ let owner = getOwner(this);
509
+ (isDevelopingApp() && !(owner) && assert('Router is unexpectedly missing an owner', owner)); // SAFETY: we don't presently have any type registries internally to make
510
+ // this safe, so in each of these cases we assume that nothing *else* is
511
+ // registered at this `FullName`, and simply check to make sure that
512
+ // *something* is.
513
+ let OutletView = owner.factoryFor('view:-outlet');
514
+ (isDevelopingApp() && !(OutletView !== undefined) && assert('[BUG] unexpectedly missing `view:-outlet`', OutletView !== undefined));
515
+ let application = owner.lookup('application:main');
516
+ (isDevelopingApp() && !(application !== undefined) && assert('[BUG] unexpectedly missing `application:-main`', application !== undefined));
517
+ let environment = owner.lookup('-environment:main');
518
+ (isDevelopingApp() && !(environment !== undefined) && assert('[BUG] unexpectedly missing `-environment:main`', environment !== undefined));
519
+ let template = owner.lookup('template:-outlet');
520
+ (isDevelopingApp() && !(template !== undefined) && assert('[BUG] unexpectedly missing `template:-outlet`', template !== undefined));
521
+ this._toplevelView = OutletView.create({
522
+ environment,
523
+ template,
524
+ application
525
+ });
526
+ this._toplevelView.setOutletState(root);
527
+
528
+ // TODO(SAFETY): At least one test runs without this set correctly. At a
529
+ // later time, update the test to configure this correctly. The test ID:
530
+ // `Router Service - non application test: RouterService#transitionTo with basic route`
531
+ let instance = owner.lookup('-application-instance:main');
532
+ // let instance = owner.lookup('-application-instance:main') as ApplicationInstance | undefined;
533
+ // assert('[BUG] unexpectedly missing `-application-instance:main`', instance !== undefined);
534
+
535
+ if (instance) {
536
+ // SAFETY: LOL. This is calling a deprecated API with a type that we
537
+ // cannot actually confirm at a type level *is* a `ViewMixin`. Seems:
538
+ // not great on multiple fronts!
539
+ instance.didCreateRootView(this._toplevelView);
540
+ }
541
+ } else {
542
+ this._toplevelView.setOutletState(root);
543
+ }
544
+ }
545
+ handleURL(url) {
546
+ // Until we have an ember-idiomatic way of accessing #hashes, we need to
547
+ // remove it because router.js doesn't know how to handle it.
548
+ let _url = url.split(/#(.+)?/)[0];
549
+ return this._doURLTransition('handleURL', _url);
550
+ }
551
+ _doURLTransition(routerJsMethod, url) {
552
+ this._initialTransitionStarted = true;
553
+ let transition = this._routerMicrolib[routerJsMethod](url || '/');
554
+ didBeginTransition(transition, this);
555
+ return transition;
556
+ }
557
+
558
+ /**
559
+ Transition the application into another route. The route may
560
+ be either a single route or route path:
561
+ @method transitionTo
562
+ @param {String} [name] the name of the route or a URL
563
+ @param {...Object} models the model(s) or identifier(s) to be used while
564
+ transitioning to the route.
565
+ @param {Object} [options] optional hash with a queryParams property
566
+ containing a mapping of query parameters
567
+ @return {Transition} the transition object associated with this
568
+ attempted transition
569
+ @public
570
+ */
571
+ transitionTo(...args) {
572
+ if (resemblesURL(args[0])) {
573
+ (isDevelopingApp() && !(!this.isDestroying && !this.isDestroyed) && assert(`A transition was attempted from '${this.currentRouteName}' to '${args[0]}' but the application instance has already been destroyed.`, !this.isDestroying && !this.isDestroyed));
574
+ return this._doURLTransition('transitionTo', args[0]);
575
+ }
576
+ let {
577
+ routeName,
578
+ models,
579
+ queryParams
580
+ } = extractRouteArgs(args);
581
+ (isDevelopingApp() && !(!this.isDestroying && !this.isDestroyed) && assert(`A transition was attempted from '${this.currentRouteName}' to '${routeName}' but the application instance has already been destroyed.`, !this.isDestroying && !this.isDestroyed));
582
+ return this._doTransition(routeName, models, queryParams);
583
+ }
584
+ intermediateTransitionTo(name, ...args) {
585
+ this._routerMicrolib.intermediateTransitionTo(name, ...args);
586
+ updatePaths(this);
587
+ if (isDevelopingApp()) {
588
+ let infos = this._routerMicrolib.currentRouteInfos;
589
+ if (this.namespace.LOG_TRANSITIONS) {
590
+ (isDevelopingApp() && !(infos) && assert('expected infos to be set', infos)); // eslint-disable-next-line no-console
591
+ console.log(`Intermediate-transitioned into '${EmberRouter._routePath(infos)}'`);
592
+ }
593
+ }
594
+ }
595
+
596
+ /**
597
+ Similar to `transitionTo`, but instead of adding the destination to the browser's URL history,
598
+ it replaces the entry for the current route.
599
+ When the user clicks the "back" button in the browser, there will be fewer steps.
600
+ This is most commonly used to manage redirects in a way that does not cause confusing additions
601
+ to the user's browsing history.
602
+ @method replaceWith
603
+ @param {String} [name] the name of the route or a URL
604
+ @param {...Object} models the model(s) or identifier(s) to be used while
605
+ transitioning to the route.
606
+ @param {Object} [options] optional hash with a queryParams property
607
+ containing a mapping of query parameters
608
+ @return {Transition} the transition object associated with this
609
+ attempted transition
610
+ @public
611
+ */
612
+ replaceWith(...args) {
613
+ return this.transitionTo(...args).method('replace');
614
+ }
615
+ generate(name, ...args) {
616
+ let url = this._routerMicrolib.generate(name, ...args);
617
+ (isDevelopingApp() && !(typeof this.location !== 'string') && assert('expected non-string location', typeof this.location !== 'string'));
618
+ return this.location.formatURL(url);
619
+ }
620
+
621
+ /**
622
+ Determines if the supplied route is currently active.
623
+ @method isActive
624
+ @param routeName
625
+ @return {Boolean}
626
+ @private
627
+ */
628
+ isActive(routeName) {
629
+ return this._routerMicrolib.isActive(routeName);
630
+ }
631
+
632
+ /**
633
+ An alternative form of `isActive` that doesn't require
634
+ manual concatenation of the arguments into a single
635
+ array.
636
+ @method isActiveIntent
637
+ @param routeName
638
+ @param models
639
+ @param queryParams
640
+ @return {Boolean}
641
+ @private
642
+ @since 1.7.0
643
+ */
644
+ isActiveIntent(routeName, models, queryParams) {
645
+ return this.currentState.isActiveIntent(routeName, models, queryParams);
646
+ }
647
+ send(name, ...args) {
648
+ /*name, context*/
649
+ this._routerMicrolib.trigger(name, ...args);
650
+ }
651
+
652
+ /**
653
+ Does this router instance have the given route.
654
+ @method hasRoute
655
+ @return {Boolean}
656
+ @private
657
+ */
658
+ hasRoute(route) {
659
+ return this._routerMicrolib.hasRoute(route);
660
+ }
661
+
662
+ /**
663
+ Resets the state of the router by clearing the current route
664
+ handlers and deactivating them.
665
+ @private
666
+ @method reset
667
+ */
668
+ reset() {
669
+ this._didSetupRouter = false;
670
+ this._initialTransitionStarted = false;
671
+ if (this._routerMicrolib) {
672
+ this._routerMicrolib.reset();
673
+ }
674
+ }
675
+ willDestroy() {
676
+ if (this._toplevelView) {
677
+ this._toplevelView.destroy();
678
+ this._toplevelView = null;
679
+ }
680
+ super.willDestroy();
681
+ this.reset();
682
+ let instances = this._engineInstances;
683
+ for (let name in instances) {
684
+ let instanceMap = instances[name];
685
+ (isDevelopingApp() && !(instanceMap) && assert('has instanceMap', instanceMap));
686
+ for (let id in instanceMap) {
687
+ let instance = instanceMap[id];
688
+ (isDevelopingApp() && !(instance) && assert('has instance', instance));
689
+ run(instance, 'destroy');
690
+ }
691
+ }
692
+ }
693
+
694
+ /*
695
+ Called when an active route's query parameter has changed.
696
+ These changes are batched into a runloop run and trigger
697
+ a single transition.
698
+ */
699
+ _activeQPChanged(queryParameterName, newValue) {
700
+ this._queuedQPChanges[queryParameterName] = newValue;
701
+ once(this, this._fireQueryParamTransition);
702
+ }
703
+
704
+ // The queryParameterName is QueryParam['urlKey']
705
+ _updatingQPChanged(queryParameterName) {
706
+ this._qpUpdates.add(queryParameterName);
707
+ }
708
+
709
+ /*
710
+ Triggers a transition to a route based on query parameter changes.
711
+ This is called once per runloop, to batch changes.
712
+ e.g.
713
+ if these methods are called in succession:
714
+ this._activeQPChanged('foo', '10');
715
+ // results in _queuedQPChanges = { foo: '10' }
716
+ this._activeQPChanged('bar', false);
717
+ // results in _queuedQPChanges = { foo: '10', bar: false }
718
+ _queuedQPChanges will represent both of these changes
719
+ and the transition using `transitionTo` will be triggered
720
+ once.
721
+ */
722
+ _fireQueryParamTransition() {
723
+ this.transitionTo({
724
+ queryParams: this._queuedQPChanges
725
+ });
726
+ this._resetQueuedQueryParameterChanges();
727
+ }
728
+ _setupLocation() {
729
+ let location = this.location;
730
+ let rootURL = this.rootURL;
731
+ let owner = getOwner(this);
732
+ (isDevelopingApp() && !(owner) && assert('Router is unexpectedly missing an owner', owner));
733
+ if ('string' === typeof location) {
734
+ let resolvedLocation = owner.lookup(`location:${location}`);
735
+ (isDevelopingApp() && !(resolvedLocation) && assert(`Could not resolve a location class at 'location:${location}'`, resolvedLocation));
736
+ location = set(this, 'location', resolvedLocation);
737
+ }
738
+ if (location !== null && typeof location === 'object') {
739
+ if (rootURL) {
740
+ set(location, 'rootURL', rootURL);
741
+ }
742
+
743
+ // ensure that initState is called AFTER the rootURL is set on
744
+ // the location instance
745
+ if (typeof location.initState === 'function') {
746
+ location.initState();
747
+ }
748
+ }
749
+ }
750
+
751
+ /**
752
+ Serializes the given query params according to their QP meta information.
753
+ @private
754
+ @method _serializeQueryParams
755
+ @param {Arrray<RouteInfo>} routeInfos
756
+ @param {Object} queryParams
757
+ @return {Void}
758
+ */
759
+ _serializeQueryParams(routeInfos, queryParams) {
760
+ forEachQueryParam(this, routeInfos, queryParams, (key, value, qp) => {
761
+ if (qp) {
762
+ delete queryParams[key];
763
+ queryParams[qp.urlKey] = qp.route.serializeQueryParam(value, qp.urlKey, qp.type);
764
+ } else if (value === undefined) {
765
+ return; // We don't serialize undefined values
766
+ } else {
767
+ queryParams[key] = this._serializeQueryParam(value, typeOf(value));
768
+ }
769
+ });
770
+ }
771
+
772
+ /**
773
+ Serializes the value of a query parameter based on a type
774
+ @private
775
+ @method _serializeQueryParam
776
+ @param {Object} value
777
+ @param {String} type
778
+ */
779
+ _serializeQueryParam(value, type) {
780
+ if (value === null || value === undefined) {
781
+ return value;
782
+ } else if (type === 'array') {
783
+ return JSON.stringify(value);
784
+ }
785
+ return `${value}`;
786
+ }
787
+
788
+ /**
789
+ Deserializes the given query params according to their QP meta information.
790
+ @private
791
+ @method _deserializeQueryParams
792
+ @param {Array<RouteInfo>} routeInfos
793
+ @param {Object} queryParams
794
+ @return {Void}
795
+ */
796
+ _deserializeQueryParams(routeInfos, queryParams) {
797
+ forEachQueryParam(this, routeInfos, queryParams, (key, value, qp) => {
798
+ // If we don't have QP meta info for a given key, then we do nothing
799
+ // because all values will be treated as strings
800
+ if (qp) {
801
+ delete queryParams[key];
802
+ queryParams[qp.prop] = qp.route.deserializeQueryParam(value, qp.urlKey, qp.type);
803
+ }
804
+ });
805
+ }
806
+
807
+ /**
808
+ Deserializes the value of a query parameter based on a default type
809
+ @private
810
+ @method _deserializeQueryParam
811
+ @param {Object} value
812
+ @param {String} defaultType
813
+ */
814
+ _deserializeQueryParam(value, defaultType) {
815
+ if (value === null || value === undefined) {
816
+ return value;
817
+ } else if (defaultType === 'boolean') {
818
+ return value === 'true';
819
+ } else if (defaultType === 'number') {
820
+ return Number(value).valueOf();
821
+ } else if (defaultType === 'array') {
822
+ return A(JSON.parse(value));
823
+ }
824
+ return value;
825
+ }
826
+
827
+ /**
828
+ Removes (prunes) any query params with default values from the given QP
829
+ object. Default values are determined from the QP meta information per key.
830
+ @private
831
+ @method _pruneDefaultQueryParamValues
832
+ @param {Array<RouteInfo>} routeInfos
833
+ @param {Object} queryParams
834
+ @return {Void}
835
+ */
836
+ _pruneDefaultQueryParamValues(routeInfos, queryParams) {
837
+ let qps = this._queryParamsFor(routeInfos);
838
+ for (let key in queryParams) {
839
+ let qp = qps.map[key];
840
+ if (qp && qp.serializedDefaultValue === queryParams[key]) {
841
+ delete queryParams[key];
842
+ }
843
+ }
844
+ }
845
+ _doTransition(_targetRouteName, models, _queryParams, _fromRouterService) {
846
+ let targetRouteName = _targetRouteName || getActiveTargetName(this._routerMicrolib);
847
+ (isDevelopingApp() && !(Boolean(targetRouteName) && this._routerMicrolib.hasRoute(targetRouteName)) && assert(`The route ${targetRouteName} was not found`, Boolean(targetRouteName) && this._routerMicrolib.hasRoute(targetRouteName)));
848
+ this._initialTransitionStarted = true;
849
+ let queryParams = {};
850
+ this._processActiveTransitionQueryParams(targetRouteName, models, queryParams, _queryParams);
851
+ Object.assign(queryParams, _queryParams);
852
+ this._prepareQueryParams(targetRouteName, models, queryParams, Boolean(_fromRouterService));
853
+ let transition = this._routerMicrolib.transitionTo(targetRouteName, ...models, {
854
+ queryParams
855
+ });
856
+ didBeginTransition(transition, this);
857
+ return transition;
858
+ }
859
+ _processActiveTransitionQueryParams(targetRouteName, models, queryParams, _queryParams) {
860
+ // merge in any queryParams from the active transition which could include
861
+ // queryParams from the url on initial load.
862
+ if (!this._routerMicrolib.activeTransition) {
863
+ return;
864
+ }
865
+ let unchangedQPs = {};
866
+ let qpUpdates = this._qpUpdates;
867
+ let params = getFullQueryParams(this, this._routerMicrolib.activeTransition[STATE_SYMBOL]);
868
+ for (let key in params) {
869
+ if (!qpUpdates.has(key)) {
870
+ unchangedQPs[key] = params[key];
871
+ }
872
+ }
873
+
874
+ // We need to fully scope queryParams so that we can create one object
875
+ // that represents both passed-in queryParams and ones that aren't changed
876
+ // from the active transition.
877
+ this._fullyScopeQueryParams(targetRouteName, models, _queryParams);
878
+ this._fullyScopeQueryParams(targetRouteName, models, unchangedQPs);
879
+ Object.assign(queryParams, unchangedQPs);
880
+ }
881
+
882
+ /**
883
+ Prepares the query params for a URL or Transition. Restores any undefined QP
884
+ keys/values, serializes all values, and then prunes any default values.
885
+ @private
886
+ @method _prepareQueryParams
887
+ @param {String} targetRouteName
888
+ @param {Array<Object>} models
889
+ @param {Object} queryParams
890
+ @param {boolean} keepDefaultQueryParamValues
891
+ @return {Void}
892
+ */
893
+ _prepareQueryParams(targetRouteName, models, queryParams, _fromRouterService) {
894
+ let state = calculatePostTransitionState(this, targetRouteName, models);
895
+ this._hydrateUnsuppliedQueryParams(state, queryParams, Boolean(_fromRouterService));
896
+ this._serializeQueryParams(state.routeInfos, queryParams);
897
+ if (!_fromRouterService) {
898
+ this._pruneDefaultQueryParamValues(state.routeInfos, queryParams);
899
+ }
900
+ }
901
+
902
+ /**
903
+ Returns the meta information for the query params of a given route. This
904
+ will be overridden to allow support for lazy routes.
905
+ @private
906
+ @method _getQPMeta
907
+ @param {RouteInfo} routeInfo
908
+ @return {Object}
909
+ */
910
+ _getQPMeta(routeInfo) {
911
+ let route = routeInfo.route;
912
+ return route && get(route, '_qp');
913
+ }
914
+
915
+ /**
916
+ Returns a merged query params meta object for a given set of routeInfos.
917
+ Useful for knowing what query params are available for a given route hierarchy.
918
+ @private
919
+ @method _queryParamsFor
920
+ @param {Array<RouteInfo>} routeInfos
921
+ @return {Object}
922
+ */
923
+ _queryParamsFor(routeInfos) {
924
+ let routeInfoLength = routeInfos.length;
925
+ let leafRouteName = routeInfos[routeInfoLength - 1].name;
926
+ let cached = this._qpCache[leafRouteName];
927
+ if (cached !== undefined) {
928
+ return cached;
929
+ }
930
+ let shouldCache = true;
931
+ let map = {};
932
+ let qps = [];
933
+ let qpsByUrlKey = isDevelopingApp() ? {} : null;
934
+ let qpMeta;
935
+ let urlKey;
936
+ let qpOther;
937
+ for (let routeInfo of routeInfos) {
938
+ qpMeta = this._getQPMeta(routeInfo);
939
+ if (!qpMeta) {
940
+ shouldCache = false;
941
+ continue;
942
+ }
943
+
944
+ // Loop over each QP to make sure we don't have any collisions by urlKey
945
+ for (let qp of qpMeta.qps) {
946
+ if (isDevelopingApp()) {
947
+ urlKey = qp.urlKey;
948
+ qpOther = qpsByUrlKey[urlKey];
949
+ if (qpOther && qpOther.controllerName !== qp.controllerName) {
950
+ (isDevelopingApp() && !(false) && assert(`You're not allowed to have more than one controller property map to the same query param key, but both \`${qpOther.scopedPropertyName}\` and \`${qp.scopedPropertyName}\` map to \`${urlKey}\`. You can fix this by mapping one of the controller properties to a different query param key via the \`as\` config option, e.g. \`${qpOther.prop}: { as: 'other-${qpOther.prop}' }\``, false));
951
+ }
952
+ qpsByUrlKey[urlKey] = qp;
953
+ }
954
+ qps.push(qp);
955
+ }
956
+ Object.assign(map, qpMeta.map);
957
+ }
958
+ let finalQPMeta = {
959
+ qps,
960
+ map
961
+ };
962
+ if (shouldCache) {
963
+ this._qpCache[leafRouteName] = finalQPMeta;
964
+ }
965
+ return finalQPMeta;
966
+ }
967
+
968
+ /**
969
+ Maps all query param keys to their fully scoped property name of the form
970
+ `controllerName:propName`.
971
+ @private
972
+ @method _fullyScopeQueryParams
973
+ @param {String} leafRouteName
974
+ @param {Array<Object>} contexts
975
+ @param {Object} queryParams
976
+ @return {Void}
977
+ */
978
+ _fullyScopeQueryParams(leafRouteName, contexts, queryParams) {
979
+ let state = calculatePostTransitionState(this, leafRouteName, contexts);
980
+ let routeInfos = state.routeInfos;
981
+ let qpMeta;
982
+ for (let routeInfo of routeInfos) {
983
+ qpMeta = this._getQPMeta(routeInfo);
984
+ if (!qpMeta) {
985
+ continue;
986
+ }
987
+ for (let qp of qpMeta.qps) {
988
+ let presentProp = qp.prop in queryParams && qp.prop || qp.scopedPropertyName in queryParams && qp.scopedPropertyName || qp.urlKey in queryParams && qp.urlKey;
989
+ if (presentProp) {
990
+ if (presentProp !== qp.scopedPropertyName) {
991
+ queryParams[qp.scopedPropertyName] = queryParams[presentProp];
992
+ delete queryParams[presentProp];
993
+ }
994
+ }
995
+ }
996
+ }
997
+ }
998
+
999
+ /**
1000
+ Hydrates (adds/restores) any query params that have pre-existing values into
1001
+ the given queryParams hash. This is what allows query params to be "sticky"
1002
+ and restore their last known values for their scope.
1003
+ @private
1004
+ @method _hydrateUnsuppliedQueryParams
1005
+ @param {TransitionState} state
1006
+ @param {Object} queryParams
1007
+ @return {Void}
1008
+ */
1009
+ _hydrateUnsuppliedQueryParams(state, queryParams, _fromRouterService) {
1010
+ let routeInfos = state.routeInfos;
1011
+ let appCache = this._bucketCache;
1012
+ let qpMeta;
1013
+ let qp;
1014
+ let presentProp;
1015
+ for (let routeInfo of routeInfos) {
1016
+ qpMeta = this._getQPMeta(routeInfo);
1017
+ if (!qpMeta) {
1018
+ continue;
1019
+ }
1020
+
1021
+ // Needs to stay for index loop to avoid throwIfClosureRequired
1022
+ for (let j = 0, qpLen = qpMeta.qps.length; j < qpLen; ++j) {
1023
+ qp = qpMeta.qps[j];
1024
+ (isDevelopingApp() && !(qp) && assert('expected qp', qp));
1025
+ presentProp = qp.prop in queryParams && qp.prop || qp.scopedPropertyName in queryParams && qp.scopedPropertyName || qp.urlKey in queryParams && qp.urlKey;
1026
+ (isDevelopingApp() && !(function () {
1027
+ if (qp.urlKey === presentProp || qp.scopedPropertyName === presentProp) {
1028
+ return true;
1029
+ }
1030
+ if (_fromRouterService && presentProp !== false && qp.urlKey !== qp.prop) {
1031
+ // assumptions (mainly from current transitionTo_test):
1032
+ // - this is only supposed to be run when there is an alias to a query param and the alias is used to set the param
1033
+ // - when there is no alias: qp.urlKey == qp.prop
1034
+ return false;
1035
+ }
1036
+ return true;
1037
+ }()) && assert(`You passed the \`${presentProp}\` query parameter during a transition into ${qp.route.routeName}, please update to ${qp.urlKey}`, function () {
1038
+ if (qp.urlKey === presentProp || qp.scopedPropertyName === presentProp) {
1039
+ return true;
1040
+ }
1041
+ if (_fromRouterService && presentProp !== false && qp.urlKey !== qp.prop) {
1042
+ return false;
1043
+ }
1044
+ return true;
1045
+ }()));
1046
+ if (presentProp) {
1047
+ if (presentProp !== qp.scopedPropertyName) {
1048
+ queryParams[qp.scopedPropertyName] = queryParams[presentProp];
1049
+ delete queryParams[presentProp];
1050
+ }
1051
+ } else {
1052
+ let cacheKey = calculateCacheKey(qp.route.fullRouteName, qp.parts, state.params);
1053
+ (isDevelopingApp() && !(appCache) && assert('ROUTER BUG: expected appCache to be defined. This is an internal bug, please open an issue on Github if you see this message!', appCache));
1054
+ queryParams[qp.scopedPropertyName] = appCache.lookup(cacheKey, qp.prop, qp.defaultValue);
1055
+ }
1056
+ }
1057
+ }
1058
+ }
1059
+ _scheduleLoadingEvent(transition, originRoute) {
1060
+ this._cancelSlowTransitionTimer();
1061
+ this._slowTransitionTimer = scheduleOnce('routerTransitions', this, this._handleSlowTransition, transition, originRoute);
1062
+ }
1063
+ currentState = null;
1064
+ targetState = null;
1065
+ _handleSlowTransition(transition, originRoute) {
1066
+ if (!this._routerMicrolib.activeTransition) {
1067
+ // Don't fire an event if we've since moved on from
1068
+ // the transition that put us in a loading state.
1069
+ return;
1070
+ }
1071
+ let targetState = new RouterState(this, this._routerMicrolib, this._routerMicrolib.activeTransition[STATE_SYMBOL]);
1072
+ this.set('targetState', targetState);
1073
+ transition.trigger(true, 'loading', transition, originRoute);
1074
+ }
1075
+ _cancelSlowTransitionTimer() {
1076
+ if (this._slowTransitionTimer) {
1077
+ cancel(this._slowTransitionTimer);
1078
+ }
1079
+ this._slowTransitionTimer = null;
1080
+ }
1081
+
1082
+ // These three helper functions are used to ensure errors aren't
1083
+ // re-raised if they're handled in a route's error action.
1084
+ _markErrorAsHandled(error) {
1085
+ this._handledErrors.add(error);
1086
+ }
1087
+ _isErrorHandled(error) {
1088
+ return this._handledErrors.has(error);
1089
+ }
1090
+ _clearHandledError(error) {
1091
+ this._handledErrors.delete(error);
1092
+ }
1093
+ _getEngineInstance({
1094
+ name,
1095
+ instanceId,
1096
+ mountPoint
1097
+ }) {
1098
+ let engineInstances = this._engineInstances;
1099
+ let namedInstances = engineInstances[name];
1100
+ if (!namedInstances) {
1101
+ namedInstances = Object.create(null);
1102
+ engineInstances[name] = namedInstances;
1103
+ }
1104
+
1105
+ // We just set these!
1106
+ (isDevelopingApp() && !(namedInstances) && assert('has namedInstances', namedInstances));
1107
+ let engineInstance = namedInstances[instanceId];
1108
+ if (!engineInstance) {
1109
+ let owner = getOwner(this);
1110
+ (isDevelopingApp() && !(owner instanceof EngineInstance) && assert('Expected router to have EngineInstance as owner', owner instanceof EngineInstance));
1111
+ (isDevelopingApp() && !(owner.hasRegistration(`engine:${name}`)) && assert(`You attempted to mount the engine '${name}' in your router map, but the engine can not be found.`, owner.hasRegistration(`engine:${name}`)));
1112
+ engineInstance = owner.buildChildEngineInstance(name, {
1113
+ routable: true,
1114
+ mountPoint
1115
+ });
1116
+ engineInstance.boot();
1117
+ namedInstances[instanceId] = engineInstance;
1118
+ }
1119
+ return engineInstance;
1120
+ }
1121
+
1122
+ /**
1123
+ Handles updating the paths and notifying any listeners of the URL
1124
+ change.
1125
+ Triggers the router level `didTransition` hook.
1126
+ For example, to notify google analytics when the route changes,
1127
+ you could use this hook. (Note: requires also including GA scripts, etc.)
1128
+ ```javascript
1129
+ import config from './config/environment';
1130
+ import EmberRouter from '@ember/routing/router';
1131
+ import { service } from '@ember/service';
1132
+ let Router = EmberRouter.extend({
1133
+ location: config.locationType,
1134
+ router: service(),
1135
+ didTransition: function() {
1136
+ this._super(...arguments);
1137
+ ga('send', 'pageview', {
1138
+ page: this.router.currentURL,
1139
+ title: this.router.currentRouteName,
1140
+ });
1141
+ }
1142
+ });
1143
+ ```
1144
+ @method didTransition
1145
+ @private
1146
+ @since 1.2.0
1147
+ */
1148
+ // Set with reopen to allow overriding via extend
1149
+
1150
+ /**
1151
+ Handles notifying any listeners of an impending URL
1152
+ change.
1153
+ Triggers the router level `willTransition` hook.
1154
+ @method willTransition
1155
+ @private
1156
+ @since 1.11.0
1157
+ */
1158
+ // Set with reopen to allow overriding via extend
1159
+
1160
+ /**
1161
+ Represents the current URL.
1162
+ @property url
1163
+ @type {String}
1164
+ @private
1165
+ */
1166
+ // Set with reopen to allow overriding via extend
1167
+ }
1168
+
1169
+ /*
1170
+ Helper function for iterating over routes in a set of routeInfos that are
1171
+ at or above the given origin route. Example: if `originRoute` === 'foo.bar'
1172
+ and the routeInfos given were for 'foo.bar.baz', then the given callback
1173
+ will be invoked with the routes for 'foo.bar', 'foo', and 'application'
1174
+ individually.
1175
+
1176
+ If the callback returns anything other than `true`, then iteration will stop.
1177
+
1178
+ @private
1179
+ @param {Route} originRoute
1180
+ @param {Array<RouteInfo>} routeInfos
1181
+ @param {Function} callback
1182
+ @return {Void}
1183
+ */
1184
+ function forEachRouteAbove(routeInfos, callback) {
1185
+ for (let i = routeInfos.length - 1; i >= 0; --i) {
1186
+ let routeInfo = routeInfos[i];
1187
+ (isDevelopingApp() && !(routeInfo) && assert('has routeInfo', routeInfo));
1188
+ let route = routeInfo.route;
1189
+
1190
+ // routeInfo.handler being `undefined` generally means either:
1191
+ //
1192
+ // 1. an error occurred during creation of the route in question
1193
+ // 2. the route is across an async boundary (e.g. within an engine)
1194
+ //
1195
+ // In both of these cases, we cannot invoke the callback on that specific
1196
+ // route, because it just doesn't exist...
1197
+ if (route === undefined) {
1198
+ continue;
1199
+ }
1200
+ if (callback(route, routeInfo) !== true) {
1201
+ return;
1202
+ }
1203
+ }
1204
+ }
1205
+
1206
+ // These get invoked when an action bubbles above ApplicationRoute
1207
+ // and are not meant to be overridable.
1208
+ let defaultActionHandlers = {
1209
+ willResolveModel(_routeInfos, transition, originRoute) {
1210
+ this._scheduleLoadingEvent(transition, originRoute);
1211
+ },
1212
+ // Attempt to find an appropriate error route or substate to enter.
1213
+ error(routeInfos, error, transition) {
1214
+ let router = this;
1215
+ let routeInfoWithError = routeInfos[routeInfos.length - 1];
1216
+ forEachRouteAbove(routeInfos, (route, routeInfo) => {
1217
+ // We don't check the leaf most routeInfo since that would
1218
+ // technically be below where we're at in the route hierarchy.
1219
+ if (routeInfo !== routeInfoWithError) {
1220
+ // Check for the existence of an 'error' route.
1221
+ let errorRouteName = findRouteStateName(route, 'error');
1222
+ if (errorRouteName) {
1223
+ router._markErrorAsHandled(error);
1224
+ router.intermediateTransitionTo(errorRouteName, error);
1225
+ return false;
1226
+ }
1227
+ }
1228
+
1229
+ // Check for an 'error' substate route
1230
+ let errorSubstateName = findRouteSubstateName(route, 'error');
1231
+ if (errorSubstateName) {
1232
+ router._markErrorAsHandled(error);
1233
+ router.intermediateTransitionTo(errorSubstateName, error);
1234
+ return false;
1235
+ }
1236
+ return true;
1237
+ });
1238
+ logError(error, `Error while processing route: ${transition.targetName}`);
1239
+ },
1240
+ // Attempt to find an appropriate loading route or substate to enter.
1241
+ loading(routeInfos, transition) {
1242
+ let router = this;
1243
+ let routeInfoWithSlowLoading = routeInfos[routeInfos.length - 1];
1244
+ forEachRouteAbove(routeInfos, (route, routeInfo) => {
1245
+ // We don't check the leaf most routeInfos since that would
1246
+ // technically be below where we're at in the route hierarchy.
1247
+ if (routeInfo !== routeInfoWithSlowLoading) {
1248
+ // Check for the existence of a 'loading' route.
1249
+ let loadingRouteName = findRouteStateName(route, 'loading');
1250
+ if (loadingRouteName) {
1251
+ router.intermediateTransitionTo(loadingRouteName);
1252
+ return false;
1253
+ }
1254
+ }
1255
+
1256
+ // Check for loading substate
1257
+ let loadingSubstateName = findRouteSubstateName(route, 'loading');
1258
+ if (loadingSubstateName) {
1259
+ router.intermediateTransitionTo(loadingSubstateName);
1260
+ return false;
1261
+ }
1262
+
1263
+ // Don't bubble above pivot route.
1264
+ return transition.pivotHandler !== route;
1265
+ });
1266
+ }
1267
+ };
1268
+ function logError(_error, initialMessage) {
1269
+ let errorArgs = [];
1270
+ let error;
1271
+ if (_error && typeof _error === 'object' && typeof _error.errorThrown === 'object') {
1272
+ error = _error.errorThrown;
1273
+ } else {
1274
+ error = _error;
1275
+ }
1276
+ if (initialMessage) {
1277
+ errorArgs.push(initialMessage);
1278
+ }
1279
+ if (error) {
1280
+ if (error.message) {
1281
+ errorArgs.push(error.message);
1282
+ }
1283
+ if (error.stack) {
1284
+ errorArgs.push(error.stack);
1285
+ }
1286
+ if (typeof error === 'string') {
1287
+ errorArgs.push(error);
1288
+ }
1289
+ }
1290
+ console.error(...errorArgs); //eslint-disable-line no-console
1291
+ }
1292
+
1293
+ /**
1294
+ Finds the name of the substate route if it exists for the given route. A
1295
+ substate route is of the form `route_state`, such as `foo_loading`.
1296
+
1297
+ @private
1298
+ @param {Route} route
1299
+ @param {String} state
1300
+ @return {String}
1301
+ */
1302
+ function findRouteSubstateName(route, state) {
1303
+ let owner = getOwner(route);
1304
+ (isDevelopingApp() && !(owner) && assert('Route is unexpectedly missing an owner', owner));
1305
+ let {
1306
+ routeName,
1307
+ fullRouteName,
1308
+ _router: router
1309
+ } = route;
1310
+ let substateName = `${routeName}_${state}`;
1311
+ let substateNameFull = `${fullRouteName}_${state}`;
1312
+ return routeHasBeenDefined(owner, router, substateName, substateNameFull) ? substateNameFull : '';
1313
+ }
1314
+
1315
+ /**
1316
+ Finds the name of the state route if it exists for the given route. A state
1317
+ route is of the form `route.state`, such as `foo.loading`. Properly Handles
1318
+ `application` named routes.
1319
+
1320
+ @private
1321
+ @param {Route} route
1322
+ @param {String} state
1323
+ @return {String}
1324
+ */
1325
+ function findRouteStateName(route, state) {
1326
+ let owner = getOwner(route);
1327
+ (isDevelopingApp() && !(owner) && assert('Route is unexpectedly missing an owner', owner));
1328
+ let {
1329
+ routeName,
1330
+ fullRouteName,
1331
+ _router: router
1332
+ } = route;
1333
+ let stateName = routeName === 'application' ? state : `${routeName}.${state}`;
1334
+ let stateNameFull = fullRouteName === 'application' ? state : `${fullRouteName}.${state}`;
1335
+ return routeHasBeenDefined(owner, router, stateName, stateNameFull) ? stateNameFull : '';
1336
+ }
1337
+
1338
+ /**
1339
+ Determines whether or not a route has been defined by checking that the route
1340
+ is in the Router's map and the owner has a registration for that route.
1341
+
1342
+ @private
1343
+ @param {Owner} owner
1344
+ @param {Router} router
1345
+ @param {String} localName
1346
+ @param {String} fullName
1347
+ @return {Boolean}
1348
+ */
1349
+ function routeHasBeenDefined(owner, router, localName, fullName) {
1350
+ let routerHasRoute = router.hasRoute(fullName);
1351
+ let ownerHasRoute = owner.factoryFor(`template:${localName}`) || owner.factoryFor(`route:${localName}`);
1352
+ return routerHasRoute && ownerHasRoute;
1353
+ }
1354
+ function triggerEvent(routeInfos, ignoreFailure, name, args) {
1355
+ if (!routeInfos) {
1356
+ if (ignoreFailure) {
1357
+ return;
1358
+ }
1359
+ // TODO: update?
1360
+ throw new Error(`Can't trigger action '${name}' because your app hasn't finished transitioning into its first route. To trigger an action on destination routes during a transition, you can call \`.send()\` on the \`Transition\` object passed to the \`model/beforeModel/afterModel\` hooks.`);
1361
+ }
1362
+ let eventWasHandled = false;
1363
+ let routeInfo, handler, actionHandler;
1364
+ for (let i = routeInfos.length - 1; i >= 0; i--) {
1365
+ routeInfo = routeInfos[i];
1366
+ (isDevelopingApp() && !(routeInfo) && assert('[BUG] Missing routeInfo', routeInfo));
1367
+ handler = routeInfo.route;
1368
+ actionHandler = handler && handler.actions && handler.actions[name];
1369
+ if (actionHandler) {
1370
+ if (actionHandler.apply(handler, args) === true) {
1371
+ eventWasHandled = true;
1372
+ } else {
1373
+ // Should only hit here if a non-bubbling error action is triggered on a route.
1374
+ if (name === 'error') {
1375
+ (isDevelopingApp() && !(handler) && assert('[BUG] Missing handler', handler));
1376
+ handler._router._markErrorAsHandled(args[0]);
1377
+ }
1378
+ return;
1379
+ }
1380
+ }
1381
+ }
1382
+ let defaultHandler = defaultActionHandlers[name];
1383
+ if (defaultHandler) {
1384
+ defaultHandler.call(this, routeInfos, ...args);
1385
+ return;
1386
+ }
1387
+ if (!eventWasHandled && !ignoreFailure) {
1388
+ throw new Error(`Nothing handled the action '${name}'. If you did handle the action, this error can be caused by returning true from an action handler in a controller, causing the action to bubble.`);
1389
+ }
1390
+ }
1391
+ function calculatePostTransitionState(emberRouter, leafRouteName, contexts) {
1392
+ let state = emberRouter._routerMicrolib.applyIntent(leafRouteName, contexts);
1393
+ let {
1394
+ routeInfos,
1395
+ params
1396
+ } = state;
1397
+ for (let routeInfo of routeInfos) {
1398
+ // If the routeInfo is not resolved, we serialize the context into params
1399
+ if (!routeInfo.isResolved) {
1400
+ params[routeInfo.name] = routeInfo.serialize(routeInfo.context);
1401
+ } else {
1402
+ params[routeInfo.name] = routeInfo.params;
1403
+ }
1404
+ }
1405
+ return state;
1406
+ }
1407
+ function updatePaths(router) {
1408
+ let infos = router._routerMicrolib.currentRouteInfos;
1409
+ if (infos.length === 0) {
1410
+ return;
1411
+ }
1412
+ let path = EmberRouter._routePath(infos);
1413
+ let info = infos[infos.length - 1];
1414
+ (isDevelopingApp() && !(info) && assert('expected info', info));
1415
+ let currentRouteName = info.name;
1416
+ let location = router.location;
1417
+ (isDevelopingApp() && !(typeof location !== 'string') && assert('expected location to not be a string', typeof location !== 'string'));
1418
+ let currentURL = location.getURL();
1419
+ set(router, 'currentPath', path);
1420
+ set(router, 'currentRouteName', currentRouteName);
1421
+ set(router, 'currentURL', currentURL);
1422
+ }
1423
+ function didBeginTransition(transition, router) {
1424
+ let routerState = new RouterState(router, router._routerMicrolib, transition[STATE_SYMBOL]);
1425
+ if (!router.currentState) {
1426
+ router.set('currentState', routerState);
1427
+ }
1428
+ router.set('targetState', routerState);
1429
+ transition.promise = transition.catch(error => {
1430
+ if (router._isErrorHandled(error)) {
1431
+ router._clearHandledError(error);
1432
+ } else {
1433
+ throw error;
1434
+ }
1435
+ }, 'Transition Error');
1436
+ }
1437
+ function forEachQueryParam(router, routeInfos, queryParams, callback) {
1438
+ let qpCache = router._queryParamsFor(routeInfos);
1439
+ for (let key in queryParams) {
1440
+ if (!Object.prototype.hasOwnProperty.call(queryParams, key)) {
1441
+ continue;
1442
+ }
1443
+ let value = queryParams[key];
1444
+ let qp = qpCache.map[key];
1445
+ callback(key, value, qp);
1446
+ }
1447
+ }
1448
+ EmberRouter.reopen({
1449
+ didTransition: defaultDidTransition,
1450
+ willTransition: defaultWillTransition,
1451
+ rootURL: '/',
1452
+ location: 'hash',
1453
+ // FIXME: Does this need to be overrideable via extend?
1454
+ url: computed(function () {
1455
+ let location = get(this, 'location');
1456
+ if (typeof location === 'string') {
1457
+ return undefined;
1458
+ }
1459
+ return location.getURL();
1460
+ })
1461
+ });
1462
+
1463
+ export { EmberRouter as default, triggerEvent };