UNPKG

96.2 kBJavaScriptView Raw
1/**
2 * State-based routing for AngularJS 1.x
3 * This bundle requires the ui-router-core.js bundle from the @uirouter/core package.
4 * @version v1.0.30
5 * @link https://ui-router.github.io
6 * @license MIT License, http://www.opensource.org/licenses/MIT
7 */
8(function (global, factory) {
9 typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('angular'), require('@uirouter/core')) :
10 typeof define === 'function' && define.amd ? define(['exports', 'angular', '@uirouter/core'], factory) :
11 (global = global || self, factory(global['@uirouter/angularjs'] = {}, global.angular, global['@uirouter/core']));
12}(this, (function (exports, ng_from_import, core) { 'use strict';
13
14 /** @publicapi @module ng1 */ /** */
15 /** @hidden */ var ng_from_global = angular;
16 /** @hidden */ var ng = ng_from_import && ng_from_import.module ? ng_from_import : ng_from_global;
17
18 /** @publicapi @module ng1 */ /** */
19 /** @internalapi */
20 function getNg1ViewConfigFactory() {
21 var templateFactory = null;
22 return function (path, view) {
23 templateFactory = templateFactory || core.services.$injector.get('$templateFactory');
24 return [new Ng1ViewConfig(path, view, templateFactory)];
25 };
26 }
27 /** @internalapi */
28 var hasAnyKey = function (keys, obj) { return keys.reduce(function (acc, key) { return acc || core.isDefined(obj[key]); }, false); };
29 /**
30 * This is a [[StateBuilder.builder]] function for angular1 `views`.
31 *
32 * When the [[StateBuilder]] builds a [[StateObject]] object from a raw [[StateDeclaration]], this builder
33 * handles the `views` property with logic specific to @uirouter/angularjs (ng1).
34 *
35 * If no `views: {}` property exists on the [[StateDeclaration]], then it creates the `views` object
36 * and applies the state-level configuration to a view named `$default`.
37 *
38 * @internalapi
39 */
40 function ng1ViewsBuilder(state) {
41 // Do not process root state
42 if (!state.parent)
43 return {};
44 var tplKeys = ['templateProvider', 'templateUrl', 'template', 'notify', 'async'], ctrlKeys = ['controller', 'controllerProvider', 'controllerAs', 'resolveAs'], compKeys = ['component', 'bindings', 'componentProvider'], nonCompKeys = tplKeys.concat(ctrlKeys), allViewKeys = compKeys.concat(nonCompKeys);
45 // Do not allow a state to have both state-level props and also a `views: {}` property.
46 // A state without a `views: {}` property can declare properties for the `$default` view as properties of the state.
47 // However, the `$default` approach should not be mixed with a separate `views: ` block.
48 if (core.isDefined(state.views) && hasAnyKey(allViewKeys, state)) {
49 throw new Error("State '" + state.name + "' has a 'views' object. " +
50 "It cannot also have \"view properties\" at the state level. " +
51 "Move the following properties into a view (in the 'views' object): " +
52 (" " + allViewKeys.filter(function (key) { return core.isDefined(state[key]); }).join(', ')));
53 }
54 var views = {}, viewsObject = state.views || { $default: core.pick(state, allViewKeys) };
55 core.forEach(viewsObject, function (config, name) {
56 // Account for views: { "": { template... } }
57 name = name || '$default';
58 // Account for views: { header: "headerComponent" }
59 if (core.isString(config))
60 config = { component: config };
61 // Make a shallow copy of the config object
62 config = core.extend({}, config);
63 // Do not allow a view to mix props for component-style view with props for template/controller-style view
64 if (hasAnyKey(compKeys, config) && hasAnyKey(nonCompKeys, config)) {
65 throw new Error("Cannot combine: " + compKeys.join('|') + " with: " + nonCompKeys.join('|') + " in stateview: '" + name + "@" + state.name + "'");
66 }
67 config.resolveAs = config.resolveAs || '$resolve';
68 config.$type = 'ng1';
69 config.$context = state;
70 config.$name = name;
71 var normalized = core.ViewService.normalizeUIViewTarget(config.$context, config.$name);
72 config.$uiViewName = normalized.uiViewName;
73 config.$uiViewContextAnchor = normalized.uiViewContextAnchor;
74 views[name] = config;
75 });
76 return views;
77 }
78 /** @hidden */
79 var id = 0;
80 /** @internalapi */
81 var Ng1ViewConfig = /** @class */ (function () {
82 function Ng1ViewConfig(path, viewDecl, factory) {
83 var _this = this;
84 this.path = path;
85 this.viewDecl = viewDecl;
86 this.factory = factory;
87 this.$id = id++;
88 this.loaded = false;
89 this.getTemplate = function (uiView, context) {
90 return _this.component
91 ? _this.factory.makeComponentTemplate(uiView, context, _this.component, _this.viewDecl.bindings)
92 : _this.template;
93 };
94 }
95 Ng1ViewConfig.prototype.load = function () {
96 var _this = this;
97 var $q = core.services.$q;
98 var context = new core.ResolveContext(this.path);
99 var params = this.path.reduce(function (acc, node) { return core.extend(acc, node.paramValues); }, {});
100 var promises = {
101 template: $q.when(this.factory.fromConfig(this.viewDecl, params, context)),
102 controller: $q.when(this.getController(context)),
103 };
104 return $q.all(promises).then(function (results) {
105 core.trace.traceViewServiceEvent('Loaded', _this);
106 _this.controller = results.controller;
107 core.extend(_this, results.template); // Either { template: "tpl" } or { component: "cmpName" }
108 return _this;
109 });
110 };
111 /**
112 * Gets the controller for a view configuration.
113 *
114 * @returns {Function|Promise.<Function>} Returns a controller, or a promise that resolves to a controller.
115 */
116 Ng1ViewConfig.prototype.getController = function (context) {
117 var provider = this.viewDecl.controllerProvider;
118 if (!core.isInjectable(provider))
119 return this.viewDecl.controller;
120 var deps = core.services.$injector.annotate(provider);
121 var providerFn = core.isArray(provider) ? core.tail(provider) : provider;
122 var resolvable = new core.Resolvable('', providerFn, deps);
123 return resolvable.get(context);
124 };
125 return Ng1ViewConfig;
126 }());
127
128 /** @publicapi @module view */ /** */
129 /**
130 * Service which manages loading of templates from a ViewConfig.
131 */
132 var TemplateFactory = /** @class */ (function () {
133 function TemplateFactory() {
134 var _this = this;
135 /** @hidden */ this._useHttp = ng.version.minor < 3;
136 /** @hidden */ this.$get = [
137 '$http',
138 '$templateCache',
139 '$injector',
140 function ($http, $templateCache, $injector) {
141 _this.$templateRequest = $injector.has && $injector.has('$templateRequest') && $injector.get('$templateRequest');
142 _this.$http = $http;
143 _this.$templateCache = $templateCache;
144 return _this;
145 },
146 ];
147 }
148 /** @hidden */
149 TemplateFactory.prototype.useHttpService = function (value) {
150 this._useHttp = value;
151 };
152 /**
153 * Creates a template from a configuration object.
154 *
155 * @param config Configuration object for which to load a template.
156 * The following properties are search in the specified order, and the first one
157 * that is defined is used to create the template:
158 *
159 * @param params Parameters to pass to the template function.
160 * @param context The resolve context associated with the template's view
161 *
162 * @return {string|object} The template html as a string, or a promise for
163 * that string,or `null` if no template is configured.
164 */
165 TemplateFactory.prototype.fromConfig = function (config, params, context) {
166 var defaultTemplate = '<ui-view></ui-view>';
167 var asTemplate = function (result) { return core.services.$q.when(result).then(function (str) { return ({ template: str }); }); };
168 var asComponent = function (result) { return core.services.$q.when(result).then(function (str) { return ({ component: str }); }); };
169 return core.isDefined(config.template)
170 ? asTemplate(this.fromString(config.template, params))
171 : core.isDefined(config.templateUrl)
172 ? asTemplate(this.fromUrl(config.templateUrl, params))
173 : core.isDefined(config.templateProvider)
174 ? asTemplate(this.fromProvider(config.templateProvider, params, context))
175 : core.isDefined(config.component)
176 ? asComponent(config.component)
177 : core.isDefined(config.componentProvider)
178 ? asComponent(this.fromComponentProvider(config.componentProvider, params, context))
179 : asTemplate(defaultTemplate);
180 };
181 /**
182 * Creates a template from a string or a function returning a string.
183 *
184 * @param template html template as a string or function that returns an html template as a string.
185 * @param params Parameters to pass to the template function.
186 *
187 * @return {string|object} The template html as a string, or a promise for that
188 * string.
189 */
190 TemplateFactory.prototype.fromString = function (template, params) {
191 return core.isFunction(template) ? template(params) : template;
192 };
193 /**
194 * Loads a template from the a URL via `$http` and `$templateCache`.
195 *
196 * @param {string|Function} url url of the template to load, or a function
197 * that returns a url.
198 * @param {Object} params Parameters to pass to the url function.
199 * @return {string|Promise.<string>} The template html as a string, or a promise
200 * for that string.
201 */
202 TemplateFactory.prototype.fromUrl = function (url, params) {
203 if (core.isFunction(url))
204 url = url(params);
205 if (url == null)
206 return null;
207 if (this._useHttp) {
208 return this.$http
209 .get(url, { cache: this.$templateCache, headers: { Accept: 'text/html' } })
210 .then(function (response) {
211 return response.data;
212 });
213 }
214 return this.$templateRequest(url);
215 };
216 /**
217 * Creates a template by invoking an injectable provider function.
218 *
219 * @param provider Function to invoke via `locals`
220 * @param {Function} injectFn a function used to invoke the template provider
221 * @return {string|Promise.<string>} The template html as a string, or a promise
222 * for that string.
223 */
224 TemplateFactory.prototype.fromProvider = function (provider, params, context) {
225 var deps = core.services.$injector.annotate(provider);
226 var providerFn = core.isArray(provider) ? core.tail(provider) : provider;
227 var resolvable = new core.Resolvable('', providerFn, deps);
228 return resolvable.get(context);
229 };
230 /**
231 * Creates a component's template by invoking an injectable provider function.
232 *
233 * @param provider Function to invoke via `locals`
234 * @param {Function} injectFn a function used to invoke the template provider
235 * @return {string} The template html as a string: "<component-name input1='::$resolve.foo'></component-name>".
236 */
237 TemplateFactory.prototype.fromComponentProvider = function (provider, params, context) {
238 var deps = core.services.$injector.annotate(provider);
239 var providerFn = core.isArray(provider) ? core.tail(provider) : provider;
240 var resolvable = new core.Resolvable('', providerFn, deps);
241 return resolvable.get(context);
242 };
243 /**
244 * Creates a template from a component's name
245 *
246 * This implements route-to-component.
247 * It works by retrieving the component (directive) metadata from the injector.
248 * It analyses the component's bindings, then constructs a template that instantiates the component.
249 * The template wires input and output bindings to resolves or from the parent component.
250 *
251 * @param uiView {object} The parent ui-view (for binding outputs to callbacks)
252 * @param context The ResolveContext (for binding outputs to callbacks returned from resolves)
253 * @param component {string} Component's name in camel case.
254 * @param bindings An object defining the component's bindings: {foo: '<'}
255 * @return {string} The template as a string: "<component-name input1='::$resolve.foo'></component-name>".
256 */
257 TemplateFactory.prototype.makeComponentTemplate = function (uiView, context, component, bindings) {
258 bindings = bindings || {};
259 // Bind once prefix
260 var prefix = ng.version.minor >= 3 ? '::' : '';
261 // Convert to kebob name. Add x- prefix if the string starts with `x-` or `data-`
262 var kebob = function (camelCase) {
263 var kebobed = core.kebobString(camelCase);
264 return /^(x|data)-/.exec(kebobed) ? "x-" + kebobed : kebobed;
265 };
266 var attributeTpl = function (input) {
267 var name = input.name, type = input.type;
268 var attrName = kebob(name);
269 // If the ui-view has an attribute which matches a binding on the routed component
270 // then pass that attribute through to the routed component template.
271 // Prefer ui-view wired mappings to resolve data, unless the resolve was explicitly bound using `bindings:`
272 if (uiView.attr(attrName) && !bindings[name])
273 return attrName + "='" + uiView.attr(attrName) + "'";
274 var resolveName = bindings[name] || name;
275 // Pre-evaluate the expression for "@" bindings by enclosing in {{ }}
276 // some-attr="{{ ::$resolve.someResolveName }}"
277 if (type === '@')
278 return attrName + "='{{" + prefix + "$resolve." + resolveName + "}}'";
279 // Wire "&" callbacks to resolves that return a callback function
280 // Get the result of the resolve (should be a function) and annotate it to get its arguments.
281 // some-attr="$resolve.someResolveResultName(foo, bar)"
282 if (type === '&') {
283 var res = context.getResolvable(resolveName);
284 var fn = res && res.data;
285 var args = (fn && core.services.$injector.annotate(fn)) || [];
286 // account for array style injection, i.e., ['foo', function(foo) {}]
287 var arrayIdxStr = core.isArray(fn) ? "[" + (fn.length - 1) + "]" : '';
288 return attrName + "='$resolve." + resolveName + arrayIdxStr + "(" + args.join(',') + ")'";
289 }
290 // some-attr="::$resolve.someResolveName"
291 return attrName + "='" + prefix + "$resolve." + resolveName + "'";
292 };
293 var attrs = getComponentBindings(component).map(attributeTpl).join(' ');
294 var kebobName = kebob(component);
295 return "<" + kebobName + " " + attrs + "></" + kebobName + ">";
296 };
297 return TemplateFactory;
298 }());
299 // Gets all the directive(s)' inputs ('@', '=', and '<') and outputs ('&')
300 function getComponentBindings(name) {
301 var cmpDefs = core.services.$injector.get(name + 'Directive'); // could be multiple
302 if (!cmpDefs || !cmpDefs.length)
303 throw new Error("Unable to find component named '" + name + "'");
304 return cmpDefs.map(getBindings).reduce(core.unnestR, []);
305 }
306 // Given a directive definition, find its object input attributes
307 // Use different properties, depending on the type of directive (component, bindToController, normal)
308 var getBindings = function (def) {
309 if (core.isObject(def.bindToController))
310 return scopeBindings(def.bindToController);
311 return scopeBindings(def.scope);
312 };
313 // for ng 1.2 style, process the scope: { input: "=foo" }
314 // for ng 1.3 through ng 1.5, process the component's bindToController: { input: "=foo" } object
315 var scopeBindings = function (bindingsObj) {
316 return Object.keys(bindingsObj || {})
317 // [ 'input', [ '=foo', '=', 'foo' ] ]
318 .map(function (key) { return [key, /^([=<@&])[?]?(.*)/.exec(bindingsObj[key])]; })
319 // skip malformed values
320 .filter(function (tuple) { return core.isDefined(tuple) && core.isArray(tuple[1]); })
321 // { name: ('foo' || 'input'), type: '=' }
322 .map(function (tuple) { return ({ name: tuple[1][2] || tuple[0], type: tuple[1][1] }); });
323 };
324
325 /** @publicapi @module ng1 */ /** */
326 /**
327 * The Angular 1 `StateProvider`
328 *
329 * The `$stateProvider` works similar to Angular's v1 router, but it focuses purely
330 * on state.
331 *
332 * A state corresponds to a "place" in the application in terms of the overall UI and
333 * navigation. A state describes (via the controller / template / view properties) what
334 * the UI looks like and does at that place.
335 *
336 * States often have things in common, and the primary way of factoring out these
337 * commonalities in this model is via the state hierarchy, i.e. parent/child states aka
338 * nested states.
339 *
340 * The `$stateProvider` provides interfaces to declare these states for your app.
341 */
342 var StateProvider = /** @class */ (function () {
343 function StateProvider(stateRegistry, stateService) {
344 this.stateRegistry = stateRegistry;
345 this.stateService = stateService;
346 core.createProxyFunctions(core.val(StateProvider.prototype), this, core.val(this));
347 }
348 /**
349 * Decorates states when they are registered
350 *
351 * Allows you to extend (carefully) or override (at your own peril) the
352 * `stateBuilder` object used internally by [[StateRegistry]].
353 * This can be used to add custom functionality to ui-router,
354 * for example inferring templateUrl based on the state name.
355 *
356 * When passing only a name, it returns the current (original or decorated) builder
357 * function that matches `name`.
358 *
359 * The builder functions that can be decorated are listed below. Though not all
360 * necessarily have a good use case for decoration, that is up to you to decide.
361 *
362 * In addition, users can attach custom decorators, which will generate new
363 * properties within the state's internal definition. There is currently no clear
364 * use-case for this beyond accessing internal states (i.e. $state.$current),
365 * however, expect this to become increasingly relevant as we introduce additional
366 * meta-programming features.
367 *
368 * **Warning**: Decorators should not be interdependent because the order of
369 * execution of the builder functions in non-deterministic. Builder functions
370 * should only be dependent on the state definition object and super function.
371 *
372 *
373 * Existing builder functions and current return values:
374 *
375 * - **parent** `{object}` - returns the parent state object.
376 * - **data** `{object}` - returns state data, including any inherited data that is not
377 * overridden by own values (if any).
378 * - **url** `{object}` - returns a {@link ui.router.util.type:UrlMatcher UrlMatcher}
379 * or `null`.
380 * - **navigable** `{object}` - returns closest ancestor state that has a URL (aka is
381 * navigable).
382 * - **params** `{object}` - returns an array of state params that are ensured to
383 * be a super-set of parent's params.
384 * - **views** `{object}` - returns a views object where each key is an absolute view
385 * name (i.e. "viewName@stateName") and each value is the config object
386 * (template, controller) for the view. Even when you don't use the views object
387 * explicitly on a state config, one is still created for you internally.
388 * So by decorating this builder function you have access to decorating template
389 * and controller properties.
390 * - **ownParams** `{object}` - returns an array of params that belong to the state,
391 * not including any params defined by ancestor states.
392 * - **path** `{string}` - returns the full path from the root down to this state.
393 * Needed for state activation.
394 * - **includes** `{object}` - returns an object that includes every state that
395 * would pass a `$state.includes()` test.
396 *
397 * #### Example:
398 * Override the internal 'views' builder with a function that takes the state
399 * definition, and a reference to the internal function being overridden:
400 * ```js
401 * $stateProvider.decorator('views', function (state, parent) {
402 * let result = {},
403 * views = parent(state);
404 *
405 * angular.forEach(views, function (config, name) {
406 * let autoName = (state.name + '.' + name).replace('.', '/');
407 * config.templateUrl = config.templateUrl || '/partials/' + autoName + '.html';
408 * result[name] = config;
409 * });
410 * return result;
411 * });
412 *
413 * $stateProvider.state('home', {
414 * views: {
415 * 'contact.list': { controller: 'ListController' },
416 * 'contact.item': { controller: 'ItemController' }
417 * }
418 * });
419 * ```
420 *
421 *
422 * ```js
423 * // Auto-populates list and item views with /partials/home/contact/list.html,
424 * // and /partials/home/contact/item.html, respectively.
425 * $state.go('home');
426 * ```
427 *
428 * @param {string} name The name of the builder function to decorate.
429 * @param {object} func A function that is responsible for decorating the original
430 * builder function. The function receives two parameters:
431 *
432 * - `{object}` - state - The state config object.
433 * - `{object}` - super - The original builder function.
434 *
435 * @return {object} $stateProvider - $stateProvider instance
436 */
437 StateProvider.prototype.decorator = function (name, func) {
438 return this.stateRegistry.decorator(name, func) || this;
439 };
440 StateProvider.prototype.state = function (name, definition) {
441 if (core.isObject(name)) {
442 definition = name;
443 }
444 else {
445 definition.name = name;
446 }
447 this.stateRegistry.register(definition);
448 return this;
449 };
450 /**
451 * Registers an invalid state handler
452 *
453 * This is a passthrough to [[StateService.onInvalid]] for ng1.
454 */
455 StateProvider.prototype.onInvalid = function (callback) {
456 return this.stateService.onInvalid(callback);
457 };
458 return StateProvider;
459 }());
460
461 /** @publicapi @module ng1 */ /** */
462 /**
463 * This is a [[StateBuilder.builder]] function for angular1 `onEnter`, `onExit`,
464 * `onRetain` callback hooks on a [[Ng1StateDeclaration]].
465 *
466 * When the [[StateBuilder]] builds a [[StateObject]] object from a raw [[StateDeclaration]], this builder
467 * ensures that those hooks are injectable for @uirouter/angularjs (ng1).
468 *
469 * @internalapi
470 */
471 var getStateHookBuilder = function (hookName) {
472 return function stateHookBuilder(stateObject) {
473 var hook = stateObject[hookName];
474 var pathname = hookName === 'onExit' ? 'from' : 'to';
475 function decoratedNg1Hook(trans, state) {
476 var resolveContext = new core.ResolveContext(trans.treeChanges(pathname));
477 var subContext = resolveContext.subContext(state.$$state());
478 var locals = core.extend(getLocals(subContext), { $state$: state, $transition$: trans });
479 return core.services.$injector.invoke(hook, this, locals);
480 }
481 return hook ? decoratedNg1Hook : undefined;
482 };
483 };
484
485 /** @publicapi @module ng1 */ /** */
486 /**
487 * Implements UI-Router LocationServices and LocationConfig using Angular 1's $location service
488 * @internalapi
489 */
490 var Ng1LocationServices = /** @class */ (function () {
491 function Ng1LocationServices($locationProvider) {
492 // .onChange() registry
493 this._urlListeners = [];
494 this.$locationProvider = $locationProvider;
495 var _lp = core.val($locationProvider);
496 core.createProxyFunctions(_lp, this, _lp, ['hashPrefix']);
497 }
498 /**
499 * Applys ng1-specific path parameter encoding
500 *
501 * The Angular 1 `$location` service is a bit weird.
502 * It doesn't allow slashes to be encoded/decoded bi-directionally.
503 *
504 * See the writeup at https://github.com/angular-ui/ui-router/issues/2598
505 *
506 * This code patches the `path` parameter type so it encoded/decodes slashes as ~2F
507 *
508 * @param router
509 */
510 Ng1LocationServices.monkeyPatchPathParameterType = function (router) {
511 var pathType = router.urlMatcherFactory.type('path');
512 pathType.encode = function (x) {
513 return x != null ? x.toString().replace(/(~|\/)/g, function (m) { return ({ '~': '~~', '/': '~2F' }[m]); }) : x;
514 };
515 pathType.decode = function (x) {
516 return x != null ? x.toString().replace(/(~~|~2F)/g, function (m) { return ({ '~~': '~', '~2F': '/' }[m]); }) : x;
517 };
518 };
519 // eslint-disable-next-line @typescript-eslint/no-empty-function
520 Ng1LocationServices.prototype.dispose = function () { };
521 Ng1LocationServices.prototype.onChange = function (callback) {
522 var _this = this;
523 this._urlListeners.push(callback);
524 return function () { return core.removeFrom(_this._urlListeners)(callback); };
525 };
526 Ng1LocationServices.prototype.html5Mode = function () {
527 var html5Mode = this.$locationProvider.html5Mode();
528 html5Mode = core.isObject(html5Mode) ? html5Mode.enabled : html5Mode;
529 return html5Mode && this.$sniffer.history;
530 };
531 Ng1LocationServices.prototype.baseHref = function () {
532 return this._baseHref || (this._baseHref = this.$browser.baseHref() || this.$window.location.pathname);
533 };
534 Ng1LocationServices.prototype.url = function (newUrl, replace, state) {
535 if (replace === void 0) { replace = false; }
536 if (core.isDefined(newUrl))
537 this.$location.url(newUrl);
538 if (replace)
539 this.$location.replace();
540 if (state)
541 this.$location.state(state);
542 return this.$location.url();
543 };
544 Ng1LocationServices.prototype._runtimeServices = function ($rootScope, $location, $sniffer, $browser, $window) {
545 var _this = this;
546 this.$location = $location;
547 this.$sniffer = $sniffer;
548 this.$browser = $browser;
549 this.$window = $window;
550 // Bind $locationChangeSuccess to the listeners registered in LocationService.onChange
551 $rootScope.$on('$locationChangeSuccess', function (evt) { return _this._urlListeners.forEach(function (fn) { return fn(evt); }); });
552 var _loc = core.val($location);
553 // Bind these LocationService functions to $location
554 core.createProxyFunctions(_loc, this, _loc, ['replace', 'path', 'search', 'hash']);
555 // Bind these LocationConfig functions to $location
556 core.createProxyFunctions(_loc, this, _loc, ['port', 'protocol', 'host']);
557 };
558 return Ng1LocationServices;
559 }());
560
561 /** @publicapi @module url */ /** */
562 /**
563 * Manages rules for client-side URL
564 *
565 * ### Deprecation warning:
566 * This class is now considered to be an internal API
567 * Use the [[UrlService]] instead.
568 * For configuring URL rules, use the [[UrlRulesApi]] which can be found as [[UrlService.rules]].
569 *
570 * This class manages the router rules for what to do when the URL changes.
571 *
572 * This provider remains for backwards compatibility.
573 *
574 * @internalapi
575 * @deprecated
576 */
577 var UrlRouterProvider = /** @class */ (function () {
578 /** @hidden */
579 function UrlRouterProvider(/** @hidden */ router) {
580 this.router = router;
581 }
582 UrlRouterProvider.injectableHandler = function (router, handler) {
583 return function (match) { return core.services.$injector.invoke(handler, null, { $match: match, $stateParams: router.globals.params }); };
584 };
585 /** @hidden */
586 UrlRouterProvider.prototype.$get = function () {
587 var urlService = this.router.urlService;
588 this.router.urlRouter.update(true);
589 if (!urlService.interceptDeferred)
590 urlService.listen();
591 return this.router.urlRouter;
592 };
593 /**
594 * Registers a url handler function.
595 *
596 * Registers a low level url handler (a `rule`).
597 * A rule detects specific URL patterns and returns a redirect, or performs some action.
598 *
599 * If a rule returns a string, the URL is replaced with the string, and all rules are fired again.
600 *
601 * #### Example:
602 * ```js
603 * var app = angular.module('app', ['ui.router.router']);
604 *
605 * app.config(function ($urlRouterProvider) {
606 * // Here's an example of how you might allow case insensitive urls
607 * $urlRouterProvider.rule(function ($injector, $location) {
608 * var path = $location.path(),
609 * normalized = path.toLowerCase();
610 *
611 * if (path !== normalized) {
612 * return normalized;
613 * }
614 * });
615 * });
616 * ```
617 *
618 * @param ruleFn
619 * Handler function that takes `$injector` and `$location` services as arguments.
620 * You can use them to detect a url and return a different url as a string.
621 *
622 * @return [[UrlRouterProvider]] (`this`)
623 */
624 UrlRouterProvider.prototype.rule = function (ruleFn) {
625 var _this = this;
626 if (!core.isFunction(ruleFn))
627 throw new Error("'rule' must be a function");
628 var match = function () { return ruleFn(core.services.$injector, _this.router.locationService); };
629 var rule = new core.BaseUrlRule(match, core.identity);
630 this.router.urlService.rules.rule(rule);
631 return this;
632 };
633 /**
634 * Defines the path or behavior to use when no url can be matched.
635 *
636 * #### Example:
637 * ```js
638 * var app = angular.module('app', ['ui.router.router']);
639 *
640 * app.config(function ($urlRouterProvider) {
641 * // if the path doesn't match any of the urls you configured
642 * // otherwise will take care of routing the user to the
643 * // specified url
644 * $urlRouterProvider.otherwise('/index');
645 *
646 * // Example of using function rule as param
647 * $urlRouterProvider.otherwise(function ($injector, $location) {
648 * return '/a/valid/url';
649 * });
650 * });
651 * ```
652 *
653 * @param rule
654 * The url path you want to redirect to or a function rule that returns the url path or performs a `$state.go()`.
655 * The function version is passed two params: `$injector` and `$location` services, and should return a url string.
656 *
657 * @return {object} `$urlRouterProvider` - `$urlRouterProvider` instance
658 */
659 UrlRouterProvider.prototype.otherwise = function (rule) {
660 var _this = this;
661 var urlRules = this.router.urlService.rules;
662 if (core.isString(rule)) {
663 urlRules.otherwise(rule);
664 }
665 else if (core.isFunction(rule)) {
666 urlRules.otherwise(function () { return rule(core.services.$injector, _this.router.locationService); });
667 }
668 else {
669 throw new Error("'rule' must be a string or function");
670 }
671 return this;
672 };
673 /**
674 * Registers a handler for a given url matching.
675 *
676 * If the handler is a string, it is
677 * treated as a redirect, and is interpolated according to the syntax of match
678 * (i.e. like `String.replace()` for `RegExp`, or like a `UrlMatcher` pattern otherwise).
679 *
680 * If the handler is a function, it is injectable.
681 * It gets invoked if `$location` matches.
682 * You have the option of inject the match object as `$match`.
683 *
684 * The handler can return
685 *
686 * - **falsy** to indicate that the rule didn't match after all, then `$urlRouter`
687 * will continue trying to find another one that matches.
688 * - **string** which is treated as a redirect and passed to `$location.url()`
689 * - **void** or any **truthy** value tells `$urlRouter` that the url was handled.
690 *
691 * #### Example:
692 * ```js
693 * var app = angular.module('app', ['ui.router.router']);
694 *
695 * app.config(function ($urlRouterProvider) {
696 * $urlRouterProvider.when($state.url, function ($match, $stateParams) {
697 * if ($state.$current.navigable !== state ||
698 * !equalForKeys($match, $stateParams) {
699 * $state.transitionTo(state, $match, false);
700 * }
701 * });
702 * });
703 * ```
704 *
705 * @param what A pattern string to match, compiled as a [[UrlMatcher]].
706 * @param handler The path (or function that returns a path) that you want to redirect your user to.
707 * @param ruleCallback [optional] A callback that receives the `rule` registered with [[UrlMatcher.rule]]
708 *
709 * Note: the handler may also invoke arbitrary code, such as `$state.go()`
710 */
711 UrlRouterProvider.prototype.when = function (what, handler) {
712 if (core.isArray(handler) || core.isFunction(handler)) {
713 handler = UrlRouterProvider.injectableHandler(this.router, handler);
714 }
715 this.router.urlService.rules.when(what, handler);
716 return this;
717 };
718 /**
719 * Disables monitoring of the URL.
720 *
721 * Call this method before UI-Router has bootstrapped.
722 * It will stop UI-Router from performing the initial url sync.
723 *
724 * This can be useful to perform some asynchronous initialization before the router starts.
725 * Once the initialization is complete, call [[listen]] to tell UI-Router to start watching and synchronizing the URL.
726 *
727 * #### Example:
728 * ```js
729 * var app = angular.module('app', ['ui.router']);
730 *
731 * app.config(function ($urlRouterProvider) {
732 * // Prevent $urlRouter from automatically intercepting URL changes;
733 * $urlRouterProvider.deferIntercept();
734 * })
735 *
736 * app.run(function (MyService, $urlRouter, $http) {
737 * $http.get("/stuff").then(function(resp) {
738 * MyService.doStuff(resp.data);
739 * $urlRouter.listen();
740 * $urlRouter.sync();
741 * });
742 * });
743 * ```
744 *
745 * @param defer Indicates whether to defer location change interception.
746 * Passing no parameter is equivalent to `true`.
747 */
748 UrlRouterProvider.prototype.deferIntercept = function (defer) {
749 this.router.urlService.deferIntercept(defer);
750 };
751 return UrlRouterProvider;
752 }());
753
754 /* eslint-disable @typescript-eslint/no-empty-function */
755 ng.module('ui.router.angular1', []);
756 var mod_init = ng.module('ui.router.init', ['ng']);
757 var mod_util = ng.module('ui.router.util', ['ui.router.init']);
758 var mod_rtr = ng.module('ui.router.router', ['ui.router.util']);
759 var mod_state = ng.module('ui.router.state', ['ui.router.router', 'ui.router.util', 'ui.router.angular1']);
760 var mod_main = ng.module('ui.router', ['ui.router.init', 'ui.router.state', 'ui.router.angular1']);
761 var mod_cmpt = ng.module('ui.router.compat', ['ui.router']);
762 var router = null;
763 $uiRouterProvider.$inject = ['$locationProvider'];
764 /** This angular 1 provider instantiates a Router and exposes its services via the angular injector */
765 function $uiRouterProvider($locationProvider) {
766 // Create a new instance of the Router when the $uiRouterProvider is initialized
767 router = this.router = new core.UIRouter();
768 router.stateProvider = new StateProvider(router.stateRegistry, router.stateService);
769 // Apply ng1 specific StateBuilder code for `views`, `resolve`, and `onExit/Retain/Enter` properties
770 router.stateRegistry.decorator('views', ng1ViewsBuilder);
771 router.stateRegistry.decorator('onExit', getStateHookBuilder('onExit'));
772 router.stateRegistry.decorator('onRetain', getStateHookBuilder('onRetain'));
773 router.stateRegistry.decorator('onEnter', getStateHookBuilder('onEnter'));
774 router.viewService._pluginapi._viewConfigFactory('ng1', getNg1ViewConfigFactory());
775 // Disable decoding of params by UrlMatcherFactory because $location already handles this
776 router.urlService.config._decodeParams = false;
777 var ng1LocationService = (router.locationService = router.locationConfig = new Ng1LocationServices($locationProvider));
778 Ng1LocationServices.monkeyPatchPathParameterType(router);
779 // backwards compat: also expose router instance as $uiRouterProvider.router
780 router['router'] = router;
781 router['$get'] = $get;
782 $get.$inject = ['$location', '$browser', '$window', '$sniffer', '$rootScope', '$http', '$templateCache'];
783 function $get($location, $browser, $window, $sniffer, $rootScope, $http, $templateCache) {
784 ng1LocationService._runtimeServices($rootScope, $location, $sniffer, $browser, $window);
785 delete router['router'];
786 delete router['$get'];
787 return router;
788 }
789 return router;
790 }
791 var getProviderFor = function (serviceName) { return [
792 '$uiRouterProvider',
793 function ($urp) {
794 var service = $urp.router[serviceName];
795 service['$get'] = function () { return service; };
796 return service;
797 },
798 ]; };
799 // This effectively calls $get() on `$uiRouterProvider` to trigger init (when ng enters runtime)
800 runBlock.$inject = ['$injector', '$q', '$uiRouter'];
801 function runBlock($injector, $q, $uiRouter) {
802 core.services.$injector = $injector;
803 core.services.$q = $q;
804 // https://github.com/angular-ui/ui-router/issues/3678
805 if (!Object.prototype.hasOwnProperty.call($injector, 'strictDi')) {
806 try {
807 $injector.invoke(function (checkStrictDi) { });
808 }
809 catch (error) {
810 $injector.strictDi = !!/strict mode/.exec(error && error.toString());
811 }
812 }
813 // The $injector is now available.
814 // Find any resolvables that had dependency annotation deferred
815 $uiRouter.stateRegistry
816 .get()
817 .map(function (x) { return x.$$state().resolvables; })
818 .reduce(core.unnestR, [])
819 .filter(function (x) { return x.deps === 'deferred'; })
820 .forEach(function (resolvable) { return (resolvable.deps = $injector.annotate(resolvable.resolveFn, $injector.strictDi)); });
821 }
822 // $urlRouter service and $urlRouterProvider
823 var getUrlRouterProvider = function (uiRouter) { return (uiRouter.urlRouterProvider = new UrlRouterProvider(uiRouter)); };
824 // $state service and $stateProvider
825 // $urlRouter service and $urlRouterProvider
826 var getStateProvider = function () { return core.extend(router.stateProvider, { $get: function () { return router.stateService; } }); };
827 watchDigests.$inject = ['$rootScope'];
828 function watchDigests($rootScope) {
829 $rootScope.$watch(function () {
830 core.trace.approximateDigests++;
831 });
832 }
833 mod_init.provider('$uiRouter', $uiRouterProvider);
834 mod_rtr.provider('$urlRouter', ['$uiRouterProvider', getUrlRouterProvider]);
835 mod_util.provider('$urlService', getProviderFor('urlService'));
836 mod_util.provider('$urlMatcherFactory', ['$uiRouterProvider', function () { return router.urlMatcherFactory; }]);
837 mod_util.provider('$templateFactory', function () { return new TemplateFactory(); });
838 mod_state.provider('$stateRegistry', getProviderFor('stateRegistry'));
839 mod_state.provider('$uiRouterGlobals', getProviderFor('globals'));
840 mod_state.provider('$transitions', getProviderFor('transitionService'));
841 mod_state.provider('$state', ['$uiRouterProvider', getStateProvider]);
842 mod_state.factory('$stateParams', ['$uiRouter', function ($uiRouter) { return $uiRouter.globals.params; }]);
843 mod_main.factory('$view', function () { return router.viewService; });
844 mod_main.service('$trace', function () { return core.trace; });
845 mod_main.run(watchDigests);
846 mod_util.run(['$urlMatcherFactory', function ($urlMatcherFactory) { }]);
847 mod_state.run(['$state', function ($state) { }]);
848 mod_rtr.run(['$urlRouter', function ($urlRouter) { }]);
849 mod_init.run(runBlock);
850 /** @hidden TODO: find a place to move this */
851 var getLocals = function (ctx) {
852 var tokens = ctx.getTokens().filter(core.isString);
853 var tuples = tokens.map(function (key) {
854 var resolvable = ctx.getResolvable(key);
855 var waitPolicy = ctx.getPolicy(resolvable).async;
856 return [key, waitPolicy === 'NOWAIT' ? resolvable.promise : resolvable.data];
857 });
858 return tuples.reduce(core.applyPairs, {});
859 };
860
861 /* eslint-disable @typescript-eslint/no-empty-interface */
862 /** @hidden */
863 function parseStateRef(ref) {
864 var paramsOnly = ref.match(/^\s*({[^}]*})\s*$/);
865 if (paramsOnly)
866 ref = '(' + paramsOnly[1] + ')';
867 var parsed = ref.replace(/\n/g, ' ').match(/^\s*([^(]*?)\s*(\((.*)\))?\s*$/);
868 if (!parsed || parsed.length !== 4)
869 throw new Error("Invalid state ref '" + ref + "'");
870 return { state: parsed[1] || null, paramExpr: parsed[3] || null };
871 }
872 /** @hidden */
873 function stateContext(el) {
874 var $uiView = el.parent().inheritedData('$uiView');
875 var path = core.parse('$cfg.path')($uiView);
876 return path ? core.tail(path).state.name : undefined;
877 }
878 /** @hidden */
879 function processedDef($state, $element, def) {
880 var uiState = def.uiState || $state.current.name;
881 var uiStateOpts = core.extend(defaultOpts($element, $state), def.uiStateOpts || {});
882 var href = $state.href(uiState, def.uiStateParams, uiStateOpts);
883 return { uiState: uiState, uiStateParams: def.uiStateParams, uiStateOpts: uiStateOpts, href: href };
884 }
885 /** @hidden */
886 function getTypeInfo(el) {
887 // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
888 var isSvg = Object.prototype.toString.call(el.prop('href')) === '[object SVGAnimatedString]';
889 var isForm = el[0].nodeName === 'FORM';
890 return {
891 attr: isForm ? 'action' : isSvg ? 'xlink:href' : 'href',
892 isAnchor: el.prop('tagName').toUpperCase() === 'A',
893 clickable: !isForm,
894 };
895 }
896 /** @hidden */
897 function clickHook(el, $state, $timeout, type, getDef) {
898 return function (e) {
899 var button = e.which || e.button, target = getDef();
900 if (!(button > 1 || e.ctrlKey || e.metaKey || e.shiftKey || e.altKey || el.attr('target'))) {
901 // HACK: This is to allow ng-clicks to be processed before the transition is initiated:
902 var transition_1 = $timeout(function () {
903 if (!el.attr('disabled')) {
904 $state.go(target.uiState, target.uiStateParams, target.uiStateOpts);
905 }
906 });
907 e.preventDefault();
908 // if the state has no URL, ignore one preventDefault from the <a> directive.
909 var ignorePreventDefaultCount_1 = type.isAnchor && !target.href ? 1 : 0;
910 e.preventDefault = function () {
911 if (ignorePreventDefaultCount_1-- <= 0)
912 $timeout.cancel(transition_1);
913 };
914 }
915 };
916 }
917 /** @hidden */
918 function defaultOpts(el, $state) {
919 return {
920 relative: stateContext(el) || $state.$current,
921 inherit: true,
922 source: 'sref',
923 };
924 }
925 /** @hidden */
926 function bindEvents(element, scope, hookFn, uiStateOpts) {
927 var events;
928 if (uiStateOpts) {
929 events = uiStateOpts.events;
930 }
931 if (!core.isArray(events)) {
932 events = ['click'];
933 }
934 var on = element.on ? 'on' : 'bind';
935 for (var _i = 0, events_1 = events; _i < events_1.length; _i++) {
936 var event_1 = events_1[_i];
937 element[on](event_1, hookFn);
938 }
939 scope.$on('$destroy', function () {
940 var off = element.off ? 'off' : 'unbind';
941 for (var _i = 0, events_2 = events; _i < events_2.length; _i++) {
942 var event_2 = events_2[_i];
943 element[off](event_2, hookFn);
944 }
945 });
946 }
947 /**
948 * `ui-sref`: A directive for linking to a state
949 *
950 * A directive which links to a state (and optionally, parameters).
951 * When clicked, this directive activates the linked state with the supplied parameter values.
952 *
953 * ### Linked State
954 * The attribute value of the `ui-sref` is the name of the state to link to.
955 *
956 * #### Example:
957 * This will activate the `home` state when the link is clicked.
958 * ```html
959 * <a ui-sref="home">Home</a>
960 * ```
961 *
962 * ### Relative Links
963 * You can also use relative state paths within `ui-sref`, just like a relative path passed to `$state.go()` ([[StateService.go]]).
964 * You just need to be aware that the path is relative to the state that *created* the link.
965 * This allows a state to create a relative `ui-sref` which always targets the same destination.
966 *
967 * #### Example:
968 * Both these links are relative to the parent state, even when a child state is currently active.
969 * ```html
970 * <a ui-sref=".child1">child 1 state</a>
971 * <a ui-sref=".child2">child 2 state</a>
972 * ```
973 *
974 * This link activates the parent state.
975 * ```html
976 * <a ui-sref="^">Return</a>
977 * ```
978 *
979 * ### hrefs
980 * If the linked state has a URL, the directive will automatically generate and
981 * update the `href` attribute (using the [[StateService.href]] method).
982 *
983 * #### Example:
984 * Assuming the `users` state has a url of `/users/`
985 * ```html
986 * <a ui-sref="users" href="/users/">Users</a>
987 * ```
988 *
989 * ### Parameter Values
990 * In addition to the state name, a `ui-sref` can include parameter values which are applied when activating the state.
991 * Param values can be provided in the `ui-sref` value after the state name, enclosed by parentheses.
992 * The content inside the parentheses is an expression, evaluated to the parameter values.
993 *
994 * #### Example:
995 * This example renders a list of links to users.
996 * The state's `userId` parameter value comes from each user's `user.id` property.
997 * ```html
998 * <li ng-repeat="user in users">
999 * <a ui-sref="users.detail({ userId: user.id })">{{ user.displayName }}</a>
1000 * </li>
1001 * ```
1002 *
1003 * Note:
1004 * The parameter values expression is `$watch`ed for updates.
1005 *
1006 * ### Transition Options
1007 * You can specify [[TransitionOptions]] to pass to [[StateService.go]] by using the `ui-sref-opts` attribute.
1008 * Options are restricted to `location`, `inherit`, and `reload`.
1009 *
1010 * #### Example:
1011 * ```html
1012 * <a ui-sref="home" ui-sref-opts="{ reload: true }">Home</a>
1013 * ```
1014 *
1015 * ### Other DOM Events
1016 *
1017 * You can also customize which DOM events to respond to (instead of `click`) by
1018 * providing an `events` array in the `ui-sref-opts` attribute.
1019 *
1020 * #### Example:
1021 * ```html
1022 * <input type="text" ui-sref="contacts" ui-sref-opts="{ events: ['change', 'blur'] }">
1023 * ```
1024 *
1025 * ### Highlighting the active link
1026 * This directive can be used in conjunction with [[uiSrefActive]] to highlight the active link.
1027 *
1028 * ### Examples
1029 * If you have the following template:
1030 *
1031 * ```html
1032 * <a ui-sref="home">Home</a>
1033 * <a ui-sref="about">About</a>
1034 * <a ui-sref="{page: 2}">Next page</a>
1035 *
1036 * <ul>
1037 * <li ng-repeat="contact in contacts">
1038 * <a ui-sref="contacts.detail({ id: contact.id })">{{ contact.name }}</a>
1039 * </li>
1040 * </ul>
1041 * ```
1042 *
1043 * Then (assuming the current state is `contacts`) the rendered html including hrefs would be:
1044 *
1045 * ```html
1046 * <a href="#/home" ui-sref="home">Home</a>
1047 * <a href="#/about" ui-sref="about">About</a>
1048 * <a href="#/contacts?page=2" ui-sref="{page: 2}">Next page</a>
1049 *
1050 * <ul>
1051 * <li ng-repeat="contact in contacts">
1052 * <a href="#/contacts/1" ui-sref="contacts.detail({ id: contact.id })">Joe</a>
1053 * </li>
1054 * <li ng-repeat="contact in contacts">
1055 * <a href="#/contacts/2" ui-sref="contacts.detail({ id: contact.id })">Alice</a>
1056 * </li>
1057 * <li ng-repeat="contact in contacts">
1058 * <a href="#/contacts/3" ui-sref="contacts.detail({ id: contact.id })">Bob</a>
1059 * </li>
1060 * </ul>
1061 *
1062 * <a href="#/home" ui-sref="home" ui-sref-opts="{reload: true}">Home</a>
1063 * ```
1064 *
1065 * ### Notes
1066 *
1067 * - You can use `ui-sref` to change **only the parameter values** by omitting the state name and parentheses.
1068 * #### Example:
1069 * Sets the `lang` parameter to `en` and remains on the same state.
1070 *
1071 * ```html
1072 * <a ui-sref="{ lang: 'en' }">English</a>
1073 * ```
1074 *
1075 * - A middle-click, right-click, or ctrl-click is handled (natively) by the browser to open the href in a new window, for example.
1076 *
1077 * - Unlike the parameter values expression, the state name is not `$watch`ed (for performance reasons).
1078 * If you need to dynamically update the state being linked to, use the fully dynamic [[uiState]] directive.
1079 */
1080 var uiSrefDirective;
1081 uiSrefDirective = [
1082 '$uiRouter',
1083 '$timeout',
1084 function $StateRefDirective($uiRouter, $timeout) {
1085 var $state = $uiRouter.stateService;
1086 return {
1087 restrict: 'A',
1088 require: ['?^uiSrefActive', '?^uiSrefActiveEq'],
1089 link: function (scope, element, attrs, uiSrefActive) {
1090 var type = getTypeInfo(element);
1091 var active = uiSrefActive[1] || uiSrefActive[0];
1092 var unlinkInfoFn = null;
1093 var rawDef = {};
1094 var getDef = function () { return processedDef($state, element, rawDef); };
1095 var ref = parseStateRef(attrs.uiSref);
1096 rawDef.uiState = ref.state;
1097 rawDef.uiStateOpts = attrs.uiSrefOpts ? scope.$eval(attrs.uiSrefOpts) : {};
1098 function update() {
1099 var def = getDef();
1100 if (unlinkInfoFn)
1101 unlinkInfoFn();
1102 if (active)
1103 unlinkInfoFn = active.$$addStateInfo(def.uiState, def.uiStateParams);
1104 if (def.href != null)
1105 attrs.$set(type.attr, def.href);
1106 }
1107 if (ref.paramExpr) {
1108 scope.$watch(ref.paramExpr, function (val) {
1109 rawDef.uiStateParams = core.extend({}, val);
1110 update();
1111 }, true);
1112 rawDef.uiStateParams = core.extend({}, scope.$eval(ref.paramExpr));
1113 }
1114 update();
1115 scope.$on('$destroy', $uiRouter.stateRegistry.onStatesChanged(update));
1116 scope.$on('$destroy', $uiRouter.transitionService.onSuccess({}, update));
1117 if (!type.clickable)
1118 return;
1119 var hookFn = clickHook(element, $state, $timeout, type, getDef);
1120 bindEvents(element, scope, hookFn, rawDef.uiStateOpts);
1121 },
1122 };
1123 },
1124 ];
1125 /**
1126 * `ui-state`: A fully dynamic directive for linking to a state
1127 *
1128 * A directive which links to a state (and optionally, parameters).
1129 * When clicked, this directive activates the linked state with the supplied parameter values.
1130 *
1131 * **This directive is very similar to [[uiSref]], but it `$observe`s and `$watch`es/evaluates all its inputs.**
1132 *
1133 * A directive which links to a state (and optionally, parameters).
1134 * When clicked, this directive activates the linked state with the supplied parameter values.
1135 *
1136 * ### Linked State
1137 * The attribute value of `ui-state` is an expression which is `$watch`ed and evaluated as the state to link to.
1138 * **This is in contrast with `ui-sref`, which takes a state name as a string literal.**
1139 *
1140 * #### Example:
1141 * Create a list of links.
1142 * ```html
1143 * <li ng-repeat="link in navlinks">
1144 * <a ui-state="link.state">{{ link.displayName }}</a>
1145 * </li>
1146 * ```
1147 *
1148 * ### Relative Links
1149 * If the expression evaluates to a relative path, it is processed like [[uiSref]].
1150 * You just need to be aware that the path is relative to the state that *created* the link.
1151 * This allows a state to create relative `ui-state` which always targets the same destination.
1152 *
1153 * ### hrefs
1154 * If the linked state has a URL, the directive will automatically generate and
1155 * update the `href` attribute (using the [[StateService.href]] method).
1156 *
1157 * ### Parameter Values
1158 * In addition to the state name expression, a `ui-state` can include parameter values which are applied when activating the state.
1159 * Param values should be provided using the `ui-state-params` attribute.
1160 * The `ui-state-params` attribute value is `$watch`ed and evaluated as an expression.
1161 *
1162 * #### Example:
1163 * This example renders a list of links with param values.
1164 * The state's `userId` parameter value comes from each user's `user.id` property.
1165 * ```html
1166 * <li ng-repeat="link in navlinks">
1167 * <a ui-state="link.state" ui-state-params="link.params">{{ link.displayName }}</a>
1168 * </li>
1169 * ```
1170 *
1171 * ### Transition Options
1172 * You can specify [[TransitionOptions]] to pass to [[StateService.go]] by using the `ui-state-opts` attribute.
1173 * Options are restricted to `location`, `inherit`, and `reload`.
1174 * The value of the `ui-state-opts` is `$watch`ed and evaluated as an expression.
1175 *
1176 * #### Example:
1177 * ```html
1178 * <a ui-state="returnto.state" ui-state-opts="{ reload: true }">Home</a>
1179 * ```
1180 *
1181 * ### Other DOM Events
1182 *
1183 * You can also customize which DOM events to respond to (instead of `click`) by
1184 * providing an `events` array in the `ui-state-opts` attribute.
1185 *
1186 * #### Example:
1187 * ```html
1188 * <input type="text" ui-state="contacts" ui-state-opts="{ events: ['change', 'blur'] }">
1189 * ```
1190 *
1191 * ### Highlighting the active link
1192 * This directive can be used in conjunction with [[uiSrefActive]] to highlight the active link.
1193 *
1194 * ### Notes
1195 *
1196 * - You can use `ui-params` to change **only the parameter values** by omitting the state name and supplying only `ui-state-params`.
1197 * However, it might be simpler to use [[uiSref]] parameter-only links.
1198 *
1199 * #### Example:
1200 * Sets the `lang` parameter to `en` and remains on the same state.
1201 *
1202 * ```html
1203 * <a ui-state="" ui-state-params="{ lang: 'en' }">English</a>
1204 * ```
1205 *
1206 * - A middle-click, right-click, or ctrl-click is handled (natively) by the browser to open the href in a new window, for example.
1207 * ```
1208 */
1209 var uiStateDirective;
1210 uiStateDirective = [
1211 '$uiRouter',
1212 '$timeout',
1213 function $StateRefDynamicDirective($uiRouter, $timeout) {
1214 var $state = $uiRouter.stateService;
1215 return {
1216 restrict: 'A',
1217 require: ['?^uiSrefActive', '?^uiSrefActiveEq'],
1218 link: function (scope, element, attrs, uiSrefActive) {
1219 var type = getTypeInfo(element);
1220 var active = uiSrefActive[1] || uiSrefActive[0];
1221 var unlinkInfoFn = null;
1222 var hookFn;
1223 var rawDef = {};
1224 var getDef = function () { return processedDef($state, element, rawDef); };
1225 var inputAttrs = ['uiState', 'uiStateParams', 'uiStateOpts'];
1226 var watchDeregFns = inputAttrs.reduce(function (acc, attr) { return ((acc[attr] = core.noop), acc); }, {});
1227 function update() {
1228 var def = getDef();
1229 if (unlinkInfoFn)
1230 unlinkInfoFn();
1231 if (active)
1232 unlinkInfoFn = active.$$addStateInfo(def.uiState, def.uiStateParams);
1233 if (def.href != null)
1234 attrs.$set(type.attr, def.href);
1235 }
1236 inputAttrs.forEach(function (field) {
1237 rawDef[field] = attrs[field] ? scope.$eval(attrs[field]) : null;
1238 attrs.$observe(field, function (expr) {
1239 watchDeregFns[field]();
1240 watchDeregFns[field] = scope.$watch(expr, function (newval) {
1241 rawDef[field] = newval;
1242 update();
1243 }, true);
1244 });
1245 });
1246 update();
1247 scope.$on('$destroy', $uiRouter.stateRegistry.onStatesChanged(update));
1248 scope.$on('$destroy', $uiRouter.transitionService.onSuccess({}, update));
1249 if (!type.clickable)
1250 return;
1251 hookFn = clickHook(element, $state, $timeout, type, getDef);
1252 bindEvents(element, scope, hookFn, rawDef.uiStateOpts);
1253 },
1254 };
1255 },
1256 ];
1257 /**
1258 * `ui-sref-active` and `ui-sref-active-eq`: A directive that adds a CSS class when a `ui-sref` is active
1259 *
1260 * A directive working alongside [[uiSref]] and [[uiState]] to add classes to an element when the
1261 * related directive's state is active (and remove them when it is inactive).
1262 *
1263 * The primary use-case is to highlight the active link in navigation menus,
1264 * distinguishing it from the inactive menu items.
1265 *
1266 * ### Linking to a `ui-sref` or `ui-state`
1267 * `ui-sref-active` can live on the same element as `ui-sref`/`ui-state`, or it can be on a parent element.
1268 * 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**.
1269 *
1270 * ### Matching
1271 *
1272 * The `ui-sref-active` directive applies the CSS class when the `ui-sref`/`ui-state`'s target state **or any child state is active**.
1273 * This is a "fuzzy match" which uses [[StateService.includes]].
1274 *
1275 * 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).
1276 * This is an "exact match" which uses [[StateService.is]].
1277 *
1278 * ### Parameter values
1279 * 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.
1280 * This allows a list of links to the same state with different parameters to be rendered, and the correct one highlighted.
1281 *
1282 * #### Example:
1283 * ```html
1284 * <li ng-repeat="user in users" ui-sref-active="active">
1285 * <a ui-sref="user.details({ userId: user.id })">{{ user.lastName }}</a>
1286 * </li>
1287 * ```
1288 *
1289 * ### Examples
1290 *
1291 * Given the following template:
1292 * #### Example:
1293 * ```html
1294 * <ul>
1295 * <li ui-sref-active="active" class="item">
1296 * <a href ui-sref="app.user({user: 'bilbobaggins'})">@bilbobaggins</a>
1297 * </li>
1298 * </ul>
1299 * ```
1300 *
1301 * When the app state is `app.user` (or any child state),
1302 * and contains the state parameter "user" with value "bilbobaggins",
1303 * the resulting HTML will appear as (note the 'active' class):
1304 *
1305 * ```html
1306 * <ul>
1307 * <li ui-sref-active="active" class="item active">
1308 * <a ui-sref="app.user({user: 'bilbobaggins'})" href="/users/bilbobaggins">@bilbobaggins</a>
1309 * </li>
1310 * </ul>
1311 * ```
1312 *
1313 * ### Glob mode
1314 *
1315 * It is possible to pass `ui-sref-active` an expression that evaluates to an object.
1316 * The objects keys represent active class names and values represent the respective state names/globs.
1317 * `ui-sref-active` will match if the current active state **includes** any of
1318 * the specified state names/globs, even the abstract ones.
1319 *
1320 * #### Example:
1321 * Given the following template, with "admin" being an abstract state:
1322 * ```html
1323 * <div ui-sref-active="{'active': 'admin.**'}">
1324 * <a ui-sref-active="active" ui-sref="admin.roles">Roles</a>
1325 * </div>
1326 * ```
1327 *
1328 * Arrays are also supported as values in the `ngClass`-like interface.
1329 * This allows multiple states to add `active` class.
1330 *
1331 * #### Example:
1332 * Given the following template, with "admin.roles" being the current state, the class will be added too:
1333 * ```html
1334 * <div ui-sref-active="{'active': ['owner.**', 'admin.**']}">
1335 * <a ui-sref-active="active" ui-sref="admin.roles">Roles</a>
1336 * </div>
1337 * ```
1338 *
1339 * When the current state is "admin.roles" the "active" class will be applied to both the `<div>` and `<a>` elements.
1340 * It is important to note that the state names/globs passed to `ui-sref-active` override any state provided by a linked `ui-sref`.
1341 *
1342 * ### Notes:
1343 *
1344 * - The class name is interpolated **once** during the directives link time (any further changes to the
1345 * interpolated value are ignored).
1346 *
1347 * - Multiple classes may be specified in a space-separated format: `ui-sref-active='class1 class2 class3'`
1348 */
1349 var uiSrefActiveDirective;
1350 uiSrefActiveDirective = [
1351 '$state',
1352 '$stateParams',
1353 '$interpolate',
1354 '$uiRouter',
1355 function $StateRefActiveDirective($state, $stateParams, $interpolate, $uiRouter) {
1356 return {
1357 restrict: 'A',
1358 controller: [
1359 '$scope',
1360 '$element',
1361 '$attrs',
1362 function ($scope, $element, $attrs) {
1363 var states = [];
1364 var activeEqClass;
1365 var uiSrefActive;
1366 // There probably isn't much point in $observing this
1367 // uiSrefActive and uiSrefActiveEq share the same directive object with some
1368 // slight difference in logic routing
1369 activeEqClass = $interpolate($attrs.uiSrefActiveEq || '', false)($scope);
1370 try {
1371 uiSrefActive = $scope.$eval($attrs.uiSrefActive);
1372 }
1373 catch (e) {
1374 // Do nothing. uiSrefActive is not a valid expression.
1375 // Fall back to using $interpolate below
1376 }
1377 uiSrefActive = uiSrefActive || $interpolate($attrs.uiSrefActive || '', false)($scope);
1378 setStatesFromDefinitionObject(uiSrefActive);
1379 // Allow uiSref to communicate with uiSrefActive[Equals]
1380 this.$$addStateInfo = function (newState, newParams) {
1381 // we already got an explicit state provided by ui-sref-active, so we
1382 // shadow the one that comes from ui-sref
1383 if (core.isObject(uiSrefActive) && states.length > 0) {
1384 return;
1385 }
1386 var deregister = addState(newState, newParams, uiSrefActive);
1387 update();
1388 return deregister;
1389 };
1390 function updateAfterTransition(trans) {
1391 trans.promise.then(update, core.noop);
1392 }
1393 $scope.$on('$destroy', setupEventListeners());
1394 if ($uiRouter.globals.transition) {
1395 updateAfterTransition($uiRouter.globals.transition);
1396 }
1397 function setupEventListeners() {
1398 var deregisterStatesChangedListener = $uiRouter.stateRegistry.onStatesChanged(handleStatesChanged);
1399 var deregisterOnStartListener = $uiRouter.transitionService.onStart({}, updateAfterTransition);
1400 var deregisterStateChangeSuccessListener = $scope.$on('$stateChangeSuccess', update);
1401 return function cleanUp() {
1402 deregisterStatesChangedListener();
1403 deregisterOnStartListener();
1404 deregisterStateChangeSuccessListener();
1405 };
1406 }
1407 function handleStatesChanged() {
1408 setStatesFromDefinitionObject(uiSrefActive);
1409 }
1410 function setStatesFromDefinitionObject(statesDefinition) {
1411 if (core.isObject(statesDefinition)) {
1412 states = [];
1413 core.forEach(statesDefinition, function (stateOrName, activeClass) {
1414 // Helper function to abstract adding state.
1415 var addStateForClass = function (stateOrName, activeClass) {
1416 var ref = parseStateRef(stateOrName);
1417 addState(ref.state, $scope.$eval(ref.paramExpr), activeClass);
1418 };
1419 if (core.isString(stateOrName)) {
1420 // If state is string, just add it.
1421 addStateForClass(stateOrName, activeClass);
1422 }
1423 else if (core.isArray(stateOrName)) {
1424 // If state is an array, iterate over it and add each array item individually.
1425 core.forEach(stateOrName, function (stateOrName) {
1426 addStateForClass(stateOrName, activeClass);
1427 });
1428 }
1429 });
1430 }
1431 }
1432 function addState(stateName, stateParams, activeClass) {
1433 var state = $state.get(stateName, stateContext($element));
1434 var stateInfo = {
1435 state: state || { name: stateName },
1436 params: stateParams,
1437 activeClass: activeClass,
1438 };
1439 states.push(stateInfo);
1440 return function removeState() {
1441 core.removeFrom(states)(stateInfo);
1442 };
1443 }
1444 // Update route state
1445 function update() {
1446 var splitClasses = function (str) { return str.split(/\s/).filter(core.identity); };
1447 var getClasses = function (stateList) {
1448 return stateList
1449 .map(function (x) { return x.activeClass; })
1450 .map(splitClasses)
1451 .reduce(core.unnestR, []);
1452 };
1453 var allClasses = getClasses(states).concat(splitClasses(activeEqClass)).reduce(core.uniqR, []);
1454 var fuzzyClasses = getClasses(states.filter(function (x) { return $state.includes(x.state.name, x.params); }));
1455 var exactlyMatchesAny = !!states.filter(function (x) { return $state.is(x.state.name, x.params); }).length;
1456 var exactClasses = exactlyMatchesAny ? splitClasses(activeEqClass) : [];
1457 var addClasses = fuzzyClasses.concat(exactClasses).reduce(core.uniqR, []);
1458 var removeClasses = allClasses.filter(function (cls) { return !core.inArray(addClasses, cls); });
1459 $scope.$evalAsync(function () {
1460 addClasses.forEach(function (className) { return $element.addClass(className); });
1461 removeClasses.forEach(function (className) { return $element.removeClass(className); });
1462 });
1463 }
1464 update();
1465 },
1466 ],
1467 };
1468 },
1469 ];
1470 ng
1471 .module('ui.router.state')
1472 .directive('uiSref', uiSrefDirective)
1473 .directive('uiSrefActive', uiSrefActiveDirective)
1474 .directive('uiSrefActiveEq', uiSrefActiveDirective)
1475 .directive('uiState', uiStateDirective);
1476
1477 /** @publicapi @module ng1 */ /** */
1478 /**
1479 * `isState` Filter: truthy if the current state is the parameter
1480 *
1481 * Translates to [[StateService.is]] `$state.is("stateName")`.
1482 *
1483 * #### Example:
1484 * ```html
1485 * <div ng-if="'stateName' | isState">show if state is 'stateName'</div>
1486 * ```
1487 */
1488 $IsStateFilter.$inject = ['$state'];
1489 function $IsStateFilter($state) {
1490 var isFilter = function (state, params, options) {
1491 return $state.is(state, params, options);
1492 };
1493 isFilter.$stateful = true;
1494 return isFilter;
1495 }
1496 /**
1497 * `includedByState` Filter: truthy if the current state includes the parameter
1498 *
1499 * Translates to [[StateService.includes]]` $state.is("fullOrPartialStateName")`.
1500 *
1501 * #### Example:
1502 * ```html
1503 * <div ng-if="'fullOrPartialStateName' | includedByState">show if state includes 'fullOrPartialStateName'</div>
1504 * ```
1505 */
1506 $IncludedByStateFilter.$inject = ['$state'];
1507 function $IncludedByStateFilter($state) {
1508 var includesFilter = function (state, params, options) {
1509 return $state.includes(state, params, options);
1510 };
1511 includesFilter.$stateful = true;
1512 return includesFilter;
1513 }
1514 ng.module('ui.router.state').filter('isState', $IsStateFilter).filter('includedByState', $IncludedByStateFilter);
1515
1516 /** @publicapi @module directives */ /** */
1517 /**
1518 * `ui-view`: A viewport directive which is filled in by a view from the active state.
1519 *
1520 * ### Attributes
1521 *
1522 * - `name`: (Optional) A view name.
1523 * The name should be unique amongst the other views in the same state.
1524 * You can have views of the same name that live in different states.
1525 * The ui-view can be targeted in a View using the name ([[Ng1StateDeclaration.views]]).
1526 *
1527 * - `autoscroll`: an expression. When it evaluates to true, the `ui-view` will be scrolled into view when it is activated.
1528 * Uses [[$uiViewScroll]] to do the scrolling.
1529 *
1530 * - `onload`: Expression to evaluate whenever the view updates.
1531 *
1532 * #### Example:
1533 * A view can be unnamed or named.
1534 * ```html
1535 * <!-- Unnamed -->
1536 * <div ui-view></div>
1537 *
1538 * <!-- Named -->
1539 * <div ui-view="viewName"></div>
1540 *
1541 * <!-- Named (different style) -->
1542 * <ui-view name="viewName"></ui-view>
1543 * ```
1544 *
1545 * You can only have one unnamed view within any template (or root html). If you are only using a
1546 * single view and it is unnamed then you can populate it like so:
1547 *
1548 * ```html
1549 * <div ui-view></div>
1550 * $stateProvider.state("home", {
1551 * template: "<h1>HELLO!</h1>"
1552 * })
1553 * ```
1554 *
1555 * The above is a convenient shortcut equivalent to specifying your view explicitly with the
1556 * [[Ng1StateDeclaration.views]] config property, by name, in this case an empty name:
1557 *
1558 * ```js
1559 * $stateProvider.state("home", {
1560 * views: {
1561 * "": {
1562 * template: "<h1>HELLO!</h1>"
1563 * }
1564 * }
1565 * })
1566 * ```
1567 *
1568 * But typically you'll only use the views property if you name your view or have more than one view
1569 * in the same template. There's not really a compelling reason to name a view if its the only one,
1570 * but you could if you wanted, like so:
1571 *
1572 * ```html
1573 * <div ui-view="main"></div>
1574 * ```
1575 *
1576 * ```js
1577 * $stateProvider.state("home", {
1578 * views: {
1579 * "main": {
1580 * template: "<h1>HELLO!</h1>"
1581 * }
1582 * }
1583 * })
1584 * ```
1585 *
1586 * Really though, you'll use views to set up multiple views:
1587 *
1588 * ```html
1589 * <div ui-view></div>
1590 * <div ui-view="chart"></div>
1591 * <div ui-view="data"></div>
1592 * ```
1593 *
1594 * ```js
1595 * $stateProvider.state("home", {
1596 * views: {
1597 * "": {
1598 * template: "<h1>HELLO!</h1>"
1599 * },
1600 * "chart": {
1601 * template: "<chart_thing/>"
1602 * },
1603 * "data": {
1604 * template: "<data_thing/>"
1605 * }
1606 * }
1607 * })
1608 * ```
1609 *
1610 * #### Examples for `autoscroll`:
1611 * ```html
1612 * <!-- If autoscroll present with no expression,
1613 * then scroll ui-view into view -->
1614 * <ui-view autoscroll/>
1615 *
1616 * <!-- If autoscroll present with valid expression,
1617 * then scroll ui-view into view if expression evaluates to true -->
1618 * <ui-view autoscroll='true'/>
1619 * <ui-view autoscroll='false'/>
1620 * <ui-view autoscroll='scopeVariable'/>
1621 * ```
1622 *
1623 * Resolve data:
1624 *
1625 * The resolved data from the state's `resolve` block is placed on the scope as `$resolve` (this
1626 * can be customized using [[Ng1ViewDeclaration.resolveAs]]). This can be then accessed from the template.
1627 *
1628 * Note that when `controllerAs` is being used, `$resolve` is set on the controller instance *after* the
1629 * controller is instantiated. The `$onInit()` hook can be used to perform initialization code which
1630 * depends on `$resolve` data.
1631 *
1632 * #### Example:
1633 * ```js
1634 * $stateProvider.state('home', {
1635 * template: '<my-component user="$resolve.user"></my-component>',
1636 * resolve: {
1637 * user: function(UserService) { return UserService.fetchUser(); }
1638 * }
1639 * });
1640 * ```
1641 */
1642 var uiView;
1643 // eslint-disable-next-line prefer-const
1644 uiView = [
1645 '$view',
1646 '$animate',
1647 '$uiViewScroll',
1648 '$interpolate',
1649 '$q',
1650 function $ViewDirective($view, $animate, $uiViewScroll, $interpolate, $q) {
1651 function getRenderer() {
1652 return {
1653 enter: function (element, target, cb) {
1654 if (ng.version.minor > 2) {
1655 $animate.enter(element, null, target).then(cb);
1656 }
1657 else {
1658 $animate.enter(element, null, target, cb);
1659 }
1660 },
1661 leave: function (element, cb) {
1662 if (ng.version.minor > 2) {
1663 $animate.leave(element).then(cb);
1664 }
1665 else {
1666 $animate.leave(element, cb);
1667 }
1668 },
1669 };
1670 }
1671 function configsEqual(config1, config2) {
1672 return config1 === config2;
1673 }
1674 var rootData = {
1675 $cfg: { viewDecl: { $context: $view._pluginapi._rootViewContext() } },
1676 $uiView: {},
1677 };
1678 var directive = {
1679 count: 0,
1680 restrict: 'ECA',
1681 terminal: true,
1682 priority: 400,
1683 transclude: 'element',
1684 compile: function (tElement, tAttrs, $transclude) {
1685 return function (scope, $element, attrs) {
1686 var onloadExp = attrs['onload'] || '', autoScrollExp = attrs['autoscroll'], renderer = getRenderer(), inherited = $element.inheritedData('$uiView') || rootData, name = $interpolate(attrs['uiView'] || attrs['name'] || '')(scope) || '$default';
1687 var previousEl, currentEl, currentScope, viewConfig;
1688 var activeUIView = {
1689 $type: 'ng1',
1690 id: directive.count++,
1691 name: name,
1692 fqn: inherited.$uiView.fqn ? inherited.$uiView.fqn + '.' + name : name,
1693 config: null,
1694 configUpdated: configUpdatedCallback,
1695 get creationContext() {
1696 // The context in which this ui-view "tag" was created
1697 var fromParentTagConfig = core.parse('$cfg.viewDecl.$context')(inherited);
1698 // Allow <ui-view name="foo"><ui-view name="bar"></ui-view></ui-view>
1699 // See https://github.com/angular-ui/ui-router/issues/3355
1700 var fromParentTag = core.parse('$uiView.creationContext')(inherited);
1701 return fromParentTagConfig || fromParentTag;
1702 },
1703 };
1704 core.trace.traceUIViewEvent('Linking', activeUIView);
1705 function configUpdatedCallback(config) {
1706 if (config && !(config instanceof Ng1ViewConfig))
1707 return;
1708 if (configsEqual(viewConfig, config))
1709 return;
1710 core.trace.traceUIViewConfigUpdated(activeUIView, config && config.viewDecl && config.viewDecl.$context);
1711 viewConfig = config;
1712 updateView(config);
1713 }
1714 $element.data('$uiView', { $uiView: activeUIView });
1715 updateView();
1716 var unregister = $view.registerUIView(activeUIView);
1717 scope.$on('$destroy', function () {
1718 core.trace.traceUIViewEvent('Destroying/Unregistering', activeUIView);
1719 unregister();
1720 });
1721 function cleanupLastView() {
1722 if (previousEl) {
1723 core.trace.traceUIViewEvent('Removing (previous) el', previousEl.data('$uiView'));
1724 previousEl.remove();
1725 previousEl = null;
1726 }
1727 if (currentScope) {
1728 core.trace.traceUIViewEvent('Destroying scope', activeUIView);
1729 currentScope.$destroy();
1730 currentScope = null;
1731 }
1732 if (currentEl) {
1733 var _viewData_1 = currentEl.data('$uiViewAnim');
1734 core.trace.traceUIViewEvent('Animate out', _viewData_1);
1735 renderer.leave(currentEl, function () {
1736 _viewData_1.$$animLeave.resolve();
1737 previousEl = null;
1738 });
1739 previousEl = currentEl;
1740 currentEl = null;
1741 }
1742 }
1743 function updateView(config) {
1744 var newScope = scope.$new();
1745 var animEnter = $q.defer(), animLeave = $q.defer();
1746 var $uiViewData = {
1747 $cfg: config,
1748 $uiView: activeUIView,
1749 };
1750 var $uiViewAnim = {
1751 $animEnter: animEnter.promise,
1752 $animLeave: animLeave.promise,
1753 $$animLeave: animLeave,
1754 };
1755 /**
1756 * @ngdoc event
1757 * @name ui.router.state.directive:ui-view#$viewContentLoading
1758 * @eventOf ui.router.state.directive:ui-view
1759 * @eventType emits on ui-view directive scope
1760 * @description
1761 *
1762 * Fired once the view **begins loading**, *before* the DOM is rendered.
1763 *
1764 * @param {Object} event Event object.
1765 * @param {string} viewName Name of the view.
1766 */
1767 newScope.$emit('$viewContentLoading', name);
1768 var cloned = $transclude(newScope, function (clone) {
1769 clone.data('$uiViewAnim', $uiViewAnim);
1770 clone.data('$uiView', $uiViewData);
1771 renderer.enter(clone, $element, function onUIViewEnter() {
1772 animEnter.resolve();
1773 if (currentScope)
1774 currentScope.$emit('$viewContentAnimationEnded');
1775 if ((core.isDefined(autoScrollExp) && !autoScrollExp) || scope.$eval(autoScrollExp)) {
1776 $uiViewScroll(clone);
1777 }
1778 });
1779 cleanupLastView();
1780 });
1781 currentEl = cloned;
1782 currentScope = newScope;
1783 /**
1784 * @ngdoc event
1785 * @name ui.router.state.directive:ui-view#$viewContentLoaded
1786 * @eventOf ui.router.state.directive:ui-view
1787 * @eventType emits on ui-view directive scope
1788 * @description *
1789 * Fired once the view is **loaded**, *after* the DOM is rendered.
1790 *
1791 * @param {Object} event Event object.
1792 */
1793 currentScope.$emit('$viewContentLoaded', config || viewConfig);
1794 currentScope.$eval(onloadExp);
1795 }
1796 };
1797 },
1798 };
1799 return directive;
1800 },
1801 ];
1802 $ViewDirectiveFill.$inject = ['$compile', '$controller', '$transitions', '$view', '$q'];
1803 /** @hidden */
1804 function $ViewDirectiveFill($compile, $controller, $transitions, $view, $q) {
1805 var getControllerAs = core.parse('viewDecl.controllerAs');
1806 var getResolveAs = core.parse('viewDecl.resolveAs');
1807 return {
1808 restrict: 'ECA',
1809 priority: -400,
1810 compile: function (tElement) {
1811 var initial = tElement.html();
1812 tElement.empty();
1813 return function (scope, $element) {
1814 var data = $element.data('$uiView');
1815 if (!data) {
1816 $element.html(initial);
1817 $compile($element.contents())(scope);
1818 return;
1819 }
1820 var cfg = data.$cfg || { viewDecl: {}, getTemplate: core.noop };
1821 var resolveCtx = cfg.path && new core.ResolveContext(cfg.path);
1822 $element.html(cfg.getTemplate($element, resolveCtx) || initial);
1823 core.trace.traceUIViewFill(data.$uiView, $element.html());
1824 var link = $compile($element.contents());
1825 var controller = cfg.controller;
1826 var controllerAs = getControllerAs(cfg);
1827 var resolveAs = getResolveAs(cfg);
1828 var locals = resolveCtx && getLocals(resolveCtx);
1829 scope[resolveAs] = locals;
1830 if (controller) {
1831 var controllerInstance = ($controller(controller, core.extend({}, locals, { $scope: scope, $element: $element })));
1832 if (controllerAs) {
1833 scope[controllerAs] = controllerInstance;
1834 scope[controllerAs][resolveAs] = locals;
1835 }
1836 // TODO: Use $view service as a central point for registering component-level hooks
1837 // Then, when a component is created, tell the $view service, so it can invoke hooks
1838 // $view.componentLoaded(controllerInstance, { $scope: scope, $element: $element });
1839 // scope.$on('$destroy', () => $view.componentUnloaded(controllerInstance, { $scope: scope, $element: $element }));
1840 $element.data('$ngControllerController', controllerInstance);
1841 $element.children().data('$ngControllerController', controllerInstance);
1842 registerControllerCallbacks($q, $transitions, controllerInstance, scope, cfg);
1843 }
1844 // Wait for the component to appear in the DOM
1845 if (core.isString(cfg.component)) {
1846 var kebobName = core.kebobString(cfg.component);
1847 var tagRegexp_1 = new RegExp("^(x-|data-)?" + kebobName + "$", 'i');
1848 var getComponentController = function () {
1849 var directiveEl = [].slice
1850 .call($element[0].children)
1851 .filter(function (el) { return el && el.tagName && tagRegexp_1.exec(el.tagName); });
1852 return directiveEl && ng.element(directiveEl).data("$" + cfg.component + "Controller");
1853 };
1854 var deregisterWatch_1 = scope.$watch(getComponentController, function (ctrlInstance) {
1855 if (!ctrlInstance)
1856 return;
1857 registerControllerCallbacks($q, $transitions, ctrlInstance, scope, cfg);
1858 deregisterWatch_1();
1859 });
1860 }
1861 link(scope);
1862 };
1863 },
1864 };
1865 }
1866 /** @hidden */
1867 var hasComponentImpl = typeof ng.module('ui.router')['component'] === 'function';
1868 /** @hidden incrementing id */
1869 var _uiCanExitId = 0;
1870 /** @hidden TODO: move these callbacks to $view and/or `/hooks/components.ts` or something */
1871 function registerControllerCallbacks($q, $transitions, controllerInstance, $scope, cfg) {
1872 // Call $onInit() ASAP
1873 if (core.isFunction(controllerInstance.$onInit) &&
1874 !((cfg.viewDecl.component || cfg.viewDecl.componentProvider) && hasComponentImpl)) {
1875 controllerInstance.$onInit();
1876 }
1877 var viewState = core.tail(cfg.path).state.self;
1878 var hookOptions = { bind: controllerInstance };
1879 // Add component-level hook for onUiParamsChanged
1880 if (core.isFunction(controllerInstance.uiOnParamsChanged)) {
1881 var resolveContext = new core.ResolveContext(cfg.path);
1882 var viewCreationTrans_1 = resolveContext.getResolvable('$transition$').data;
1883 // Fire callback on any successful transition
1884 var paramsUpdated = function ($transition$) {
1885 // Exit early if the $transition$ is the same as the view was created within.
1886 // Exit early if the $transition$ will exit the state the view is for.
1887 if ($transition$ === viewCreationTrans_1 || $transition$.exiting().indexOf(viewState) !== -1)
1888 return;
1889 var toParams = $transition$.params('to');
1890 var fromParams = $transition$.params('from');
1891 var getNodeSchema = function (node) { return node.paramSchema; };
1892 var toSchema = $transition$.treeChanges('to').map(getNodeSchema).reduce(core.unnestR, []);
1893 var fromSchema = $transition$.treeChanges('from').map(getNodeSchema).reduce(core.unnestR, []);
1894 // Find the to params that have different values than the from params
1895 var changedToParams = toSchema.filter(function (param) {
1896 var idx = fromSchema.indexOf(param);
1897 return idx === -1 || !fromSchema[idx].type.equals(toParams[param.id], fromParams[param.id]);
1898 });
1899 // Only trigger callback if a to param has changed or is new
1900 if (changedToParams.length) {
1901 var changedKeys_1 = changedToParams.map(function (x) { return x.id; });
1902 // Filter the params to only changed/new to params. `$transition$.params()` may be used to get all params.
1903 var newValues = core.filter(toParams, function (val, key) { return changedKeys_1.indexOf(key) !== -1; });
1904 controllerInstance.uiOnParamsChanged(newValues, $transition$);
1905 }
1906 };
1907 $scope.$on('$destroy', $transitions.onSuccess({}, paramsUpdated, hookOptions));
1908 }
1909 // Add component-level hook for uiCanExit
1910 if (core.isFunction(controllerInstance.uiCanExit)) {
1911 var id_1 = _uiCanExitId++;
1912 var cacheProp_1 = '_uiCanExitIds';
1913 // Returns true if a redirect transition already answered truthy
1914 var prevTruthyAnswer_1 = function (trans) {
1915 return !!trans && ((trans[cacheProp_1] && trans[cacheProp_1][id_1] === true) || prevTruthyAnswer_1(trans.redirectedFrom()));
1916 };
1917 // If a user answered yes, but the transition was later redirected, don't also ask for the new redirect transition
1918 var wrappedHook = function (trans) {
1919 var promise;
1920 var ids = (trans[cacheProp_1] = trans[cacheProp_1] || {});
1921 if (!prevTruthyAnswer_1(trans)) {
1922 promise = $q.when(controllerInstance.uiCanExit(trans));
1923 promise.then(function (val) { return (ids[id_1] = val !== false); });
1924 }
1925 return promise;
1926 };
1927 var criteria = { exiting: viewState.name };
1928 $scope.$on('$destroy', $transitions.onBefore(criteria, wrappedHook, hookOptions));
1929 }
1930 }
1931 ng.module('ui.router.state').directive('uiView', uiView);
1932 ng.module('ui.router.state').directive('uiView', $ViewDirectiveFill);
1933
1934 /** @publicapi @module ng1 */ /** */
1935 /** @hidden */
1936 function $ViewScrollProvider() {
1937 var useAnchorScroll = false;
1938 this.useAnchorScroll = function () {
1939 useAnchorScroll = true;
1940 };
1941 this.$get = [
1942 '$anchorScroll',
1943 '$timeout',
1944 function ($anchorScroll, $timeout) {
1945 if (useAnchorScroll) {
1946 return $anchorScroll;
1947 }
1948 return function ($element) {
1949 return $timeout(function () {
1950 $element[0].scrollIntoView();
1951 }, 0, false);
1952 };
1953 },
1954 ];
1955 }
1956 ng.module('ui.router.state').provider('$uiViewScroll', $ViewScrollProvider);
1957
1958 /**
1959 * Main entry point for angular 1.x build
1960 * @publicapi @module ng1
1961 */ /** */
1962 var index = 'ui.router';
1963
1964 Object.keys(core).forEach(function (k) {
1965 if (k !== 'default') Object.defineProperty(exports, k, {
1966 enumerable: true,
1967 get: function () {
1968 return core[k];
1969 }
1970 });
1971 });
1972 exports.core = core;
1973 exports.Ng1ViewConfig = Ng1ViewConfig;
1974 exports.StateProvider = StateProvider;
1975 exports.UrlRouterProvider = UrlRouterProvider;
1976 exports.default = index;
1977 exports.getLocals = getLocals;
1978 exports.getNg1ViewConfigFactory = getNg1ViewConfigFactory;
1979 exports.ng1ViewsBuilder = ng1ViewsBuilder;
1980 exports.watchDigests = watchDigests;
1981
1982 Object.defineProperty(exports, '__esModule', { value: true });
1983
1984})));
1985//# sourceMappingURL=ui-router-angularjs.js.map