interval-braining-ui-asset-pack 0.0.1 → 0.1.0

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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/lib/asset_pack/engine.rb +5 -2
  3. data/lib/asset_pack/version.rb +1 -1
  4. data/vendor/assets/bower_components/angular/angular.js +23377 -0
  5. data/vendor/assets/bower_components/angular-animate/angular-animate.js +1729 -0
  6. data/vendor/assets/bower_components/angular-growl-notifications/dist/growl-notifications.js +241 -0
  7. data/vendor/assets/bower_components/angular-input-match/dist/angular-input-match.js +39 -0
  8. data/vendor/assets/bower_components/angular-messages/angular-messages.js +400 -0
  9. data/vendor/assets/bower_components/angular-mocks/angular-mocks.js +2228 -0
  10. data/vendor/assets/bower_components/angular-resource/angular-resource.js +660 -0
  11. data/vendor/assets/bower_components/angular-sanitize/angular-sanitize.js +640 -0
  12. data/vendor/assets/bower_components/angular-ui-router/release/angular-ui-router.js +3223 -0
  13. data/vendor/assets/bower_components/angular-ui-router-breadcrumbs/dist/angular-ui-router-breadcrumbs.js +82 -0
  14. data/vendor/assets/bower_components/angular-ui-router-helpers/dist/angular-ui-router-helpers.js +52 -0
  15. data/vendor/assets/bower_components/angular-ui-router-hooks-before-state/dist/angular-ui-router-hooks-before-state.js +56 -0
  16. data/vendor/assets/bower_components/angular-validate-in-set/dist/angular-validate-in-set.js +77 -0
  17. data/vendor/assets/bower_components/animate.css/animate.css +3125 -0
  18. data/vendor/assets/bower_components/bootstrap/dist/css/bootstrap.css +5785 -0
  19. data/vendor/assets/bower_components/bootstrap/dist/fonts/glyphicons-halflings-regular.eot +0 -0
  20. data/vendor/assets/bower_components/bootstrap/dist/fonts/glyphicons-halflings-regular.svg +229 -0
  21. data/vendor/assets/bower_components/bootstrap/dist/fonts/glyphicons-halflings-regular.ttf +0 -0
  22. data/vendor/assets/bower_components/bootstrap/dist/fonts/glyphicons-halflings-regular.woff +0 -0
  23. data/vendor/assets/bower_components/bootstrap/dist/js/bootstrap.js +1951 -0
  24. data/vendor/assets/bower_components/cardigan/dist/cardigan.css +37 -0
  25. data/vendor/assets/bower_components/cardigan/dist/cardigan.js +239 -0
  26. data/vendor/assets/bower_components/font-awesome-bower/css/font-awesome.css +1338 -0
  27. data/vendor/assets/bower_components/font-awesome-bower/fonts/FontAwesome.otf +0 -0
  28. data/vendor/assets/bower_components/font-awesome-bower/fonts/fontawesome-webfont.eot +0 -0
  29. data/vendor/assets/bower_components/font-awesome-bower/fonts/fontawesome-webfont.svg +414 -0
  30. data/vendor/assets/bower_components/font-awesome-bower/fonts/fontawesome-webfont.ttf +0 -0
  31. data/vendor/assets/bower_components/font-awesome-bower/fonts/fontawesome-webfont.woff +0 -0
  32. data/vendor/assets/bower_components/jquery/dist/jquery.js +9190 -0
  33. data/vendor/assets/bower_components/webfont-OpenSans-Light/dist/OpenSans-Light.css +10 -0
  34. data/vendor/assets/bower_components/webfont-OpenSans-Light/dist/OpenSans-Light.eot +0 -0
  35. data/vendor/assets/bower_components/webfont-OpenSans-Light/dist/OpenSans-Light.svg +1831 -0
  36. data/vendor/assets/bower_components/webfont-OpenSans-Light/dist/OpenSans-Light.ttf +0 -0
  37. data/vendor/assets/bower_components/webfont-OpenSans-Light/dist/OpenSans-Light.woff +0 -0
  38. metadata +36 -2
@@ -0,0 +1,3223 @@
1
+ /**
2
+ * State-based routing for AngularJS
3
+ * @version v0.2.10
4
+ * @link http://angular-ui.github.com/
5
+ * @license MIT License, http://www.opensource.org/licenses/MIT
6
+ */
7
+
8
+ /* commonjs package manager support (eg componentjs) */
9
+ if (typeof module !== "undefined" && typeof exports !== "undefined" && module.exports === exports){
10
+ module.exports = 'ui.router';
11
+ }
12
+
13
+ (function (window, angular, undefined) {
14
+ /*jshint globalstrict:true*/
15
+ /*global angular:false*/
16
+ 'use strict';
17
+
18
+ var isDefined = angular.isDefined,
19
+ isFunction = angular.isFunction,
20
+ isString = angular.isString,
21
+ isObject = angular.isObject,
22
+ isArray = angular.isArray,
23
+ forEach = angular.forEach,
24
+ extend = angular.extend,
25
+ copy = angular.copy;
26
+
27
+ function inherit(parent, extra) {
28
+ return extend(new (extend(function() {}, { prototype: parent }))(), extra);
29
+ }
30
+
31
+ function merge(dst) {
32
+ forEach(arguments, function(obj) {
33
+ if (obj !== dst) {
34
+ forEach(obj, function(value, key) {
35
+ if (!dst.hasOwnProperty(key)) dst[key] = value;
36
+ });
37
+ }
38
+ });
39
+ return dst;
40
+ }
41
+
42
+ /**
43
+ * Finds the common ancestor path between two states.
44
+ *
45
+ * @param {Object} first The first state.
46
+ * @param {Object} second The second state.
47
+ * @return {Array} Returns an array of state names in descending order, not including the root.
48
+ */
49
+ function ancestors(first, second) {
50
+ var path = [];
51
+
52
+ for (var n in first.path) {
53
+ if (first.path[n] !== second.path[n]) break;
54
+ path.push(first.path[n]);
55
+ }
56
+ return path;
57
+ }
58
+
59
+ /**
60
+ * IE8-safe wrapper for `Object.keys()`.
61
+ *
62
+ * @param {Object} object A JavaScript object.
63
+ * @return {Array} Returns the keys of the object as an array.
64
+ */
65
+ function keys(object) {
66
+ if (Object.keys) {
67
+ return Object.keys(object);
68
+ }
69
+ var result = [];
70
+
71
+ angular.forEach(object, function(val, key) {
72
+ result.push(key);
73
+ });
74
+ return result;
75
+ }
76
+
77
+ /**
78
+ * IE8-safe wrapper for `Array.prototype.indexOf()`.
79
+ *
80
+ * @param {Array} array A JavaScript array.
81
+ * @param {*} value A value to search the array for.
82
+ * @return {Number} Returns the array index value of `value`, or `-1` if not present.
83
+ */
84
+ function arraySearch(array, value) {
85
+ if (Array.prototype.indexOf) {
86
+ return array.indexOf(value, Number(arguments[2]) || 0);
87
+ }
88
+ var len = array.length >>> 0, from = Number(arguments[2]) || 0;
89
+ from = (from < 0) ? Math.ceil(from) : Math.floor(from);
90
+
91
+ if (from < 0) from += len;
92
+
93
+ for (; from < len; from++) {
94
+ if (from in array && array[from] === value) return from;
95
+ }
96
+ return -1;
97
+ }
98
+
99
+ /**
100
+ * Merges a set of parameters with all parameters inherited between the common parents of the
101
+ * current state and a given destination state.
102
+ *
103
+ * @param {Object} currentParams The value of the current state parameters ($stateParams).
104
+ * @param {Object} newParams The set of parameters which will be composited with inherited params.
105
+ * @param {Object} $current Internal definition of object representing the current state.
106
+ * @param {Object} $to Internal definition of object representing state to transition to.
107
+ */
108
+ function inheritParams(currentParams, newParams, $current, $to) {
109
+ var parents = ancestors($current, $to), parentParams, inherited = {}, inheritList = [];
110
+
111
+ for (var i in parents) {
112
+ if (!parents[i].params || !parents[i].params.length) continue;
113
+ parentParams = parents[i].params;
114
+
115
+ for (var j in parentParams) {
116
+ if (arraySearch(inheritList, parentParams[j]) >= 0) continue;
117
+ inheritList.push(parentParams[j]);
118
+ inherited[parentParams[j]] = currentParams[parentParams[j]];
119
+ }
120
+ }
121
+ return extend({}, inherited, newParams);
122
+ }
123
+
124
+ /**
125
+ * Normalizes a set of values to string or `null`, filtering them by a list of keys.
126
+ *
127
+ * @param {Array} keys The list of keys to normalize/return.
128
+ * @param {Object} values An object hash of values to normalize.
129
+ * @return {Object} Returns an object hash of normalized string values.
130
+ */
131
+ function normalize(keys, values) {
132
+ var normalized = {};
133
+
134
+ forEach(keys, function (name) {
135
+ var value = values[name];
136
+ normalized[name] = (value != null) ? String(value) : null;
137
+ });
138
+ return normalized;
139
+ }
140
+
141
+ /**
142
+ * Performs a non-strict comparison of the subset of two objects, defined by a list of keys.
143
+ *
144
+ * @param {Object} a The first object.
145
+ * @param {Object} b The second object.
146
+ * @param {Array} keys The list of keys within each object to compare. If the list is empty or not specified,
147
+ * it defaults to the list of keys in `a`.
148
+ * @return {Boolean} Returns `true` if the keys match, otherwise `false`.
149
+ */
150
+ function equalForKeys(a, b, keys) {
151
+ if (!keys) {
152
+ keys = [];
153
+ for (var n in a) keys.push(n); // Used instead of Object.keys() for IE8 compatibility
154
+ }
155
+
156
+ for (var i=0; i<keys.length; i++) {
157
+ var k = keys[i];
158
+ if (a[k] != b[k]) return false; // Not '===', values aren't necessarily normalized
159
+ }
160
+ return true;
161
+ }
162
+
163
+ /**
164
+ * Returns the subset of an object, based on a list of keys.
165
+ *
166
+ * @param {Array} keys
167
+ * @param {Object} values
168
+ * @return {Boolean} Returns a subset of `values`.
169
+ */
170
+ function filterByKeys(keys, values) {
171
+ var filtered = {};
172
+
173
+ forEach(keys, function (name) {
174
+ filtered[name] = values[name];
175
+ });
176
+ return filtered;
177
+ }
178
+ /**
179
+ * @ngdoc overview
180
+ * @name ui.router.util
181
+ *
182
+ * @description
183
+ * # ui.router.util sub-module
184
+ *
185
+ * This module is a dependency of other sub-modules. Do not include this module as a dependency
186
+ * in your angular app (use {@link ui.router} module instead).
187
+ *
188
+ */
189
+ angular.module('ui.router.util', ['ng']);
190
+
191
+ /**
192
+ * @ngdoc overview
193
+ * @name ui.router.router
194
+ *
195
+ * @requires ui.router.util
196
+ *
197
+ * @description
198
+ * # ui.router.router sub-module
199
+ *
200
+ * This module is a dependency of other sub-modules. Do not include this module as a dependency
201
+ * in your angular app (use {@link ui.router} module instead).
202
+ */
203
+ angular.module('ui.router.router', ['ui.router.util']);
204
+
205
+ /**
206
+ * @ngdoc overview
207
+ * @name ui.router.state
208
+ *
209
+ * @requires ui.router.router
210
+ * @requires ui.router.util
211
+ *
212
+ * @description
213
+ * # ui.router.state sub-module
214
+ *
215
+ * This module is a dependency of the main ui.router module. Do not include this module as a dependency
216
+ * in your angular app (use {@link ui.router} module instead).
217
+ *
218
+ */
219
+ angular.module('ui.router.state', ['ui.router.router', 'ui.router.util']);
220
+
221
+ /**
222
+ * @ngdoc overview
223
+ * @name ui.router
224
+ *
225
+ * @requires ui.router.state
226
+ *
227
+ * @description
228
+ * # ui.router
229
+ *
230
+ * ## The main module for ui.router
231
+ * There are several sub-modules included with the ui.router module, however only this module is needed
232
+ * as a dependency within your angular app. The other modules are for organization purposes.
233
+ *
234
+ * The modules are:
235
+ * * ui.router - the main "umbrella" module
236
+ * * ui.router.router -
237
+ *
238
+ * *You'll need to include **only** this module as the dependency within your angular app.*
239
+ *
240
+ * <pre>
241
+ * <!doctype html>
242
+ * <html ng-app="myApp">
243
+ * <head>
244
+ * <script src="js/angular.js"></script>
245
+ * <!-- Include the ui-router script -->
246
+ * <script src="js/angular-ui-router.min.js"></script>
247
+ * <script>
248
+ * // ...and add 'ui.router' as a dependency
249
+ * var myApp = angular.module('myApp', ['ui.router']);
250
+ * </script>
251
+ * </head>
252
+ * <body>
253
+ * </body>
254
+ * </html>
255
+ * </pre>
256
+ */
257
+ angular.module('ui.router', ['ui.router.state']);
258
+
259
+ angular.module('ui.router.compat', ['ui.router']);
260
+
261
+ /**
262
+ * @ngdoc object
263
+ * @name ui.router.util.$resolve
264
+ *
265
+ * @requires $q
266
+ * @requires $injector
267
+ *
268
+ * @description
269
+ * Manages resolution of (acyclic) graphs of promises.
270
+ */
271
+ $Resolve.$inject = ['$q', '$injector'];
272
+ function $Resolve( $q, $injector) {
273
+
274
+ var VISIT_IN_PROGRESS = 1,
275
+ VISIT_DONE = 2,
276
+ NOTHING = {},
277
+ NO_DEPENDENCIES = [],
278
+ NO_LOCALS = NOTHING,
279
+ NO_PARENT = extend($q.when(NOTHING), { $$promises: NOTHING, $$values: NOTHING });
280
+
281
+
282
+ /**
283
+ * @ngdoc function
284
+ * @name ui.router.util.$resolve#study
285
+ * @methodOf ui.router.util.$resolve
286
+ *
287
+ * @description
288
+ * Studies a set of invocables that are likely to be used multiple times.
289
+ * <pre>
290
+ * $resolve.study(invocables)(locals, parent, self)
291
+ * </pre>
292
+ * is equivalent to
293
+ * <pre>
294
+ * $resolve.resolve(invocables, locals, parent, self)
295
+ * </pre>
296
+ * but the former is more efficient (in fact `resolve` just calls `study`
297
+ * internally).
298
+ *
299
+ * @param {object} invocables Invocable objects
300
+ * @return {function} a function to pass in locals, parent and self
301
+ */
302
+ this.study = function (invocables) {
303
+ if (!isObject(invocables)) throw new Error("'invocables' must be an object");
304
+
305
+ // Perform a topological sort of invocables to build an ordered plan
306
+ var plan = [], cycle = [], visited = {};
307
+ function visit(value, key) {
308
+ if (visited[key] === VISIT_DONE) return;
309
+
310
+ cycle.push(key);
311
+ if (visited[key] === VISIT_IN_PROGRESS) {
312
+ cycle.splice(0, cycle.indexOf(key));
313
+ throw new Error("Cyclic dependency: " + cycle.join(" -> "));
314
+ }
315
+ visited[key] = VISIT_IN_PROGRESS;
316
+
317
+ if (isString(value)) {
318
+ plan.push(key, [ function() { return $injector.get(value); }], NO_DEPENDENCIES);
319
+ } else {
320
+ var params = $injector.annotate(value);
321
+ forEach(params, function (param) {
322
+ if (param !== key && invocables.hasOwnProperty(param)) visit(invocables[param], param);
323
+ });
324
+ plan.push(key, value, params);
325
+ }
326
+
327
+ cycle.pop();
328
+ visited[key] = VISIT_DONE;
329
+ }
330
+ forEach(invocables, visit);
331
+ invocables = cycle = visited = null; // plan is all that's required
332
+
333
+ function isResolve(value) {
334
+ return isObject(value) && value.then && value.$$promises;
335
+ }
336
+
337
+ return function (locals, parent, self) {
338
+ if (isResolve(locals) && self === undefined) {
339
+ self = parent; parent = locals; locals = null;
340
+ }
341
+ if (!locals) locals = NO_LOCALS;
342
+ else if (!isObject(locals)) {
343
+ throw new Error("'locals' must be an object");
344
+ }
345
+ if (!parent) parent = NO_PARENT;
346
+ else if (!isResolve(parent)) {
347
+ throw new Error("'parent' must be a promise returned by $resolve.resolve()");
348
+ }
349
+
350
+ // To complete the overall resolution, we have to wait for the parent
351
+ // promise and for the promise for each invokable in our plan.
352
+ var resolution = $q.defer(),
353
+ result = resolution.promise,
354
+ promises = result.$$promises = {},
355
+ values = extend({}, locals),
356
+ wait = 1 + plan.length/3,
357
+ merged = false;
358
+
359
+ function done() {
360
+ // Merge parent values we haven't got yet and publish our own $$values
361
+ if (!--wait) {
362
+ if (!merged) merge(values, parent.$$values);
363
+ result.$$values = values;
364
+ result.$$promises = true; // keep for isResolve()
365
+ resolution.resolve(values);
366
+ }
367
+ }
368
+
369
+ function fail(reason) {
370
+ result.$$failure = reason;
371
+ resolution.reject(reason);
372
+ }
373
+
374
+ // Short-circuit if parent has already failed
375
+ if (isDefined(parent.$$failure)) {
376
+ fail(parent.$$failure);
377
+ return result;
378
+ }
379
+
380
+ // Merge parent values if the parent has already resolved, or merge
381
+ // parent promises and wait if the parent resolve is still in progress.
382
+ if (parent.$$values) {
383
+ merged = merge(values, parent.$$values);
384
+ done();
385
+ } else {
386
+ extend(promises, parent.$$promises);
387
+ parent.then(done, fail);
388
+ }
389
+
390
+ // Process each invocable in the plan, but ignore any where a local of the same name exists.
391
+ for (var i=0, ii=plan.length; i<ii; i+=3) {
392
+ if (locals.hasOwnProperty(plan[i])) done();
393
+ else invoke(plan[i], plan[i+1], plan[i+2]);
394
+ }
395
+
396
+ function invoke(key, invocable, params) {
397
+ // Create a deferred for this invocation. Failures will propagate to the resolution as well.
398
+ var invocation = $q.defer(), waitParams = 0;
399
+ function onfailure(reason) {
400
+ invocation.reject(reason);
401
+ fail(reason);
402
+ }
403
+ // Wait for any parameter that we have a promise for (either from parent or from this
404
+ // resolve; in that case study() will have made sure it's ordered before us in the plan).
405
+ forEach(params, function (dep) {
406
+ if (promises.hasOwnProperty(dep) && !locals.hasOwnProperty(dep)) {
407
+ waitParams++;
408
+ promises[dep].then(function (result) {
409
+ values[dep] = result;
410
+ if (!(--waitParams)) proceed();
411
+ }, onfailure);
412
+ }
413
+ });
414
+ if (!waitParams) proceed();
415
+ function proceed() {
416
+ if (isDefined(result.$$failure)) return;
417
+ try {
418
+ invocation.resolve($injector.invoke(invocable, self, values));
419
+ invocation.promise.then(function (result) {
420
+ values[key] = result;
421
+ done();
422
+ }, onfailure);
423
+ } catch (e) {
424
+ onfailure(e);
425
+ }
426
+ }
427
+ // Publish promise synchronously; invocations further down in the plan may depend on it.
428
+ promises[key] = invocation.promise;
429
+ }
430
+
431
+ return result;
432
+ };
433
+ };
434
+
435
+ /**
436
+ * @ngdoc function
437
+ * @name ui.router.util.$resolve#resolve
438
+ * @methodOf ui.router.util.$resolve
439
+ *
440
+ * @description
441
+ * Resolves a set of invocables. An invocable is a function to be invoked via
442
+ * `$injector.invoke()`, and can have an arbitrary number of dependencies.
443
+ * An invocable can either return a value directly,
444
+ * or a `$q` promise. If a promise is returned it will be resolved and the
445
+ * resulting value will be used instead. Dependencies of invocables are resolved
446
+ * (in this order of precedence)
447
+ *
448
+ * - from the specified `locals`
449
+ * - from another invocable that is part of this `$resolve` call
450
+ * - from an invocable that is inherited from a `parent` call to `$resolve`
451
+ * (or recursively
452
+ * - from any ancestor `$resolve` of that parent).
453
+ *
454
+ * The return value of `$resolve` is a promise for an object that contains
455
+ * (in this order of precedence)
456
+ *
457
+ * - any `locals` (if specified)
458
+ * - the resolved return values of all injectables
459
+ * - any values inherited from a `parent` call to `$resolve` (if specified)
460
+ *
461
+ * The promise will resolve after the `parent` promise (if any) and all promises
462
+ * returned by injectables have been resolved. If any invocable
463
+ * (or `$injector.invoke`) throws an exception, or if a promise returned by an
464
+ * invocable is rejected, the `$resolve` promise is immediately rejected with the
465
+ * same error. A rejection of a `parent` promise (if specified) will likewise be
466
+ * propagated immediately. Once the `$resolve` promise has been rejected, no
467
+ * further invocables will be called.
468
+ *
469
+ * Cyclic dependencies between invocables are not permitted and will caues `$resolve`
470
+ * to throw an error. As a special case, an injectable can depend on a parameter
471
+ * with the same name as the injectable, which will be fulfilled from the `parent`
472
+ * injectable of the same name. This allows inherited values to be decorated.
473
+ * Note that in this case any other injectable in the same `$resolve` with the same
474
+ * dependency would see the decorated value, not the inherited value.
475
+ *
476
+ * Note that missing dependencies -- unlike cyclic dependencies -- will cause an
477
+ * (asynchronous) rejection of the `$resolve` promise rather than a (synchronous)
478
+ * exception.
479
+ *
480
+ * Invocables are invoked eagerly as soon as all dependencies are available.
481
+ * This is true even for dependencies inherited from a `parent` call to `$resolve`.
482
+ *
483
+ * As a special case, an invocable can be a string, in which case it is taken to
484
+ * be a service name to be passed to `$injector.get()`. This is supported primarily
485
+ * for backwards-compatibility with the `resolve` property of `$routeProvider`
486
+ * routes.
487
+ *
488
+ * @param {object} invocables functions to invoke or
489
+ * `$injector` services to fetch.
490
+ * @param {object} locals values to make available to the injectables
491
+ * @param {object} parent a promise returned by another call to `$resolve`.
492
+ * @param {object} self the `this` for the invoked methods
493
+ * @return {object} Promise for an object that contains the resolved return value
494
+ * of all invocables, as well as any inherited and local values.
495
+ */
496
+ this.resolve = function (invocables, locals, parent, self) {
497
+ return this.study(invocables)(locals, parent, self);
498
+ };
499
+ }
500
+
501
+ angular.module('ui.router.util').service('$resolve', $Resolve);
502
+
503
+
504
+ /**
505
+ * @ngdoc object
506
+ * @name ui.router.util.$templateFactory
507
+ *
508
+ * @requires $http
509
+ * @requires $templateCache
510
+ * @requires $injector
511
+ *
512
+ * @description
513
+ * Service. Manages loading of templates.
514
+ */
515
+ $TemplateFactory.$inject = ['$http', '$templateCache', '$injector'];
516
+ function $TemplateFactory( $http, $templateCache, $injector) {
517
+
518
+ /**
519
+ * @ngdoc function
520
+ * @name ui.router.util.$templateFactory#fromConfig
521
+ * @methodOf ui.router.util.$templateFactory
522
+ *
523
+ * @description
524
+ * Creates a template from a configuration object.
525
+ *
526
+ * @param {object} config Configuration object for which to load a template.
527
+ * The following properties are search in the specified order, and the first one
528
+ * that is defined is used to create the template:
529
+ *
530
+ * @param {string|object} config.template html string template or function to
531
+ * load via {@link ui.router.util.$templateFactory#fromString fromString}.
532
+ * @param {string|object} config.templateUrl url to load or a function returning
533
+ * the url to load via {@link ui.router.util.$templateFactory#fromUrl fromUrl}.
534
+ * @param {Function} config.templateProvider function to invoke via
535
+ * {@link ui.router.util.$templateFactory#fromProvider fromProvider}.
536
+ * @param {object} params Parameters to pass to the template function.
537
+ * @param {object} locals Locals to pass to `invoke` if the template is loaded
538
+ * via a `templateProvider`. Defaults to `{ params: params }`.
539
+ *
540
+ * @return {string|object} The template html as a string, or a promise for
541
+ * that string,or `null` if no template is configured.
542
+ */
543
+ this.fromConfig = function (config, params, locals) {
544
+ return (
545
+ isDefined(config.template) ? this.fromString(config.template, params) :
546
+ isDefined(config.templateUrl) ? this.fromUrl(config.templateUrl, params) :
547
+ isDefined(config.templateProvider) ? this.fromProvider(config.templateProvider, params, locals) :
548
+ null
549
+ );
550
+ };
551
+
552
+ /**
553
+ * @ngdoc function
554
+ * @name ui.router.util.$templateFactory#fromString
555
+ * @methodOf ui.router.util.$templateFactory
556
+ *
557
+ * @description
558
+ * Creates a template from a string or a function returning a string.
559
+ *
560
+ * @param {string|object} template html template as a string or function that
561
+ * returns an html template as a string.
562
+ * @param {object} params Parameters to pass to the template function.
563
+ *
564
+ * @return {string|object} The template html as a string, or a promise for that
565
+ * string.
566
+ */
567
+ this.fromString = function (template, params) {
568
+ return isFunction(template) ? template(params) : template;
569
+ };
570
+
571
+ /**
572
+ * @ngdoc function
573
+ * @name ui.router.util.$templateFactory#fromUrl
574
+ * @methodOf ui.router.util.$templateFactory
575
+ *
576
+ * @description
577
+ * Loads a template from the a URL via `$http` and `$templateCache`.
578
+ *
579
+ * @param {string|Function} url url of the template to load, or a function
580
+ * that returns a url.
581
+ * @param {Object} params Parameters to pass to the url function.
582
+ * @return {string|Promise.<string>} The template html as a string, or a promise
583
+ * for that string.
584
+ */
585
+ this.fromUrl = function (url, params) {
586
+ if (isFunction(url)) url = url(params);
587
+ if (url == null) return null;
588
+ else return $http
589
+ .get(url, { cache: $templateCache })
590
+ .then(function(response) { return response.data; });
591
+ };
592
+
593
+ /**
594
+ * @ngdoc function
595
+ * @name ui.router.util.$templateFactory#fromUrl
596
+ * @methodOf ui.router.util.$templateFactory
597
+ *
598
+ * @description
599
+ * Creates a template by invoking an injectable provider function.
600
+ *
601
+ * @param {Function} provider Function to invoke via `$injector.invoke`
602
+ * @param {Object} params Parameters for the template.
603
+ * @param {Object} locals Locals to pass to `invoke`. Defaults to
604
+ * `{ params: params }`.
605
+ * @return {string|Promise.<string>} The template html as a string, or a promise
606
+ * for that string.
607
+ */
608
+ this.fromProvider = function (provider, params, locals) {
609
+ return $injector.invoke(provider, null, locals || { params: params });
610
+ };
611
+ }
612
+
613
+ angular.module('ui.router.util').service('$templateFactory', $TemplateFactory);
614
+
615
+ /**
616
+ * @ngdoc object
617
+ * @name ui.router.util.type:UrlMatcher
618
+ *
619
+ * @description
620
+ * Matches URLs against patterns and extracts named parameters from the path or the search
621
+ * part of the URL. A URL pattern consists of a path pattern, optionally followed by '?' and a list
622
+ * of search parameters. Multiple search parameter names are separated by '&'. Search parameters
623
+ * do not influence whether or not a URL is matched, but their values are passed through into
624
+ * the matched parameters returned by {@link ui.router.util.type:UrlMatcher#methods_exec exec}.
625
+ *
626
+ * Path parameter placeholders can be specified using simple colon/catch-all syntax or curly brace
627
+ * syntax, which optionally allows a regular expression for the parameter to be specified:
628
+ *
629
+ * * `':'` name - colon placeholder
630
+ * * `'*'` name - catch-all placeholder
631
+ * * `'{' name '}'` - curly placeholder
632
+ * * `'{' name ':' regexp '}'` - curly placeholder with regexp. Should the regexp itself contain
633
+ * curly braces, they must be in matched pairs or escaped with a backslash.
634
+ *
635
+ * Parameter names may contain only word characters (latin letters, digits, and underscore) and
636
+ * must be unique within the pattern (across both path and search parameters). For colon
637
+ * placeholders or curly placeholders without an explicit regexp, a path parameter matches any
638
+ * number of characters other than '/'. For catch-all placeholders the path parameter matches
639
+ * any number of characters.
640
+ *
641
+ * Examples:
642
+ *
643
+ * * `'/hello/'` - Matches only if the path is exactly '/hello/'. There is no special treatment for
644
+ * trailing slashes, and patterns have to match the entire path, not just a prefix.
645
+ * * `'/user/:id'` - Matches '/user/bob' or '/user/1234!!!' or even '/user/' but not '/user' or
646
+ * '/user/bob/details'. The second path segment will be captured as the parameter 'id'.
647
+ * * `'/user/{id}'` - Same as the previous example, but using curly brace syntax.
648
+ * * `'/user/{id:[^/]*}'` - Same as the previous example.
649
+ * * `'/user/{id:[0-9a-fA-F]{1,8}}'` - Similar to the previous example, but only matches if the id
650
+ * parameter consists of 1 to 8 hex digits.
651
+ * * `'/files/{path:.*}'` - Matches any URL starting with '/files/' and captures the rest of the
652
+ * path into the parameter 'path'.
653
+ * * `'/files/*path'` - ditto.
654
+ *
655
+ * @param {string} pattern the pattern to compile into a matcher.
656
+ *
657
+ * @property {string} prefix A static prefix of this pattern. The matcher guarantees that any
658
+ * URL matching this matcher (i.e. any string for which {@link ui.router.util.type:UrlMatcher#methods_exec exec()} returns
659
+ * non-null) will start with this prefix.
660
+ *
661
+ * @property {string} source The pattern that was passed into the contructor
662
+ *
663
+ * @property {string} sourcePath The path portion of the source property
664
+ *
665
+ * @property {string} sourceSearch The search portion of the source property
666
+ *
667
+ * @property {string} regex The constructed regex that will be used to match against the url when
668
+ * it is time to determine which url will match.
669
+ *
670
+ * @returns {Object} New UrlMatcher object
671
+ */
672
+ function UrlMatcher(pattern) {
673
+
674
+ // Find all placeholders and create a compiled pattern, using either classic or curly syntax:
675
+ // '*' name
676
+ // ':' name
677
+ // '{' name '}'
678
+ // '{' name ':' regexp '}'
679
+ // The regular expression is somewhat complicated due to the need to allow curly braces
680
+ // inside the regular expression. The placeholder regexp breaks down as follows:
681
+ // ([:*])(\w+) classic placeholder ($1 / $2)
682
+ // \{(\w+)(?:\:( ... ))?\} curly brace placeholder ($3) with optional regexp ... ($4)
683
+ // (?: ... | ... | ... )+ the regexp consists of any number of atoms, an atom being either
684
+ // [^{}\\]+ - anything other than curly braces or backslash
685
+ // \\. - a backslash escape
686
+ // \{(?:[^{}\\]+|\\.)*\} - a matched set of curly braces containing other atoms
687
+ var placeholder = /([:*])(\w+)|\{(\w+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,
688
+ names = {}, compiled = '^', last = 0, m,
689
+ segments = this.segments = [],
690
+ params = this.params = [];
691
+
692
+ function addParameter(id) {
693
+ if (!/^\w+(-+\w+)*$/.test(id)) throw new Error("Invalid parameter name '" + id + "' in pattern '" + pattern + "'");
694
+ if (names[id]) throw new Error("Duplicate parameter name '" + id + "' in pattern '" + pattern + "'");
695
+ names[id] = true;
696
+ params.push(id);
697
+ }
698
+
699
+ function quoteRegExp(string) {
700
+ return string.replace(/[\\\[\]\^$*+?.()|{}]/g, "\\$&");
701
+ }
702
+
703
+ this.source = pattern;
704
+
705
+ // Split into static segments separated by path parameter placeholders.
706
+ // The number of segments is always 1 more than the number of parameters.
707
+ var id, regexp, segment;
708
+ while ((m = placeholder.exec(pattern))) {
709
+ id = m[2] || m[3]; // IE[78] returns '' for unmatched groups instead of null
710
+ regexp = m[4] || (m[1] == '*' ? '.*' : '[^/]*');
711
+ segment = pattern.substring(last, m.index);
712
+ if (segment.indexOf('?') >= 0) break; // we're into the search part
713
+ compiled += quoteRegExp(segment) + '(' + regexp + ')';
714
+ addParameter(id);
715
+ segments.push(segment);
716
+ last = placeholder.lastIndex;
717
+ }
718
+ segment = pattern.substring(last);
719
+
720
+ // Find any search parameter names and remove them from the last segment
721
+ var i = segment.indexOf('?');
722
+ if (i >= 0) {
723
+ var search = this.sourceSearch = segment.substring(i);
724
+ segment = segment.substring(0, i);
725
+ this.sourcePath = pattern.substring(0, last+i);
726
+
727
+ // Allow parameters to be separated by '?' as well as '&' to make concat() easier
728
+ forEach(search.substring(1).split(/[&?]/), addParameter);
729
+ } else {
730
+ this.sourcePath = pattern;
731
+ this.sourceSearch = '';
732
+ }
733
+
734
+ compiled += quoteRegExp(segment) + '$';
735
+ segments.push(segment);
736
+ this.regexp = new RegExp(compiled);
737
+ this.prefix = segments[0];
738
+ }
739
+
740
+ /**
741
+ * @ngdoc function
742
+ * @name ui.router.util.type:UrlMatcher#concat
743
+ * @methodOf ui.router.util.type:UrlMatcher
744
+ *
745
+ * @description
746
+ * Returns a new matcher for a pattern constructed by appending the path part and adding the
747
+ * search parameters of the specified pattern to this pattern. The current pattern is not
748
+ * modified. This can be understood as creating a pattern for URLs that are relative to (or
749
+ * suffixes of) the current pattern.
750
+ *
751
+ * @example
752
+ * The following two matchers are equivalent:
753
+ * ```
754
+ * new UrlMatcher('/user/{id}?q').concat('/details?date');
755
+ * new UrlMatcher('/user/{id}/details?q&date');
756
+ * ```
757
+ *
758
+ * @param {string} pattern The pattern to append.
759
+ * @returns {ui.router.util.type:UrlMatcher} A matcher for the concatenated pattern.
760
+ */
761
+ UrlMatcher.prototype.concat = function (pattern) {
762
+ // Because order of search parameters is irrelevant, we can add our own search
763
+ // parameters to the end of the new pattern. Parse the new pattern by itself
764
+ // and then join the bits together, but it's much easier to do this on a string level.
765
+ return new UrlMatcher(this.sourcePath + pattern + this.sourceSearch);
766
+ };
767
+
768
+ UrlMatcher.prototype.toString = function () {
769
+ return this.source;
770
+ };
771
+
772
+ /**
773
+ * @ngdoc function
774
+ * @name ui.router.util.type:UrlMatcher#exec
775
+ * @methodOf ui.router.util.type:UrlMatcher
776
+ *
777
+ * @description
778
+ * Tests the specified path against this matcher, and returns an object containing the captured
779
+ * parameter values, or null if the path does not match. The returned object contains the values
780
+ * of any search parameters that are mentioned in the pattern, but their value may be null if
781
+ * they are not present in `searchParams`. This means that search parameters are always treated
782
+ * as optional.
783
+ *
784
+ * @example
785
+ * ```
786
+ * new UrlMatcher('/user/{id}?q&r').exec('/user/bob', { x:'1', q:'hello' });
787
+ * // returns { id:'bob', q:'hello', r:null }
788
+ * ```
789
+ *
790
+ * @param {string} path The URL path to match, e.g. `$location.path()`.
791
+ * @param {Object} searchParams URL search parameters, e.g. `$location.search()`.
792
+ * @returns {Object} The captured parameter values.
793
+ */
794
+ UrlMatcher.prototype.exec = function (path, searchParams) {
795
+ var m = this.regexp.exec(path);
796
+ if (!m) return null;
797
+
798
+ var params = this.params, nTotal = params.length,
799
+ nPath = this.segments.length-1,
800
+ values = {}, i;
801
+
802
+ if (nPath !== m.length - 1) throw new Error("Unbalanced capture group in route '" + this.source + "'");
803
+
804
+ for (i=0; i<nPath; i++) values[params[i]] = m[i+1];
805
+ for (/**/; i<nTotal; i++) values[params[i]] = searchParams[params[i]];
806
+
807
+ return values;
808
+ };
809
+
810
+ /**
811
+ * @ngdoc function
812
+ * @name ui.router.util.type:UrlMatcher#parameters
813
+ * @methodOf ui.router.util.type:UrlMatcher
814
+ *
815
+ * @description
816
+ * Returns the names of all path and search parameters of this pattern in an unspecified order.
817
+ *
818
+ * @returns {Array.<string>} An array of parameter names. Must be treated as read-only. If the
819
+ * pattern has no parameters, an empty array is returned.
820
+ */
821
+ UrlMatcher.prototype.parameters = function () {
822
+ return this.params;
823
+ };
824
+
825
+ /**
826
+ * @ngdoc function
827
+ * @name ui.router.util.type:UrlMatcher#format
828
+ * @methodOf ui.router.util.type:UrlMatcher
829
+ *
830
+ * @description
831
+ * Creates a URL that matches this pattern by substituting the specified values
832
+ * for the path and search parameters. Null values for path parameters are
833
+ * treated as empty strings.
834
+ *
835
+ * @example
836
+ * ```
837
+ * new UrlMatcher('/user/{id}?q').format({ id:'bob', q:'yes' });
838
+ * // returns '/user/bob?q=yes'
839
+ * ```
840
+ *
841
+ * @param {Object} values the values to substitute for the parameters in this pattern.
842
+ * @returns {string} the formatted URL (path and optionally search part).
843
+ */
844
+ UrlMatcher.prototype.format = function (values) {
845
+ var segments = this.segments, params = this.params;
846
+ if (!values) return segments.join('');
847
+
848
+ var nPath = segments.length-1, nTotal = params.length,
849
+ result = segments[0], i, search, value;
850
+
851
+ for (i=0; i<nPath; i++) {
852
+ value = values[params[i]];
853
+ // TODO: Maybe we should throw on null here? It's not really good style to use '' and null interchangeabley
854
+ if (value != null) result += encodeURIComponent(value);
855
+ result += segments[i+1];
856
+ }
857
+ for (/**/; i<nTotal; i++) {
858
+ value = values[params[i]];
859
+ if (value != null) {
860
+ result += (search ? '&' : '?') + params[i] + '=' + encodeURIComponent(value);
861
+ search = true;
862
+ }
863
+ }
864
+
865
+ return result;
866
+ };
867
+
868
+
869
+
870
+ /**
871
+ * @ngdoc object
872
+ * @name ui.router.util.$urlMatcherFactory
873
+ *
874
+ * @description
875
+ * Factory for {@link ui.router.util.type:UrlMatcher} instances. The factory is also available to providers
876
+ * under the name `$urlMatcherFactoryProvider`.
877
+ */
878
+ function $UrlMatcherFactory() {
879
+
880
+ /**
881
+ * @ngdoc function
882
+ * @name ui.router.util.$urlMatcherFactory#compile
883
+ * @methodOf ui.router.util.$urlMatcherFactory
884
+ *
885
+ * @description
886
+ * Creates a {@link ui.router.util.type:UrlMatcher} for the specified pattern.
887
+ *
888
+ * @param {string} pattern The URL pattern.
889
+ * @returns {ui.router.util.type:UrlMatcher} The UrlMatcher.
890
+ */
891
+ this.compile = function (pattern) {
892
+ return new UrlMatcher(pattern);
893
+ };
894
+
895
+ /**
896
+ * @ngdoc function
897
+ * @name ui.router.util.$urlMatcherFactory#isMatcher
898
+ * @methodOf ui.router.util.$urlMatcherFactory
899
+ *
900
+ * @description
901
+ * Returns true if the specified object is a UrlMatcher, or false otherwise.
902
+ *
903
+ * @param {Object} object The object to perform the type check against.
904
+ * @returns {Boolean} Returns `true` if the object has the following functions: `exec`, `format`, and `concat`.
905
+ */
906
+ this.isMatcher = function (o) {
907
+ return isObject(o) && isFunction(o.exec) && isFunction(o.format) && isFunction(o.concat);
908
+ };
909
+
910
+ /* No need to document $get, since it returns this */
911
+ this.$get = function () {
912
+ return this;
913
+ };
914
+ }
915
+
916
+ // Register as a provider so it's available to other providers
917
+ angular.module('ui.router.util').provider('$urlMatcherFactory', $UrlMatcherFactory);
918
+
919
+ /**
920
+ * @ngdoc object
921
+ * @name ui.router.router.$urlRouterProvider
922
+ *
923
+ * @requires ui.router.util.$urlMatcherFactoryProvider
924
+ *
925
+ * @description
926
+ * `$urlRouterProvider` has the responsibility of watching `$location`.
927
+ * When `$location` changes it runs through a list of rules one by one until a
928
+ * match is found. `$urlRouterProvider` is used behind the scenes anytime you specify
929
+ * a url in a state configuration. All urls are compiled into a UrlMatcher object.
930
+ *
931
+ * There are several methods on `$urlRouterProvider` that make it useful to use directly
932
+ * in your module config.
933
+ */
934
+ $UrlRouterProvider.$inject = ['$urlMatcherFactoryProvider'];
935
+ function $UrlRouterProvider( $urlMatcherFactory) {
936
+ var rules = [],
937
+ otherwise = null;
938
+
939
+ // Returns a string that is a prefix of all strings matching the RegExp
940
+ function regExpPrefix(re) {
941
+ var prefix = /^\^((?:\\[^a-zA-Z0-9]|[^\\\[\]\^$*+?.()|{}]+)*)/.exec(re.source);
942
+ return (prefix != null) ? prefix[1].replace(/\\(.)/g, "$1") : '';
943
+ }
944
+
945
+ // Interpolates matched values into a String.replace()-style pattern
946
+ function interpolate(pattern, match) {
947
+ return pattern.replace(/\$(\$|\d{1,2})/, function (m, what) {
948
+ return match[what === '$' ? 0 : Number(what)];
949
+ });
950
+ }
951
+
952
+ /**
953
+ * @ngdoc function
954
+ * @name ui.router.router.$urlRouterProvider#rule
955
+ * @methodOf ui.router.router.$urlRouterProvider
956
+ *
957
+ * @description
958
+ * Defines rules that are used by `$urlRouterProvider to find matches for
959
+ * specific URLs.
960
+ *
961
+ * @example
962
+ * <pre>
963
+ * var app = angular.module('app', ['ui.router.router']);
964
+ *
965
+ * app.config(function ($urlRouterProvider) {
966
+ * // Here's an example of how you might allow case insensitive urls
967
+ * $urlRouterProvider.rule(function ($injector, $location) {
968
+ * var path = $location.path(),
969
+ * normalized = path.toLowerCase();
970
+ *
971
+ * if (path !== normalized) {
972
+ * return normalized;
973
+ * }
974
+ * });
975
+ * });
976
+ * </pre>
977
+ *
978
+ * @param {object} rule Handler function that takes `$injector` and `$location`
979
+ * services as arguments. You can use them to return a valid path as a string.
980
+ *
981
+ * @return {object} $urlRouterProvider - $urlRouterProvider instance
982
+ */
983
+ this.rule =
984
+ function (rule) {
985
+ if (!isFunction(rule)) throw new Error("'rule' must be a function");
986
+ rules.push(rule);
987
+ return this;
988
+ };
989
+
990
+ /**
991
+ * @ngdoc object
992
+ * @name ui.router.router.$urlRouterProvider#otherwise
993
+ * @methodOf ui.router.router.$urlRouterProvider
994
+ *
995
+ * @description
996
+ * Defines a path that is used when an invalied route is requested.
997
+ *
998
+ * @example
999
+ * <pre>
1000
+ * var app = angular.module('app', ['ui.router.router']);
1001
+ *
1002
+ * app.config(function ($urlRouterProvider) {
1003
+ * // if the path doesn't match any of the urls you configured
1004
+ * // otherwise will take care of routing the user to the
1005
+ * // specified url
1006
+ * $urlRouterProvider.otherwise('/index');
1007
+ *
1008
+ * // Example of using function rule as param
1009
+ * $urlRouterProvider.otherwise(function ($injector, $location) {
1010
+ * ...
1011
+ * });
1012
+ * });
1013
+ * </pre>
1014
+ *
1015
+ * @param {string|object} rule The url path you want to redirect to or a function
1016
+ * rule that returns the url path. The function version is passed two params:
1017
+ * `$injector` and `$location` services.
1018
+ *
1019
+ * @return {object} $urlRouterProvider - $urlRouterProvider instance
1020
+ */
1021
+ this.otherwise =
1022
+ function (rule) {
1023
+ if (isString(rule)) {
1024
+ var redirect = rule;
1025
+ rule = function () { return redirect; };
1026
+ }
1027
+ else if (!isFunction(rule)) throw new Error("'rule' must be a function");
1028
+ otherwise = rule;
1029
+ return this;
1030
+ };
1031
+
1032
+
1033
+ function handleIfMatch($injector, handler, match) {
1034
+ if (!match) return false;
1035
+ var result = $injector.invoke(handler, handler, { $match: match });
1036
+ return isDefined(result) ? result : true;
1037
+ }
1038
+
1039
+ /**
1040
+ * @ngdoc function
1041
+ * @name ui.router.router.$urlRouterProvider#when
1042
+ * @methodOf ui.router.router.$urlRouterProvider
1043
+ *
1044
+ * @description
1045
+ * Registers a handler for a given url matching. if handle is a string, it is
1046
+ * treated as a redirect, and is interpolated according to the syyntax of match
1047
+ * (i.e. like String.replace() for RegExp, or like a UrlMatcher pattern otherwise).
1048
+ *
1049
+ * If the handler is a function, it is injectable. It gets invoked if `$location`
1050
+ * matches. You have the option of inject the match object as `$match`.
1051
+ *
1052
+ * The handler can return
1053
+ *
1054
+ * - **falsy** to indicate that the rule didn't match after all, then `$urlRouter`
1055
+ * will continue trying to find another one that matches.
1056
+ * - **string** which is treated as a redirect and passed to `$location.url()`
1057
+ * - **void** or any **truthy** value tells `$urlRouter` that the url was handled.
1058
+ *
1059
+ * @example
1060
+ * <pre>
1061
+ * var app = angular.module('app', ['ui.router.router']);
1062
+ *
1063
+ * app.config(function ($urlRouterProvider) {
1064
+ * $urlRouterProvider.when($state.url, function ($match, $stateParams) {
1065
+ * if ($state.$current.navigable !== state ||
1066
+ * !equalForKeys($match, $stateParams) {
1067
+ * $state.transitionTo(state, $match, false);
1068
+ * }
1069
+ * });
1070
+ * });
1071
+ * </pre>
1072
+ *
1073
+ * @param {string|object} what The incoming path that you want to redirect.
1074
+ * @param {string|object} handler The path you want to redirect your user to.
1075
+ */
1076
+ this.when =
1077
+ function (what, handler) {
1078
+ var redirect, handlerIsString = isString(handler);
1079
+ if (isString(what)) what = $urlMatcherFactory.compile(what);
1080
+
1081
+ if (!handlerIsString && !isFunction(handler) && !isArray(handler))
1082
+ throw new Error("invalid 'handler' in when()");
1083
+
1084
+ var strategies = {
1085
+ matcher: function (what, handler) {
1086
+ if (handlerIsString) {
1087
+ redirect = $urlMatcherFactory.compile(handler);
1088
+ handler = ['$match', function ($match) { return redirect.format($match); }];
1089
+ }
1090
+ return extend(function ($injector, $location) {
1091
+ return handleIfMatch($injector, handler, what.exec($location.path(), $location.search()));
1092
+ }, {
1093
+ prefix: isString(what.prefix) ? what.prefix : ''
1094
+ });
1095
+ },
1096
+ regex: function (what, handler) {
1097
+ if (what.global || what.sticky) throw new Error("when() RegExp must not be global or sticky");
1098
+
1099
+ if (handlerIsString) {
1100
+ redirect = handler;
1101
+ handler = ['$match', function ($match) { return interpolate(redirect, $match); }];
1102
+ }
1103
+ return extend(function ($injector, $location) {
1104
+ return handleIfMatch($injector, handler, what.exec($location.path()));
1105
+ }, {
1106
+ prefix: regExpPrefix(what)
1107
+ });
1108
+ }
1109
+ };
1110
+
1111
+ var check = { matcher: $urlMatcherFactory.isMatcher(what), regex: what instanceof RegExp };
1112
+
1113
+ for (var n in check) {
1114
+ if (check[n]) {
1115
+ return this.rule(strategies[n](what, handler));
1116
+ }
1117
+ }
1118
+
1119
+ throw new Error("invalid 'what' in when()");
1120
+ };
1121
+
1122
+ /**
1123
+ * @ngdoc object
1124
+ * @name ui.router.router.$urlRouter
1125
+ *
1126
+ * @requires $location
1127
+ * @requires $rootScope
1128
+ * @requires $injector
1129
+ *
1130
+ * @description
1131
+ *
1132
+ */
1133
+ this.$get =
1134
+ [ '$location', '$rootScope', '$injector',
1135
+ function ($location, $rootScope, $injector) {
1136
+ // TODO: Optimize groups of rules with non-empty prefix into some sort of decision tree
1137
+ function update(evt) {
1138
+ if (evt && evt.defaultPrevented) return;
1139
+ function check(rule) {
1140
+ var handled = rule($injector, $location);
1141
+ if (handled) {
1142
+ if (isString(handled)) $location.replace().url(handled);
1143
+ return true;
1144
+ }
1145
+ return false;
1146
+ }
1147
+ var n=rules.length, i;
1148
+ for (i=0; i<n; i++) {
1149
+ if (check(rules[i])) return;
1150
+ }
1151
+ // always check otherwise last to allow dynamic updates to the set of rules
1152
+ if (otherwise) check(otherwise);
1153
+ }
1154
+
1155
+ $rootScope.$on('$locationChangeSuccess', update);
1156
+
1157
+ return {
1158
+ /**
1159
+ * @ngdoc function
1160
+ * @name ui.router.router.$urlRouter#sync
1161
+ * @methodOf ui.router.router.$urlRouter
1162
+ *
1163
+ * @description
1164
+ * Triggers an update; the same update that happens when the address bar url changes, aka `$locationChangeSuccess`.
1165
+ * This method is useful when you need to use `preventDefault()` on the `$locationChangeSuccess` event,
1166
+ * perform some custom logic (route protection, auth, config, redirection, etc) and then finally proceed
1167
+ * with the transition by calling `$urlRouter.sync()`.
1168
+ *
1169
+ * @example
1170
+ * <pre>
1171
+ * angular.module('app', ['ui.router']);
1172
+ * .run(function($rootScope, $urlRouter) {
1173
+ * $rootScope.$on('$locationChangeSuccess', function(evt) {
1174
+ * // Halt state change from even starting
1175
+ * evt.preventDefault();
1176
+ * // Perform custom logic
1177
+ * var meetsRequirement = ...
1178
+ * // Continue with the update and state transition if logic allows
1179
+ * if (meetsRequirement) $urlRouter.sync();
1180
+ * });
1181
+ * });
1182
+ * </pre>
1183
+ */
1184
+ sync: function () {
1185
+ update();
1186
+ }
1187
+ };
1188
+ }];
1189
+ }
1190
+
1191
+ angular.module('ui.router.router').provider('$urlRouter', $UrlRouterProvider);
1192
+
1193
+ /**
1194
+ * @ngdoc object
1195
+ * @name ui.router.state.$stateProvider
1196
+ *
1197
+ * @requires ui.router.router.$urlRouterProvider
1198
+ * @requires ui.router.util.$urlMatcherFactoryProvider
1199
+ * @requires $locationProvider
1200
+ *
1201
+ * @description
1202
+ * The new `$stateProvider` works similar to Angular's v1 router, but it focuses purely
1203
+ * on state.
1204
+ *
1205
+ * A state corresponds to a "place" in the application in terms of the overall UI and
1206
+ * navigation. A state describes (via the controller / template / view properties) what
1207
+ * the UI looks like and does at that place.
1208
+ *
1209
+ * States often have things in common, and the primary way of factoring out these
1210
+ * commonalities in this model is via the state hierarchy, i.e. parent/child states aka
1211
+ * nested states.
1212
+ *
1213
+ * The `$stateProvider` provides interfaces to declare these states for your app.
1214
+ */
1215
+ $StateProvider.$inject = ['$urlRouterProvider', '$urlMatcherFactoryProvider', '$locationProvider'];
1216
+ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $locationProvider) {
1217
+
1218
+ var root, states = {}, $state, queue = {}, abstractKey = 'abstract';
1219
+
1220
+ // Builds state properties from definition passed to registerState()
1221
+ var stateBuilder = {
1222
+
1223
+ // Derive parent state from a hierarchical name only if 'parent' is not explicitly defined.
1224
+ // state.children = [];
1225
+ // if (parent) parent.children.push(state);
1226
+ parent: function(state) {
1227
+ if (isDefined(state.parent) && state.parent) return findState(state.parent);
1228
+ // regex matches any valid composite state name
1229
+ // would match "contact.list" but not "contacts"
1230
+ var compositeName = /^(.+)\.[^.]+$/.exec(state.name);
1231
+ return compositeName ? findState(compositeName[1]) : root;
1232
+ },
1233
+
1234
+ // inherit 'data' from parent and override by own values (if any)
1235
+ data: function(state) {
1236
+ if (state.parent && state.parent.data) {
1237
+ state.data = state.self.data = extend({}, state.parent.data, state.data);
1238
+ }
1239
+ return state.data;
1240
+ },
1241
+
1242
+ // Build a URLMatcher if necessary, either via a relative or absolute URL
1243
+ url: function(state) {
1244
+ var url = state.url;
1245
+
1246
+ if (isString(url)) {
1247
+ if (url.charAt(0) == '^') {
1248
+ return $urlMatcherFactory.compile(url.substring(1));
1249
+ }
1250
+ return (state.parent.navigable || root).url.concat(url);
1251
+ }
1252
+
1253
+ if ($urlMatcherFactory.isMatcher(url) || url == null) {
1254
+ return url;
1255
+ }
1256
+ throw new Error("Invalid url '" + url + "' in state '" + state + "'");
1257
+ },
1258
+
1259
+ // Keep track of the closest ancestor state that has a URL (i.e. is navigable)
1260
+ navigable: function(state) {
1261
+ return state.url ? state : (state.parent ? state.parent.navigable : null);
1262
+ },
1263
+
1264
+ // Derive parameters for this state and ensure they're a super-set of parent's parameters
1265
+ params: function(state) {
1266
+ if (!state.params) {
1267
+ return state.url ? state.url.parameters() : state.parent.params;
1268
+ }
1269
+ if (!isArray(state.params)) throw new Error("Invalid params in state '" + state + "'");
1270
+ if (state.url) throw new Error("Both params and url specicified in state '" + state + "'");
1271
+ return state.params;
1272
+ },
1273
+
1274
+ // If there is no explicit multi-view configuration, make one up so we don't have
1275
+ // to handle both cases in the view directive later. Note that having an explicit
1276
+ // 'views' property will mean the default unnamed view properties are ignored. This
1277
+ // is also a good time to resolve view names to absolute names, so everything is a
1278
+ // straight lookup at link time.
1279
+ views: function(state) {
1280
+ var views = {};
1281
+
1282
+ forEach(isDefined(state.views) ? state.views : { '': state }, function (view, name) {
1283
+ if (name.indexOf('@') < 0) name += '@' + state.parent.name;
1284
+ views[name] = view;
1285
+ });
1286
+ return views;
1287
+ },
1288
+
1289
+ ownParams: function(state) {
1290
+ if (!state.parent) {
1291
+ return state.params;
1292
+ }
1293
+ var paramNames = {}; forEach(state.params, function (p) { paramNames[p] = true; });
1294
+
1295
+ forEach(state.parent.params, function (p) {
1296
+ if (!paramNames[p]) {
1297
+ throw new Error("Missing required parameter '" + p + "' in state '" + state.name + "'");
1298
+ }
1299
+ paramNames[p] = false;
1300
+ });
1301
+ var ownParams = [];
1302
+
1303
+ forEach(paramNames, function (own, p) {
1304
+ if (own) ownParams.push(p);
1305
+ });
1306
+ return ownParams;
1307
+ },
1308
+
1309
+ // Keep a full path from the root down to this state as this is needed for state activation.
1310
+ path: function(state) {
1311
+ return state.parent ? state.parent.path.concat(state) : []; // exclude root from path
1312
+ },
1313
+
1314
+ // Speed up $state.contains() as it's used a lot
1315
+ includes: function(state) {
1316
+ var includes = state.parent ? extend({}, state.parent.includes) : {};
1317
+ includes[state.name] = true;
1318
+ return includes;
1319
+ },
1320
+
1321
+ $delegates: {}
1322
+ };
1323
+
1324
+ function isRelative(stateName) {
1325
+ return stateName.indexOf(".") === 0 || stateName.indexOf("^") === 0;
1326
+ }
1327
+
1328
+ function findState(stateOrName, base) {
1329
+ var isStr = isString(stateOrName),
1330
+ name = isStr ? stateOrName : stateOrName.name,
1331
+ path = isRelative(name);
1332
+
1333
+ if (path) {
1334
+ if (!base) throw new Error("No reference point given for path '" + name + "'");
1335
+ var rel = name.split("."), i = 0, pathLength = rel.length, current = base;
1336
+
1337
+ for (; i < pathLength; i++) {
1338
+ if (rel[i] === "" && i === 0) {
1339
+ current = base;
1340
+ continue;
1341
+ }
1342
+ if (rel[i] === "^") {
1343
+ if (!current.parent) throw new Error("Path '" + name + "' not valid for state '" + base.name + "'");
1344
+ current = current.parent;
1345
+ continue;
1346
+ }
1347
+ break;
1348
+ }
1349
+ rel = rel.slice(i).join(".");
1350
+ name = current.name + (current.name && rel ? "." : "") + rel;
1351
+ }
1352
+ var state = states[name];
1353
+
1354
+ if (state && (isStr || (!isStr && (state === stateOrName || state.self === stateOrName)))) {
1355
+ return state;
1356
+ }
1357
+ return undefined;
1358
+ }
1359
+
1360
+ function queueState(parentName, state) {
1361
+ if (!queue[parentName]) {
1362
+ queue[parentName] = [];
1363
+ }
1364
+ queue[parentName].push(state);
1365
+ }
1366
+
1367
+ function registerState(state) {
1368
+ // Wrap a new object around the state so we can store our private details easily.
1369
+ state = inherit(state, {
1370
+ self: state,
1371
+ resolve: state.resolve || {},
1372
+ toString: function() { return this.name; }
1373
+ });
1374
+
1375
+ var name = state.name;
1376
+ if (!isString(name) || name.indexOf('@') >= 0) throw new Error("State must have a valid name");
1377
+ if (states.hasOwnProperty(name)) throw new Error("State '" + name + "'' is already defined");
1378
+
1379
+ // Get parent name
1380
+ var parentName = (name.indexOf('.') !== -1) ? name.substring(0, name.lastIndexOf('.'))
1381
+ : (isString(state.parent)) ? state.parent
1382
+ : '';
1383
+
1384
+ // If parent is not registered yet, add state to queue and register later
1385
+ if (parentName && !states[parentName]) {
1386
+ return queueState(parentName, state.self);
1387
+ }
1388
+
1389
+ for (var key in stateBuilder) {
1390
+ if (isFunction(stateBuilder[key])) state[key] = stateBuilder[key](state, stateBuilder.$delegates[key]);
1391
+ }
1392
+ states[name] = state;
1393
+
1394
+ // Register the state in the global state list and with $urlRouter if necessary.
1395
+ if (!state[abstractKey] && state.url) {
1396
+ $urlRouterProvider.when(state.url, ['$match', '$stateParams', function ($match, $stateParams) {
1397
+ if ($state.$current.navigable != state || !equalForKeys($match, $stateParams)) {
1398
+ $state.transitionTo(state, $match, { location: false });
1399
+ }
1400
+ }]);
1401
+ }
1402
+
1403
+ // Register any queued children
1404
+ if (queue[name]) {
1405
+ for (var i = 0; i < queue[name].length; i++) {
1406
+ registerState(queue[name][i]);
1407
+ }
1408
+ }
1409
+
1410
+ return state;
1411
+ }
1412
+
1413
+ // Checks text to see if it looks like a glob.
1414
+ function isGlob (text) {
1415
+ return text.indexOf('*') > -1;
1416
+ }
1417
+
1418
+ // Returns true if glob matches current $state name.
1419
+ function doesStateMatchGlob (glob) {
1420
+ var globSegments = glob.split('.'),
1421
+ segments = $state.$current.name.split('.');
1422
+
1423
+ //match greedy starts
1424
+ if (globSegments[0] === '**') {
1425
+ segments = segments.slice(segments.indexOf(globSegments[1]));
1426
+ segments.unshift('**');
1427
+ }
1428
+ //match greedy ends
1429
+ if (globSegments[globSegments.length - 1] === '**') {
1430
+ segments.splice(segments.indexOf(globSegments[globSegments.length - 2]) + 1, Number.MAX_VALUE);
1431
+ segments.push('**');
1432
+ }
1433
+
1434
+ if (globSegments.length != segments.length) {
1435
+ return false;
1436
+ }
1437
+
1438
+ //match single stars
1439
+ for (var i = 0, l = globSegments.length; i < l; i++) {
1440
+ if (globSegments[i] === '*') {
1441
+ segments[i] = '*';
1442
+ }
1443
+ }
1444
+
1445
+ return segments.join('') === globSegments.join('');
1446
+ }
1447
+
1448
+
1449
+ // Implicit root state that is always active
1450
+ root = registerState({
1451
+ name: '',
1452
+ url: '^',
1453
+ views: null,
1454
+ 'abstract': true
1455
+ });
1456
+ root.navigable = null;
1457
+
1458
+
1459
+ /**
1460
+ * @ngdoc function
1461
+ * @name ui.router.state.$stateProvider#decorator
1462
+ * @methodOf ui.router.state.$stateProvider
1463
+ *
1464
+ * @description
1465
+ * Allows you to extend (carefully) or override (at your own peril) the
1466
+ * `stateBuilder` object used internally by `$stateProvider`. This can be used
1467
+ * to add custom functionality to ui-router, for example inferring templateUrl
1468
+ * based on the state name.
1469
+ *
1470
+ * When passing only a name, it returns the current (original or decorated) builder
1471
+ * function that matches `name`.
1472
+ *
1473
+ * The builder functions that can be decorated are listed below. Though not all
1474
+ * necessarily have a good use case for decoration, that is up to you to decide.
1475
+ *
1476
+ * In addition, users can attach custom decorators, which will generate new
1477
+ * properties within the state's internal definition. There is currently no clear
1478
+ * use-case for this beyond accessing internal states (i.e. $state.$current),
1479
+ * however, expect this to become increasingly relevant as we introduce additional
1480
+ * meta-programming features.
1481
+ *
1482
+ * **Warning**: Decorators should not be interdependent because the order of
1483
+ * execution of the builder functions in non-deterministic. Builder functions
1484
+ * should only be dependent on the state definition object and super function.
1485
+ *
1486
+ *
1487
+ * Existing builder functions and current return values:
1488
+ *
1489
+ * - **parent** `{object}` - returns the parent state object.
1490
+ * - **data** `{object}` - returns state data, including any inherited data that is not
1491
+ * overridden by own values (if any).
1492
+ * - **url** `{object}` - returns a {link ui.router.util.type:UrlMatcher} or null.
1493
+ * - **navigable** `{object}` - returns closest ancestor state that has a URL (aka is
1494
+ * navigable).
1495
+ * - **params** `{object}` - returns an array of state params that are ensured to
1496
+ * be a super-set of parent's params.
1497
+ * - **views** `{object}` - returns a views object where each key is an absolute view
1498
+ * name (i.e. "viewName@stateName") and each value is the config object
1499
+ * (template, controller) for the view. Even when you don't use the views object
1500
+ * explicitly on a state config, one is still created for you internally.
1501
+ * So by decorating this builder function you have access to decorating template
1502
+ * and controller properties.
1503
+ * - **ownParams** `{object}` - returns an array of params that belong to the state,
1504
+ * not including any params defined by ancestor states.
1505
+ * - **path** `{string}` - returns the full path from the root down to this state.
1506
+ * Needed for state activation.
1507
+ * - **includes** `{object}` - returns an object that includes every state that
1508
+ * would pass a '$state.includes()' test.
1509
+ *
1510
+ * @example
1511
+ * <pre>
1512
+ * // Override the internal 'views' builder with a function that takes the state
1513
+ * // definition, and a reference to the internal function being overridden:
1514
+ * $stateProvider.decorator('views', function ($state, parent) {
1515
+ * var result = {},
1516
+ * views = parent(state);
1517
+ *
1518
+ * angular.forEach(view, function (config, name) {
1519
+ * var autoName = (state.name + '.' + name).replace('.', '/');
1520
+ * config.templateUrl = config.templateUrl || '/partials/' + autoName + '.html';
1521
+ * result[name] = config;
1522
+ * });
1523
+ * return result;
1524
+ * });
1525
+ *
1526
+ * $stateProvider.state('home', {
1527
+ * views: {
1528
+ * 'contact.list': { controller: 'ListController' },
1529
+ * 'contact.item': { controller: 'ItemController' }
1530
+ * }
1531
+ * });
1532
+ *
1533
+ * // ...
1534
+ *
1535
+ * $state.go('home');
1536
+ * // Auto-populates list and item views with /partials/home/contact/list.html,
1537
+ * // and /partials/home/contact/item.html, respectively.
1538
+ * </pre>
1539
+ *
1540
+ * @param {string} name The name of the builder function to decorate.
1541
+ * @param {object} func A function that is responsible for decorating the original
1542
+ * builder function. The function receives two parameters:
1543
+ *
1544
+ * - `{object}` - state - The state config object.
1545
+ * - `{object}` - super - The original builder function.
1546
+ *
1547
+ * @return {object} $stateProvider - $stateProvider instance
1548
+ */
1549
+ this.decorator = decorator;
1550
+ function decorator(name, func) {
1551
+ /*jshint validthis: true */
1552
+ if (isString(name) && !isDefined(func)) {
1553
+ return stateBuilder[name];
1554
+ }
1555
+ if (!isFunction(func) || !isString(name)) {
1556
+ return this;
1557
+ }
1558
+ if (stateBuilder[name] && !stateBuilder.$delegates[name]) {
1559
+ stateBuilder.$delegates[name] = stateBuilder[name];
1560
+ }
1561
+ stateBuilder[name] = func;
1562
+ return this;
1563
+ }
1564
+
1565
+ /**
1566
+ * @ngdoc function
1567
+ * @name ui.router.state.$stateProvider#state
1568
+ * @methodOf ui.router.state.$stateProvider
1569
+ *
1570
+ * @description
1571
+ * Registers a state configuration under a given state name. The stateConfig object
1572
+ * has the following acceptable properties.
1573
+ *
1574
+ * <a id='template'></a>
1575
+ *
1576
+ * - **`template`** - {string|function=} - html template as a string or a function that returns
1577
+ * an html template as a string which should be used by the uiView directives. This property
1578
+ * takes precedence over templateUrl.
1579
+ *
1580
+ * If `template` is a function, it will be called with the following parameters:
1581
+ *
1582
+ * - {array.&lt;object&gt;} - state parameters extracted from the current $location.path() by
1583
+ * applying the current state
1584
+ *
1585
+ * <a id='templateUrl'></a>
1586
+ *
1587
+ * - **`templateUrl`** - {string|function=} - path or function that returns a path to an html
1588
+ * template that should be used by uiView.
1589
+ *
1590
+ * If `templateUrl` is a function, it will be called with the following parameters:
1591
+ *
1592
+ * - {array.&lt;object&gt;} - state parameters extracted from the current $location.path() by
1593
+ * applying the current state
1594
+ *
1595
+ * <a id='templateProvider'></a>
1596
+ *
1597
+ * - **`templateProvider`** - {function=} - Provider function that returns HTML content
1598
+ * string.
1599
+ *
1600
+ * <a id='controller'></a>
1601
+ *
1602
+ * - **`controller`** - {string|function=} - Controller fn that should be associated with newly
1603
+ * related scope or the name of a registered controller if passed as a string.
1604
+ *
1605
+ * <a id='controllerProvider'></a>
1606
+ *
1607
+ * - **`controllerProvider`** - {function=} - Injectable provider function that returns
1608
+ * the actual controller or string.
1609
+ *
1610
+ * <a id='controllerAs'></a>
1611
+ *
1612
+ * - **`controllerAs`** – {string=} – A controller alias name. If present the controller will be
1613
+ * published to scope under the controllerAs name.
1614
+ *
1615
+ * <a id='resolve'></a>
1616
+ *
1617
+ * - **`resolve`** - {object.&lt;string, function&gt;=} - An optional map of dependencies which
1618
+ * should be injected into the controller. If any of these dependencies are promises,
1619
+ * the router will wait for them all to be resolved or one to be rejected before the
1620
+ * controller is instantiated. If all the promises are resolved successfully, the values
1621
+ * of the resolved promises are injected and $stateChangeSuccess event is fired. If any
1622
+ * of the promises are rejected the $stateChangeError event is fired. The map object is:
1623
+ *
1624
+ * - key - {string}: name of dependency to be injected into controller
1625
+ * - factory - {string|function}: If string then it is alias for service. Otherwise if function,
1626
+ * it is injected and return value it treated as dependency. If result is a promise, it is
1627
+ * resolved before its value is injected into controller.
1628
+ *
1629
+ * <a id='url'></a>
1630
+ *
1631
+ * - **`url`** - {string=} - A url with optional parameters. When a state is navigated or
1632
+ * transitioned to, the `$stateParams` service will be populated with any
1633
+ * parameters that were passed.
1634
+ *
1635
+ * <a id='params'></a>
1636
+ *
1637
+ * - **`params`** - {object=} - An array of parameter names or regular expressions. Only
1638
+ * use this within a state if you are not using url. Otherwise you can specify your
1639
+ * parameters within the url. When a state is navigated or transitioned to, the
1640
+ * $stateParams service will be populated with any parameters that were passed.
1641
+ *
1642
+ * <a id='views'></a>
1643
+ *
1644
+ * - **`views`** - {object=} - Use the views property to set up multiple views or to target views
1645
+ * manually/explicitly.
1646
+ *
1647
+ * <a id='abstract'></a>
1648
+ *
1649
+ * - **`abstract`** - {boolean=} - An abstract state will never be directly activated,
1650
+ * but can provide inherited properties to its common children states.
1651
+ *
1652
+ * <a id='onEnter'></a>
1653
+ *
1654
+ * - **`onEnter`** - {object=} - Callback function for when a state is entered. Good way
1655
+ * to trigger an action or dispatch an event, such as opening a dialog.
1656
+ *
1657
+ * <a id='onExit'></a>
1658
+ *
1659
+ * - **`onExit`** - {object=} - Callback function for when a state is exited. Good way to
1660
+ * trigger an action or dispatch an event, such as opening a dialog.
1661
+ *
1662
+ * <a id='reloadOnSearch'></a>
1663
+ *
1664
+ * - **`reloadOnSearch = true`** - {boolean=} - If `false`, will not retrigger the same state
1665
+ * just because a search/query parameter has changed (via $location.search() or $location.hash()).
1666
+ * Useful for when you'd like to modify $location.search() without triggering a reload.
1667
+ *
1668
+ * <a id='data'></a>
1669
+ *
1670
+ * - **`data`** - {object=} - Arbitrary data object, useful for custom configuration.
1671
+ *
1672
+ * @example
1673
+ * <pre>
1674
+ * // Some state name examples
1675
+ *
1676
+ * // stateName can be a single top-level name (must be unique).
1677
+ * $stateProvider.state("home", {});
1678
+ *
1679
+ * // Or it can be a nested state name. This state is a child of the
1680
+ * // above "home" state.
1681
+ * $stateProvider.state("home.newest", {});
1682
+ *
1683
+ * // Nest states as deeply as needed.
1684
+ * $stateProvider.state("home.newest.abc.xyz.inception", {});
1685
+ *
1686
+ * // state() returns $stateProvider, so you can chain state declarations.
1687
+ * $stateProvider
1688
+ * .state("home", {})
1689
+ * .state("about", {})
1690
+ * .state("contacts", {});
1691
+ * </pre>
1692
+ *
1693
+ * @param {string} name A unique state name, e.g. "home", "about", "contacts".
1694
+ * To create a parent/child state use a dot, e.g. "about.sales", "home.newest".
1695
+ * @param {object} definition State configuration object.
1696
+ */
1697
+ this.state = state;
1698
+ function state(name, definition) {
1699
+ /*jshint validthis: true */
1700
+ if (isObject(name)) definition = name;
1701
+ else definition.name = name;
1702
+ registerState(definition);
1703
+ return this;
1704
+ }
1705
+
1706
+ /**
1707
+ * @ngdoc object
1708
+ * @name ui.router.state.$state
1709
+ *
1710
+ * @requires $rootScope
1711
+ * @requires $q
1712
+ * @requires ui.router.state.$view
1713
+ * @requires $injector
1714
+ * @requires ui.router.util.$resolve
1715
+ * @requires ui.router.state.$stateParams
1716
+ *
1717
+ * @property {object} params A param object, e.g. {sectionId: section.id)}, that
1718
+ * you'd like to test against the current active state.
1719
+ * @property {object} current A reference to the state's config object. However
1720
+ * you passed it in. Useful for accessing custom data.
1721
+ * @property {object} transition Currently pending transition. A promise that'll
1722
+ * resolve or reject.
1723
+ *
1724
+ * @description
1725
+ * `$state` service is responsible for representing states as well as transitioning
1726
+ * between them. It also provides interfaces to ask for current state or even states
1727
+ * you're coming from.
1728
+ */
1729
+ // $urlRouter is injected just to ensure it gets instantiated
1730
+ this.$get = $get;
1731
+ $get.$inject = ['$rootScope', '$q', '$view', '$injector', '$resolve', '$stateParams', '$location', '$urlRouter', '$browser'];
1732
+ function $get( $rootScope, $q, $view, $injector, $resolve, $stateParams, $location, $urlRouter, $browser) {
1733
+
1734
+ var TransitionSuperseded = $q.reject(new Error('transition superseded'));
1735
+ var TransitionPrevented = $q.reject(new Error('transition prevented'));
1736
+ var TransitionAborted = $q.reject(new Error('transition aborted'));
1737
+ var TransitionFailed = $q.reject(new Error('transition failed'));
1738
+ var currentLocation = $location.url();
1739
+ var baseHref = $browser.baseHref();
1740
+
1741
+ function syncUrl() {
1742
+ if ($location.url() !== currentLocation) {
1743
+ $location.url(currentLocation);
1744
+ $location.replace();
1745
+ }
1746
+ }
1747
+
1748
+ root.locals = { resolve: null, globals: { $stateParams: {} } };
1749
+ $state = {
1750
+ params: {},
1751
+ current: root.self,
1752
+ $current: root,
1753
+ transition: null
1754
+ };
1755
+
1756
+ /**
1757
+ * @ngdoc function
1758
+ * @name ui.router.state.$state#reload
1759
+ * @methodOf ui.router.state.$state
1760
+ *
1761
+ * @description
1762
+ * A method that force reloads the current state. All resolves are re-resolved, events are not re-fired,
1763
+ * and controllers reinstantiated (bug with controllers reinstantiating right now, fixing soon).
1764
+ *
1765
+ * @example
1766
+ * <pre>
1767
+ * var app angular.module('app', ['ui.router']);
1768
+ *
1769
+ * app.controller('ctrl', function ($scope, $state) {
1770
+ * $scope.reload = function(){
1771
+ * $state.reload();
1772
+ * }
1773
+ * });
1774
+ * </pre>
1775
+ *
1776
+ * `reload()` is just an alias for:
1777
+ * <pre>
1778
+ * $state.transitionTo($state.current, $stateParams, {
1779
+ * reload: true, inherit: false, notify: false
1780
+ * });
1781
+ * </pre>
1782
+ */
1783
+ $state.reload = function reload() {
1784
+ $state.transitionTo($state.current, $stateParams, { reload: true, inherit: false, notify: false });
1785
+ };
1786
+
1787
+ /**
1788
+ * @ngdoc function
1789
+ * @name ui.router.state.$state#go
1790
+ * @methodOf ui.router.state.$state
1791
+ *
1792
+ * @description
1793
+ * Convenience method for transitioning to a new state. `$state.go` calls
1794
+ * `$state.transitionTo` internally but automatically sets options to
1795
+ * `{ location: true, inherit: true, relative: $state.$current, notify: true }`.
1796
+ * This allows you to easily use an absolute or relative to path and specify
1797
+ * only the parameters you'd like to update (while letting unspecified parameters
1798
+ * inherit from the currently active ancestor states).
1799
+ *
1800
+ * @example
1801
+ * <pre>
1802
+ * var app = angular.module('app', ['ui.router']);
1803
+ *
1804
+ * app.controller('ctrl', function ($scope, $state) {
1805
+ * $scope.changeState = function () {
1806
+ * $state.go('contact.detail');
1807
+ * };
1808
+ * });
1809
+ * </pre>
1810
+ * <img src='../ngdoc_assets/StateGoExamples.png'/>
1811
+ *
1812
+ * @param {string} to Absolute state name or relative state path. Some examples:
1813
+ *
1814
+ * - `$state.go('contact.detail')` - will go to the `contact.detail` state
1815
+ * - `$state.go('^')` - will go to a parent state
1816
+ * - `$state.go('^.sibling')` - will go to a sibling state
1817
+ * - `$state.go('.child.grandchild')` - will go to grandchild state
1818
+ *
1819
+ * @param {object=} params A map of the parameters that will be sent to the state,
1820
+ * will populate $stateParams. Any parameters that are not specified will be inherited from currently
1821
+ * defined parameters. This allows, for example, going to a sibling state that shares parameters
1822
+ * specified in a parent state. Parameter inheritance only works between common ancestor states, I.e.
1823
+ * transitioning to a sibling will get you the parameters for all parents, transitioning to a child
1824
+ * will get you all current parameters, etc.
1825
+ * @param {object=} options Options object. The options are:
1826
+ *
1827
+ * - **`location`** - {boolean=true|string=} - If `true` will update the url in the location bar, if `false`
1828
+ * will not. If string, must be `"replace"`, which will update url and also replace last history record.
1829
+ * - **`inherit`** - {boolean=true}, If `true` will inherit url parameters from current url.
1830
+ * - **`relative`** - {object=$state.$current}, When transitioning with relative path (e.g '^'),
1831
+ * defines which state to be relative from.
1832
+ * - **`notify`** - {boolean=true}, If `true` will broadcast $stateChangeStart and $stateChangeSuccess events.
1833
+ * - **`reload`** (v0.2.5) - {boolean=false}, If `true` will force transition even if the state or params
1834
+ * have not changed, aka a reload of the same state. It differs from reloadOnSearch because you'd
1835
+ * use this when you want to force a reload when *everything* is the same, including search params.
1836
+ *
1837
+ * @returns {promise} A promise representing the state of the new transition.
1838
+ *
1839
+ * Possible success values:
1840
+ *
1841
+ * - $state.current
1842
+ *
1843
+ * <br/>Possible rejection values:
1844
+ *
1845
+ * - 'transition superseded' - when a newer transition has been started after this one
1846
+ * - 'transition prevented' - when `event.preventDefault()` has been called in a `$stateChangeStart` listener
1847
+ * - 'transition aborted' - when `event.preventDefault()` has been called in a `$stateNotFound` listener or
1848
+ * when a `$stateNotFound` `event.retry` promise errors.
1849
+ * - 'transition failed' - when a state has been unsuccessfully found after 2 tries.
1850
+ * - *resolve error* - when an error has occurred with a `resolve`
1851
+ *
1852
+ */
1853
+ $state.go = function go(to, params, options) {
1854
+ return this.transitionTo(to, params, extend({ inherit: true, relative: $state.$current }, options));
1855
+ };
1856
+
1857
+ /**
1858
+ * @ngdoc function
1859
+ * @name ui.router.state.$state#transitionTo
1860
+ * @methodOf ui.router.state.$state
1861
+ *
1862
+ * @description
1863
+ * Low-level method for transitioning to a new state. {@link ui.router.state.$state#methods_go $state.go}
1864
+ * uses `transitionTo` internally. `$state.go` is recommended in most situations.
1865
+ *
1866
+ * @example
1867
+ * <pre>
1868
+ * var app = angular.module('app', ['ui.router']);
1869
+ *
1870
+ * app.controller('ctrl', function ($scope, $state) {
1871
+ * $scope.changeState = function () {
1872
+ * $state.transitionTo('contact.detail');
1873
+ * };
1874
+ * });
1875
+ * </pre>
1876
+ *
1877
+ * @param {string} to State name.
1878
+ * @param {object=} toParams A map of the parameters that will be sent to the state,
1879
+ * will populate $stateParams.
1880
+ * @param {object=} options Options object. The options are:
1881
+ *
1882
+ * - **`location`** - {boolean=true|string=} - If `true` will update the url in the location bar, if `false`
1883
+ * will not. If string, must be `"replace"`, which will update url and also replace last history record.
1884
+ * - **`inherit`** - {boolean=false}, If `true` will inherit url parameters from current url.
1885
+ * - **`relative`** - {object=}, When transitioning with relative path (e.g '^'),
1886
+ * defines which state to be relative from.
1887
+ * - **`notify`** - {boolean=true}, If `true` will broadcast $stateChangeStart and $stateChangeSuccess events.
1888
+ * - **`reload`** (v0.2.5) - {boolean=false}, If `true` will force transition even if the state or params
1889
+ * have not changed, aka a reload of the same state. It differs from reloadOnSearch because you'd
1890
+ * use this when you want to force a reload when *everything* is the same, including search params.
1891
+ *
1892
+ * @returns {promise} A promise representing the state of the new transition. See
1893
+ * {@link ui.router.state.$state#methods_go $state.go}.
1894
+ */
1895
+ $state.transitionTo = function transitionTo(to, toParams, options) {
1896
+ toParams = toParams || {};
1897
+ options = extend({
1898
+ location: true, inherit: false, relative: null, notify: true, reload: false, $retry: false
1899
+ }, options || {});
1900
+
1901
+ var from = $state.$current, fromParams = $state.params, fromPath = from.path;
1902
+ var evt, toState = findState(to, options.relative);
1903
+
1904
+ if (!isDefined(toState)) {
1905
+ // Broadcast not found event and abort the transition if prevented
1906
+ var redirect = { to: to, toParams: toParams, options: options };
1907
+
1908
+ /**
1909
+ * @ngdoc event
1910
+ * @name ui.router.state.$state#$stateNotFound
1911
+ * @eventOf ui.router.state.$state
1912
+ * @eventType broadcast on root scope
1913
+ * @description
1914
+ * Fired when a requested state **cannot be found** using the provided state name during transition.
1915
+ * The event is broadcast allowing any handlers a single chance to deal with the error (usually by
1916
+ * lazy-loading the unfound state). A special `unfoundState` object is passed to the listener handler,
1917
+ * you can see its three properties in the example. You can use `event.preventDefault()` to abort the
1918
+ * transition and the promise returned from `go` will be rejected with a `'transition aborted'` value.
1919
+ *
1920
+ * @param {Object} event Event object.
1921
+ * @param {Object} unfoundState Unfound State information. Contains: `to, toParams, options` properties.
1922
+ * @param {State} fromState Current state object.
1923
+ * @param {Object} fromParams Current state params.
1924
+ *
1925
+ * @example
1926
+ *
1927
+ * <pre>
1928
+ * // somewhere, assume lazy.state has not been defined
1929
+ * $state.go("lazy.state", {a:1, b:2}, {inherit:false});
1930
+ *
1931
+ * // somewhere else
1932
+ * $scope.$on('$stateNotFound',
1933
+ * function(event, unfoundState, fromState, fromParams){
1934
+ * console.log(unfoundState.to); // "lazy.state"
1935
+ * console.log(unfoundState.toParams); // {a:1, b:2}
1936
+ * console.log(unfoundState.options); // {inherit:false} + default options
1937
+ * })
1938
+ * </pre>
1939
+ */
1940
+ evt = $rootScope.$broadcast('$stateNotFound', redirect, from.self, fromParams);
1941
+ if (evt.defaultPrevented) {
1942
+ syncUrl();
1943
+ return TransitionAborted;
1944
+ }
1945
+
1946
+ // Allow the handler to return a promise to defer state lookup retry
1947
+ if (evt.retry) {
1948
+ if (options.$retry) {
1949
+ syncUrl();
1950
+ return TransitionFailed;
1951
+ }
1952
+ var retryTransition = $state.transition = $q.when(evt.retry);
1953
+ retryTransition.then(function() {
1954
+ if (retryTransition !== $state.transition) return TransitionSuperseded;
1955
+ redirect.options.$retry = true;
1956
+ return $state.transitionTo(redirect.to, redirect.toParams, redirect.options);
1957
+ }, function() {
1958
+ return TransitionAborted;
1959
+ });
1960
+ syncUrl();
1961
+ return retryTransition;
1962
+ }
1963
+
1964
+ // Always retry once if the $stateNotFound was not prevented
1965
+ // (handles either redirect changed or state lazy-definition)
1966
+ to = redirect.to;
1967
+ toParams = redirect.toParams;
1968
+ options = redirect.options;
1969
+ toState = findState(to, options.relative);
1970
+ if (!isDefined(toState)) {
1971
+ if (options.relative) throw new Error("Could not resolve '" + to + "' from state '" + options.relative + "'");
1972
+ throw new Error("No such state '" + to + "'");
1973
+ }
1974
+ }
1975
+ if (toState[abstractKey]) throw new Error("Cannot transition to abstract state '" + to + "'");
1976
+ if (options.inherit) toParams = inheritParams($stateParams, toParams || {}, $state.$current, toState);
1977
+ to = toState;
1978
+
1979
+ var toPath = to.path;
1980
+
1981
+ // Starting from the root of the path, keep all levels that haven't changed
1982
+ var keep, state, locals = root.locals, toLocals = [];
1983
+ for (keep = 0, state = toPath[keep];
1984
+ state && state === fromPath[keep] && equalForKeys(toParams, fromParams, state.ownParams) && !options.reload;
1985
+ keep++, state = toPath[keep]) {
1986
+ locals = toLocals[keep] = state.locals;
1987
+ }
1988
+
1989
+ // If we're going to the same state and all locals are kept, we've got nothing to do.
1990
+ // But clear 'transition', as we still want to cancel any other pending transitions.
1991
+ // TODO: We may not want to bump 'transition' if we're called from a location change that we've initiated ourselves,
1992
+ // because we might accidentally abort a legitimate transition initiated from code?
1993
+ if (shouldTriggerReload(to, from, locals, options) ) {
1994
+ if ( to.self.reloadOnSearch !== false )
1995
+ syncUrl();
1996
+ $state.transition = null;
1997
+ return $q.when($state.current);
1998
+ }
1999
+
2000
+ // Normalize/filter parameters before we pass them to event handlers etc.
2001
+ toParams = normalize(to.params, toParams || {});
2002
+
2003
+ // Broadcast start event and cancel the transition if requested
2004
+ if (options.notify) {
2005
+ /**
2006
+ * @ngdoc event
2007
+ * @name ui.router.state.$state#$stateChangeStart
2008
+ * @eventOf ui.router.state.$state
2009
+ * @eventType broadcast on root scope
2010
+ * @description
2011
+ * Fired when the state transition **begins**. You can use `event.preventDefault()`
2012
+ * to prevent the transition from happening and then the transition promise will be
2013
+ * rejected with a `'transition prevented'` value.
2014
+ *
2015
+ * @param {Object} event Event object.
2016
+ * @param {State} toState The state being transitioned to.
2017
+ * @param {Object} toParams The params supplied to the `toState`.
2018
+ * @param {State} fromState The current state, pre-transition.
2019
+ * @param {Object} fromParams The params supplied to the `fromState`.
2020
+ *
2021
+ * @example
2022
+ *
2023
+ * <pre>
2024
+ * $rootScope.$on('$stateChangeStart',
2025
+ * function(event, toState, toParams, fromState, fromParams){
2026
+ * event.preventDefault();
2027
+ * // transitionTo() promise will be rejected with
2028
+ * // a 'transition prevented' error
2029
+ * })
2030
+ * </pre>
2031
+ */
2032
+ evt = $rootScope.$broadcast('$stateChangeStart', to.self, toParams, from.self, fromParams);
2033
+ if (evt.defaultPrevented) {
2034
+ syncUrl();
2035
+ return TransitionPrevented;
2036
+ }
2037
+ }
2038
+
2039
+ // Resolve locals for the remaining states, but don't update any global state just
2040
+ // yet -- if anything fails to resolve the current state needs to remain untouched.
2041
+ // We also set up an inheritance chain for the locals here. This allows the view directive
2042
+ // to quickly look up the correct definition for each view in the current state. Even
2043
+ // though we create the locals object itself outside resolveState(), it is initially
2044
+ // empty and gets filled asynchronously. We need to keep track of the promise for the
2045
+ // (fully resolved) current locals, and pass this down the chain.
2046
+ var resolved = $q.when(locals);
2047
+ for (var l=keep; l<toPath.length; l++, state=toPath[l]) {
2048
+ locals = toLocals[l] = inherit(locals);
2049
+ resolved = resolveState(state, toParams, state===to, resolved, locals);
2050
+ }
2051
+
2052
+ // Once everything is resolved, we are ready to perform the actual transition
2053
+ // and return a promise for the new state. We also keep track of what the
2054
+ // current promise is, so that we can detect overlapping transitions and
2055
+ // keep only the outcome of the last transition.
2056
+ var transition = $state.transition = resolved.then(function () {
2057
+ var l, entering, exiting;
2058
+
2059
+ if ($state.transition !== transition) return TransitionSuperseded;
2060
+
2061
+ // Exit 'from' states not kept
2062
+ for (l=fromPath.length-1; l>=keep; l--) {
2063
+ exiting = fromPath[l];
2064
+ if (exiting.self.onExit) {
2065
+ $injector.invoke(exiting.self.onExit, exiting.self, exiting.locals.globals);
2066
+ }
2067
+ exiting.locals = null;
2068
+ }
2069
+
2070
+ // Enter 'to' states not kept
2071
+ for (l=keep; l<toPath.length; l++) {
2072
+ entering = toPath[l];
2073
+ entering.locals = toLocals[l];
2074
+ if (entering.self.onEnter) {
2075
+ $injector.invoke(entering.self.onEnter, entering.self, entering.locals.globals);
2076
+ }
2077
+ }
2078
+
2079
+ // Run it again, to catch any transitions in callbacks
2080
+ if ($state.transition !== transition) return TransitionSuperseded;
2081
+
2082
+ // Update globals in $state
2083
+ $state.$current = to;
2084
+ $state.current = to.self;
2085
+ $state.params = toParams;
2086
+ copy($state.params, $stateParams);
2087
+ $state.transition = null;
2088
+
2089
+ // Update $location
2090
+ var toNav = to.navigable;
2091
+ if (options.location && toNav) {
2092
+ $location.url(toNav.url.format(toNav.locals.globals.$stateParams));
2093
+
2094
+ if (options.location === 'replace') {
2095
+ $location.replace();
2096
+ }
2097
+ }
2098
+
2099
+ if (options.notify) {
2100
+ /**
2101
+ * @ngdoc event
2102
+ * @name ui.router.state.$state#$stateChangeSuccess
2103
+ * @eventOf ui.router.state.$state
2104
+ * @eventType broadcast on root scope
2105
+ * @description
2106
+ * Fired once the state transition is **complete**.
2107
+ *
2108
+ * @param {Object} event Event object.
2109
+ * @param {State} toState The state being transitioned to.
2110
+ * @param {Object} toParams The params supplied to the `toState`.
2111
+ * @param {State} fromState The current state, pre-transition.
2112
+ * @param {Object} fromParams The params supplied to the `fromState`.
2113
+ */
2114
+ $rootScope.$broadcast('$stateChangeSuccess', to.self, toParams, from.self, fromParams);
2115
+ }
2116
+ currentLocation = $location.url();
2117
+
2118
+ return $state.current;
2119
+ }, function (error) {
2120
+ if ($state.transition !== transition) return TransitionSuperseded;
2121
+
2122
+ $state.transition = null;
2123
+ /**
2124
+ * @ngdoc event
2125
+ * @name ui.router.state.$state#$stateChangeError
2126
+ * @eventOf ui.router.state.$state
2127
+ * @eventType broadcast on root scope
2128
+ * @description
2129
+ * Fired when an **error occurs** during transition. It's important to note that if you
2130
+ * have any errors in your resolve functions (javascript errors, non-existent services, etc)
2131
+ * they will not throw traditionally. You must listen for this $stateChangeError event to
2132
+ * catch **ALL** errors.
2133
+ *
2134
+ * @param {Object} event Event object.
2135
+ * @param {State} toState The state being transitioned to.
2136
+ * @param {Object} toParams The params supplied to the `toState`.
2137
+ * @param {State} fromState The current state, pre-transition.
2138
+ * @param {Object} fromParams The params supplied to the `fromState`.
2139
+ * @param {Error} error The resolve error object.
2140
+ */
2141
+ $rootScope.$broadcast('$stateChangeError', to.self, toParams, from.self, fromParams, error);
2142
+ syncUrl();
2143
+
2144
+ return $q.reject(error);
2145
+ });
2146
+
2147
+ return transition;
2148
+ };
2149
+
2150
+ /**
2151
+ * @ngdoc function
2152
+ * @name ui.router.state.$state#is
2153
+ * @methodOf ui.router.state.$state
2154
+ *
2155
+ * @description
2156
+ * Similar to {@link ui.router.state.$state#methods_includes $state.includes},
2157
+ * but only checks for the full state name. If params is supplied then it will be
2158
+ * tested for strict equality against the current active params object, so all params
2159
+ * must match with none missing and no extras.
2160
+ *
2161
+ * @example
2162
+ * <pre>
2163
+ * $state.is('contact.details.item'); // returns true
2164
+ * $state.is(contactDetailItemStateObject); // returns true
2165
+ *
2166
+ * // everything else would return false
2167
+ * </pre>
2168
+ *
2169
+ * @param {string|object} stateName The state name or state object you'd like to check.
2170
+ * @param {object=} params A param object, e.g. `{sectionId: section.id}`, that you'd like
2171
+ * to test against the current active state.
2172
+ * @returns {boolean} Returns true if it is the state.
2173
+ */
2174
+ $state.is = function is(stateOrName, params) {
2175
+ var state = findState(stateOrName);
2176
+
2177
+ if (!isDefined(state)) {
2178
+ return undefined;
2179
+ }
2180
+
2181
+ if ($state.$current !== state) {
2182
+ return false;
2183
+ }
2184
+
2185
+ return isDefined(params) && params !== null ? angular.equals($stateParams, params) : true;
2186
+ };
2187
+
2188
+ /**
2189
+ * @ngdoc function
2190
+ * @name ui.router.state.$state#includes
2191
+ * @methodOf ui.router.state.$state
2192
+ *
2193
+ * @description
2194
+ * A method to determine if the current active state is equal to or is the child of the
2195
+ * state stateName. If any params are passed then they will be tested for a match as well.
2196
+ * Not all the parameters need to be passed, just the ones you'd like to test for equality.
2197
+ *
2198
+ * @example
2199
+ * <pre>
2200
+ * $state.$current.name = 'contacts.details.item';
2201
+ *
2202
+ * $state.includes("contacts"); // returns true
2203
+ * $state.includes("contacts.details"); // returns true
2204
+ * $state.includes("contacts.details.item"); // returns true
2205
+ * $state.includes("contacts.list"); // returns false
2206
+ * $state.includes("about"); // returns false
2207
+ * </pre>
2208
+ *
2209
+ * @description
2210
+ * Basic globing patterns will also work.
2211
+ *
2212
+ * @example
2213
+ * <pre>
2214
+ * $state.$current.name = 'contacts.details.item.url';
2215
+ *
2216
+ * $state.includes("*.details.*.*"); // returns true
2217
+ * $state.includes("*.details.**"); // returns true
2218
+ * $state.includes("**.item.**"); // returns true
2219
+ * $state.includes("*.details.item.url"); // returns true
2220
+ * $state.includes("*.details.*.url"); // returns true
2221
+ * $state.includes("*.details.*"); // returns false
2222
+ * $state.includes("item.**"); // returns false
2223
+ * </pre>
2224
+ *
2225
+ * @param {string} stateOrName A partial name to be searched for within the current state name.
2226
+ * @param {object} params A param object, e.g. `{sectionId: section.id}`,
2227
+ * that you'd like to test against the current active state.
2228
+ * @returns {boolean} Returns true if it does include the state
2229
+ */
2230
+
2231
+ $state.includes = function includes(stateOrName, params) {
2232
+ if (isString(stateOrName) && isGlob(stateOrName)) {
2233
+ if (doesStateMatchGlob(stateOrName)) {
2234
+ stateOrName = $state.$current.name;
2235
+ } else {
2236
+ return false;
2237
+ }
2238
+ }
2239
+
2240
+ var state = findState(stateOrName);
2241
+ if (!isDefined(state)) {
2242
+ return undefined;
2243
+ }
2244
+
2245
+ if (!isDefined($state.$current.includes[state.name])) {
2246
+ return false;
2247
+ }
2248
+
2249
+ var validParams = true;
2250
+ angular.forEach(params, function(value, key) {
2251
+ if (!isDefined($stateParams[key]) || $stateParams[key] !== value) {
2252
+ validParams = false;
2253
+ }
2254
+ });
2255
+ return validParams;
2256
+ };
2257
+
2258
+
2259
+ /**
2260
+ * @ngdoc function
2261
+ * @name ui.router.state.$state#href
2262
+ * @methodOf ui.router.state.$state
2263
+ *
2264
+ * @description
2265
+ * A url generation method that returns the compiled url for the given state populated with the given params.
2266
+ *
2267
+ * @example
2268
+ * <pre>
2269
+ * expect($state.href("about.person", { person: "bob" })).toEqual("/about/bob");
2270
+ * </pre>
2271
+ *
2272
+ * @param {string|object} stateOrName The state name or state object you'd like to generate a url from.
2273
+ * @param {object=} params An object of parameter values to fill the state's required parameters.
2274
+ * @param {object=} options Options object. The options are:
2275
+ *
2276
+ * - **`lossy`** - {boolean=true} - If true, and if there is no url associated with the state provided in the
2277
+ * first parameter, then the constructed href url will be built from the first navigable ancestor (aka
2278
+ * ancestor with a valid url).
2279
+ * - **`inherit`** - {boolean=false}, If `true` will inherit url parameters from current url.
2280
+ * - **`relative`** - {object=$state.$current}, When transitioning with relative path (e.g '^'),
2281
+ * defines which state to be relative from.
2282
+ * - **`absolute`** - {boolean=false}, If true will generate an absolute url, e.g. "http://www.example.com/fullurl".
2283
+ *
2284
+ * @returns {string} compiled state url
2285
+ */
2286
+ $state.href = function href(stateOrName, params, options) {
2287
+ options = extend({ lossy: true, inherit: false, absolute: false, relative: $state.$current }, options || {});
2288
+ var state = findState(stateOrName, options.relative);
2289
+ if (!isDefined(state)) return null;
2290
+
2291
+ params = inheritParams($stateParams, params || {}, $state.$current, state);
2292
+ var nav = (state && options.lossy) ? state.navigable : state;
2293
+ var url = (nav && nav.url) ? nav.url.format(normalize(state.params, params || {})) : null;
2294
+ if (!$locationProvider.html5Mode() && url) {
2295
+ url = "#" + $locationProvider.hashPrefix() + url;
2296
+ }
2297
+
2298
+ if (baseHref !== '/') {
2299
+ if ($locationProvider.html5Mode()) {
2300
+ url = baseHref.slice(0, -1) + url;
2301
+ } else if (options.absolute){
2302
+ url = baseHref.slice(1) + url;
2303
+ }
2304
+ }
2305
+
2306
+ if (options.absolute && url) {
2307
+ url = $location.protocol() + '://' +
2308
+ $location.host() +
2309
+ ($location.port() == 80 || $location.port() == 443 ? '' : ':' + $location.port()) +
2310
+ (!$locationProvider.html5Mode() && url ? '/' : '') +
2311
+ url;
2312
+ }
2313
+ return url;
2314
+ };
2315
+
2316
+ /**
2317
+ * @ngdoc function
2318
+ * @name ui.router.state.$state#get
2319
+ * @methodOf ui.router.state.$state
2320
+ *
2321
+ * @description
2322
+ * Returns the state configuration object for any specific state or all states.
2323
+ *
2324
+ * @param {string|object=} stateOrName If provided, will only get the config for
2325
+ * the requested state. If not provided, returns an array of ALL state configs.
2326
+ * @returns {object|array} State configuration object or array of all objects.
2327
+ */
2328
+ $state.get = function (stateOrName, context) {
2329
+ if (!isDefined(stateOrName)) {
2330
+ var list = [];
2331
+ forEach(states, function(state) { list.push(state.self); });
2332
+ return list;
2333
+ }
2334
+ var state = findState(stateOrName, context);
2335
+ return (state && state.self) ? state.self : null;
2336
+ };
2337
+
2338
+ function resolveState(state, params, paramsAreFiltered, inherited, dst) {
2339
+ // Make a restricted $stateParams with only the parameters that apply to this state if
2340
+ // necessary. In addition to being available to the controller and onEnter/onExit callbacks,
2341
+ // we also need $stateParams to be available for any $injector calls we make during the
2342
+ // dependency resolution process.
2343
+ var $stateParams = (paramsAreFiltered) ? params : filterByKeys(state.params, params);
2344
+ var locals = { $stateParams: $stateParams };
2345
+
2346
+ // Resolve 'global' dependencies for the state, i.e. those not specific to a view.
2347
+ // We're also including $stateParams in this; that way the parameters are restricted
2348
+ // to the set that should be visible to the state, and are independent of when we update
2349
+ // the global $state and $stateParams values.
2350
+ dst.resolve = $resolve.resolve(state.resolve, locals, dst.resolve, state);
2351
+ var promises = [ dst.resolve.then(function (globals) {
2352
+ dst.globals = globals;
2353
+ }) ];
2354
+ if (inherited) promises.push(inherited);
2355
+
2356
+ // Resolve template and dependencies for all views.
2357
+ forEach(state.views, function (view, name) {
2358
+ var injectables = (view.resolve && view.resolve !== state.resolve ? view.resolve : {});
2359
+ injectables.$template = [ function () {
2360
+ return $view.load(name, { view: view, locals: locals, params: $stateParams, notify: false }) || '';
2361
+ }];
2362
+
2363
+ promises.push($resolve.resolve(injectables, locals, dst.resolve, state).then(function (result) {
2364
+ // References to the controller (only instantiated at link time)
2365
+ if (isFunction(view.controllerProvider) || isArray(view.controllerProvider)) {
2366
+ var injectLocals = angular.extend({}, injectables, locals);
2367
+ result.$$controller = $injector.invoke(view.controllerProvider, null, injectLocals);
2368
+ } else {
2369
+ result.$$controller = view.controller;
2370
+ }
2371
+ // Provide access to the state itself for internal use
2372
+ result.$$state = state;
2373
+ result.$$controllerAs = view.controllerAs;
2374
+ dst[name] = result;
2375
+ }));
2376
+ });
2377
+
2378
+ // Wait for all the promises and then return the activation object
2379
+ return $q.all(promises).then(function (values) {
2380
+ return dst;
2381
+ });
2382
+ }
2383
+
2384
+ return $state;
2385
+ }
2386
+
2387
+ function shouldTriggerReload(to, from, locals, options) {
2388
+ if ( to === from && ((locals === from.locals && !options.reload) || (to.self.reloadOnSearch === false)) ) {
2389
+ return true;
2390
+ }
2391
+ }
2392
+ }
2393
+
2394
+ angular.module('ui.router.state')
2395
+ .value('$stateParams', {})
2396
+ .provider('$state', $StateProvider);
2397
+
2398
+
2399
+ $ViewProvider.$inject = [];
2400
+ function $ViewProvider() {
2401
+
2402
+ this.$get = $get;
2403
+ /**
2404
+ * @ngdoc object
2405
+ * @name ui.router.state.$view
2406
+ *
2407
+ * @requires ui.router.util.$templateFactory
2408
+ * @requires $rootScope
2409
+ *
2410
+ * @description
2411
+ *
2412
+ */
2413
+ $get.$inject = ['$rootScope', '$templateFactory'];
2414
+ function $get( $rootScope, $templateFactory) {
2415
+ return {
2416
+ // $view.load('full.viewName', { template: ..., controller: ..., resolve: ..., async: false, params: ... })
2417
+ /**
2418
+ * @ngdoc function
2419
+ * @name ui.router.state.$view#load
2420
+ * @methodOf ui.router.state.$view
2421
+ *
2422
+ * @description
2423
+ *
2424
+ * @param {string} name name
2425
+ * @param {object} options option object.
2426
+ */
2427
+ load: function load(name, options) {
2428
+ var result, defaults = {
2429
+ template: null, controller: null, view: null, locals: null, notify: true, async: true, params: {}
2430
+ };
2431
+ options = extend(defaults, options);
2432
+
2433
+ if (options.view) {
2434
+ result = $templateFactory.fromConfig(options.view, options.params, options.locals);
2435
+ }
2436
+ if (result && options.notify) {
2437
+ /**
2438
+ * @ngdoc event
2439
+ * @name ui.router.state.$state#$viewContentLoading
2440
+ * @eventOf ui.router.state.$view
2441
+ * @eventType broadcast on root scope
2442
+ * @description
2443
+ *
2444
+ * Fired once the view **begins loading**, *before* the DOM is rendered.
2445
+ *
2446
+ * @param {Object} event Event object.
2447
+ * @param {Object} viewConfig The view config properties (template, controller, etc).
2448
+ *
2449
+ * @example
2450
+ *
2451
+ * <pre>
2452
+ * $scope.$on('$viewContentLoading',
2453
+ * function(event, viewConfig){
2454
+ * // Access to all the view config properties.
2455
+ * // and one special property 'targetView'
2456
+ * // viewConfig.targetView
2457
+ * });
2458
+ * </pre>
2459
+ */
2460
+ $rootScope.$broadcast('$viewContentLoading', options);
2461
+ }
2462
+ return result;
2463
+ }
2464
+ };
2465
+ }
2466
+ }
2467
+
2468
+ angular.module('ui.router.state').provider('$view', $ViewProvider);
2469
+
2470
+ /**
2471
+ * @ngdoc object
2472
+ * @name ui.router.state.$uiViewScrollProvider
2473
+ *
2474
+ * @description
2475
+ * Provider that returns the {@link ui.router.state.$uiViewScroll} service function.
2476
+ */
2477
+ function $ViewScrollProvider() {
2478
+
2479
+ var useAnchorScroll = false;
2480
+
2481
+ /**
2482
+ * @ngdoc function
2483
+ * @name ui.router.state.$uiViewScrollProvider#useAnchorScroll
2484
+ * @methodOf ui.router.state.$uiViewScrollProvider
2485
+ *
2486
+ * @description
2487
+ * Reverts back to using the core [`$anchorScroll`](http://docs.angularjs.org/api/ng.$anchorScroll) service for
2488
+ * scrolling based on the url anchor.
2489
+ */
2490
+ this.useAnchorScroll = function () {
2491
+ useAnchorScroll = true;
2492
+ };
2493
+
2494
+ /**
2495
+ * @ngdoc object
2496
+ * @name ui.router.state.$uiViewScroll
2497
+ *
2498
+ * @requires $anchorScroll
2499
+ * @requires $timeout
2500
+ *
2501
+ * @description
2502
+ * When called with a jqLite element, it scrolls the element into view (after a
2503
+ * `$timeout` so the DOM has time to refresh).
2504
+ *
2505
+ * If you prefer to rely on `$anchorScroll` to scroll the view to the anchor,
2506
+ * this can be enabled by calling {@link ui.router.state.$uiViewScrollProvider#methods_useAnchorScroll `$uiViewScrollProvider.useAnchorScroll()`}.
2507
+ */
2508
+ this.$get = ['$anchorScroll', '$timeout', function ($anchorScroll, $timeout) {
2509
+ if (useAnchorScroll) {
2510
+ return $anchorScroll;
2511
+ }
2512
+
2513
+ return function ($element) {
2514
+ $timeout(function () {
2515
+ $element[0].scrollIntoView();
2516
+ }, 0, false);
2517
+ };
2518
+ }];
2519
+ }
2520
+
2521
+ angular.module('ui.router.state').provider('$uiViewScroll', $ViewScrollProvider);
2522
+
2523
+ /**
2524
+ * @ngdoc directive
2525
+ * @name ui.router.state.directive:ui-view
2526
+ *
2527
+ * @requires ui.router.state.$state
2528
+ * @requires $compile
2529
+ * @requires $controller
2530
+ * @requires $injector
2531
+ * @requires ui.router.state.$uiViewScroll
2532
+ * @requires $document
2533
+ *
2534
+ * @restrict ECA
2535
+ *
2536
+ * @description
2537
+ * The ui-view directive tells $state where to place your templates.
2538
+ *
2539
+ * @param {string=} ui-view A view name. The name should be unique amongst the other views in the
2540
+ * same state. You can have views of the same name that live in different states.
2541
+ *
2542
+ * @param {string=} autoscroll It allows you to set the scroll behavior of the browser window
2543
+ * when a view is populated. By default, $anchorScroll is overridden by ui-router's custom scroll
2544
+ * service, {@link ui.router.state.$uiViewScroll}. This custom service let's you
2545
+ * scroll ui-view elements into view when they are populated during a state activation.
2546
+ *
2547
+ * *Note: To revert back to old [`$anchorScroll`](http://docs.angularjs.org/api/ng.$anchorScroll)
2548
+ * functionality, call `$uiViewScrollProvider.useAnchorScroll()`.*
2549
+ *
2550
+ * @param {string=} onload Expression to evaluate whenever the view updates.
2551
+ *
2552
+ * @example
2553
+ * A view can be unnamed or named.
2554
+ * <pre>
2555
+ * <!-- Unnamed -->
2556
+ * <div ui-view></div>
2557
+ *
2558
+ * <!-- Named -->
2559
+ * <div ui-view="viewName"></div>
2560
+ * </pre>
2561
+ *
2562
+ * You can only have one unnamed view within any template (or root html). If you are only using a
2563
+ * single view and it is unnamed then you can populate it like so:
2564
+ * <pre>
2565
+ * <div ui-view></div>
2566
+ * $stateProvider.state("home", {
2567
+ * template: "<h1>HELLO!</h1>"
2568
+ * })
2569
+ * </pre>
2570
+ *
2571
+ * The above is a convenient shortcut equivalent to specifying your view explicitly with the {@link ui.router.state.$stateProvider#views `views`}
2572
+ * config property, by name, in this case an empty name:
2573
+ * <pre>
2574
+ * $stateProvider.state("home", {
2575
+ * views: {
2576
+ * "": {
2577
+ * template: "<h1>HELLO!</h1>"
2578
+ * }
2579
+ * }
2580
+ * })
2581
+ * </pre>
2582
+ *
2583
+ * But typically you'll only use the views property if you name your view or have more than one view
2584
+ * in the same template. There's not really a compelling reason to name a view if its the only one,
2585
+ * but you could if you wanted, like so:
2586
+ * <pre>
2587
+ * <div ui-view="main"></div>
2588
+ * </pre>
2589
+ * <pre>
2590
+ * $stateProvider.state("home", {
2591
+ * views: {
2592
+ * "main": {
2593
+ * template: "<h1>HELLO!</h1>"
2594
+ * }
2595
+ * }
2596
+ * })
2597
+ * </pre>
2598
+ *
2599
+ * Really though, you'll use views to set up multiple views:
2600
+ * <pre>
2601
+ * <div ui-view></div>
2602
+ * <div ui-view="chart"></div>
2603
+ * <div ui-view="data"></div>
2604
+ * </pre>
2605
+ *
2606
+ * <pre>
2607
+ * $stateProvider.state("home", {
2608
+ * views: {
2609
+ * "": {
2610
+ * template: "<h1>HELLO!</h1>"
2611
+ * },
2612
+ * "chart": {
2613
+ * template: "<chart_thing/>"
2614
+ * },
2615
+ * "data": {
2616
+ * template: "<data_thing/>"
2617
+ * }
2618
+ * }
2619
+ * })
2620
+ * </pre>
2621
+ *
2622
+ * Examples for `autoscroll`:
2623
+ *
2624
+ * <pre>
2625
+ * <!-- If autoscroll present with no expression,
2626
+ * then scroll ui-view into view -->
2627
+ * <ui-view autoscroll/>
2628
+ *
2629
+ * <!-- If autoscroll present with valid expression,
2630
+ * then scroll ui-view into view if expression evaluates to true -->
2631
+ * <ui-view autoscroll='true'/>
2632
+ * <ui-view autoscroll='false'/>
2633
+ * <ui-view autoscroll='scopeVariable'/>
2634
+ * </pre>
2635
+ */
2636
+ $ViewDirective.$inject = ['$state', '$injector', '$uiViewScroll'];
2637
+ function $ViewDirective( $state, $injector, $uiViewScroll) {
2638
+
2639
+ function getService() {
2640
+ return ($injector.has) ? function(service) {
2641
+ return $injector.has(service) ? $injector.get(service) : null;
2642
+ } : function(service) {
2643
+ try {
2644
+ return $injector.get(service);
2645
+ } catch (e) {
2646
+ return null;
2647
+ }
2648
+ };
2649
+ }
2650
+
2651
+ var service = getService(),
2652
+ $animator = service('$animator'),
2653
+ $animate = service('$animate');
2654
+
2655
+ // Returns a set of DOM manipulation functions based on which Angular version
2656
+ // it should use
2657
+ function getRenderer(attrs, scope) {
2658
+ var statics = function() {
2659
+ return {
2660
+ enter: function (element, target, cb) { target.after(element); cb(); },
2661
+ leave: function (element, cb) { element.remove(); cb(); }
2662
+ };
2663
+ };
2664
+
2665
+ if ($animate) {
2666
+ return {
2667
+ enter: function(element, target, cb) { $animate.enter(element, null, target, cb); },
2668
+ leave: function(element, cb) { $animate.leave(element, cb); }
2669
+ };
2670
+ }
2671
+
2672
+ if ($animator) {
2673
+ var animate = $animator && $animator(scope, attrs);
2674
+
2675
+ return {
2676
+ enter: function(element, target, cb) {animate.enter(element, null, target); cb(); },
2677
+ leave: function(element, cb) { animate.leave(element); cb(); }
2678
+ };
2679
+ }
2680
+
2681
+ return statics();
2682
+ }
2683
+
2684
+ var directive = {
2685
+ restrict: 'ECA',
2686
+ terminal: true,
2687
+ priority: 400,
2688
+ transclude: 'element',
2689
+ compile: function (tElement, tAttrs, $transclude) {
2690
+ return function (scope, $element, attrs) {
2691
+ var previousEl, currentEl, currentScope, latestLocals,
2692
+ onloadExp = attrs.onload || '',
2693
+ autoScrollExp = attrs.autoscroll,
2694
+ renderer = getRenderer(attrs, scope);
2695
+
2696
+ scope.$on('$stateChangeSuccess', function() {
2697
+ updateView(false);
2698
+ });
2699
+ scope.$on('$viewContentLoading', function() {
2700
+ updateView(false);
2701
+ });
2702
+
2703
+ updateView(true);
2704
+
2705
+ function cleanupLastView() {
2706
+ if (previousEl) {
2707
+ previousEl.remove();
2708
+ previousEl = null;
2709
+ }
2710
+
2711
+ if (currentScope) {
2712
+ currentScope.$destroy();
2713
+ currentScope = null;
2714
+ }
2715
+
2716
+ if (currentEl) {
2717
+ renderer.leave(currentEl, function() {
2718
+ previousEl = null;
2719
+ });
2720
+
2721
+ previousEl = currentEl;
2722
+ currentEl = null;
2723
+ }
2724
+ }
2725
+
2726
+ function updateView(firstTime) {
2727
+ var newScope = scope.$new(),
2728
+ name = currentEl && currentEl.data('$uiViewName'),
2729
+ previousLocals = name && $state.$current && $state.$current.locals[name];
2730
+
2731
+ if (!firstTime && previousLocals === latestLocals) return; // nothing to do
2732
+
2733
+ var clone = $transclude(newScope, function(clone) {
2734
+ renderer.enter(clone, $element, function onUiViewEnter() {
2735
+ if (angular.isDefined(autoScrollExp) && !autoScrollExp || scope.$eval(autoScrollExp)) {
2736
+ $uiViewScroll(clone);
2737
+ }
2738
+ });
2739
+ cleanupLastView();
2740
+ });
2741
+
2742
+ latestLocals = $state.$current.locals[clone.data('$uiViewName')];
2743
+
2744
+ currentEl = clone;
2745
+ currentScope = newScope;
2746
+ /**
2747
+ * @ngdoc event
2748
+ * @name ui.router.state.directive:ui-view#$viewContentLoaded
2749
+ * @eventOf ui.router.state.directive:ui-view
2750
+ * @eventType emits on ui-view directive scope
2751
+ * @description *
2752
+ * Fired once the view is **loaded**, *after* the DOM is rendered.
2753
+ *
2754
+ * @param {Object} event Event object.
2755
+ */
2756
+ currentScope.$emit('$viewContentLoaded');
2757
+ currentScope.$eval(onloadExp);
2758
+ }
2759
+ };
2760
+ }
2761
+ };
2762
+
2763
+ return directive;
2764
+ }
2765
+
2766
+ $ViewDirectiveFill.$inject = ['$compile', '$controller', '$state'];
2767
+ function $ViewDirectiveFill ($compile, $controller, $state) {
2768
+ return {
2769
+ restrict: 'ECA',
2770
+ priority: -400,
2771
+ compile: function (tElement) {
2772
+ var initial = tElement.html();
2773
+ return function (scope, $element, attrs) {
2774
+ var name = attrs.uiView || attrs.name || '',
2775
+ inherited = $element.inheritedData('$uiView');
2776
+
2777
+ if (name.indexOf('@') < 0) {
2778
+ name = name + '@' + (inherited ? inherited.state.name : '');
2779
+ }
2780
+
2781
+ $element.data('$uiViewName', name);
2782
+
2783
+ var current = $state.$current,
2784
+ locals = current && current.locals[name];
2785
+
2786
+ if (! locals) {
2787
+ return;
2788
+ }
2789
+
2790
+ $element.data('$uiView', { name: name, state: locals.$$state });
2791
+ $element.html(locals.$template ? locals.$template : initial);
2792
+
2793
+ var link = $compile($element.contents());
2794
+
2795
+ if (locals.$$controller) {
2796
+ locals.$scope = scope;
2797
+ var controller = $controller(locals.$$controller, locals);
2798
+ if (locals.$$controllerAs) {
2799
+ scope[locals.$$controllerAs] = controller;
2800
+ }
2801
+ $element.data('$ngControllerController', controller);
2802
+ $element.children().data('$ngControllerController', controller);
2803
+ }
2804
+
2805
+ link(scope);
2806
+ };
2807
+ }
2808
+ };
2809
+ }
2810
+
2811
+ angular.module('ui.router.state').directive('uiView', $ViewDirective);
2812
+ angular.module('ui.router.state').directive('uiView', $ViewDirectiveFill);
2813
+
2814
+ function parseStateRef(ref) {
2815
+ var parsed = ref.replace(/\n/g, " ").match(/^([^(]+?)\s*(\((.*)\))?$/);
2816
+ if (!parsed || parsed.length !== 4) throw new Error("Invalid state ref '" + ref + "'");
2817
+ return { state: parsed[1], paramExpr: parsed[3] || null };
2818
+ }
2819
+
2820
+ function stateContext(el) {
2821
+ var stateData = el.parent().inheritedData('$uiView');
2822
+
2823
+ if (stateData && stateData.state && stateData.state.name) {
2824
+ return stateData.state;
2825
+ }
2826
+ }
2827
+
2828
+ /**
2829
+ * @ngdoc directive
2830
+ * @name ui.router.state.directive:ui-sref
2831
+ *
2832
+ * @requires ui.router.state.$state
2833
+ * @requires $timeout
2834
+ *
2835
+ * @restrict A
2836
+ *
2837
+ * @description
2838
+ * A directive that binds a link (`<a>` tag) to a state. If the state has an associated
2839
+ * URL, the directive will automatically generate & update the `href` attribute via
2840
+ * the {@link ui.router.state.$state#methods_href $state.href()} method. Clicking
2841
+ * the link will trigger a state transition with optional parameters.
2842
+ *
2843
+ * Also middle-clicking, right-clicking, and ctrl-clicking on the link will be
2844
+ * handled natively by the browser.
2845
+ *
2846
+ * You can also use relative state paths within ui-sref, just like the relative
2847
+ * paths passed to `$state.go()`. You just need to be aware that the path is relative
2848
+ * to the state that the link lives in, in other words the state that loaded the
2849
+ * template containing the link.
2850
+ *
2851
+ * You can specify options to pass to {@link ui.router.state.$state#go $state.go()}
2852
+ * using the `ui-sref-opts` attribute. Options are restricted to `location`, `inherit`,
2853
+ * and `reload`.
2854
+ *
2855
+ * @example
2856
+ * Here's an example of how you'd use ui-sref and how it would compile. If you have the
2857
+ * following template:
2858
+ * <pre>
2859
+ * <a ui-sref="home">Home</a> | <a ui-sref="about">About</a>
2860
+ *
2861
+ * <ul>
2862
+ * <li ng-repeat="contact in contacts">
2863
+ * <a ui-sref="contacts.detail({ id: contact.id })">{{ contact.name }}</a>
2864
+ * </li>
2865
+ * </ul>
2866
+ * </pre>
2867
+ *
2868
+ * Then the compiled html would be (assuming Html5Mode is off):
2869
+ * <pre>
2870
+ * <a href="#/home" ui-sref="home">Home</a> | <a href="#/about" ui-sref="about">About</a>
2871
+ *
2872
+ * <ul>
2873
+ * <li ng-repeat="contact in contacts">
2874
+ * <a href="#/contacts/1" ui-sref="contacts.detail({ id: contact.id })">Joe</a>
2875
+ * </li>
2876
+ * <li ng-repeat="contact in contacts">
2877
+ * <a href="#/contacts/2" ui-sref="contacts.detail({ id: contact.id })">Alice</a>
2878
+ * </li>
2879
+ * <li ng-repeat="contact in contacts">
2880
+ * <a href="#/contacts/3" ui-sref="contacts.detail({ id: contact.id })">Bob</a>
2881
+ * </li>
2882
+ * </ul>
2883
+ *
2884
+ * <a ui-sref="home" ui-sref-opts="{reload: true}">Home</a>
2885
+ * </pre>
2886
+ *
2887
+ * @param {string} ui-sref 'stateName' can be any valid absolute or relative state
2888
+ * @param {Object} ui-sref-opts options to pass to {@link ui.router.state.$state#go $state.go()}
2889
+ */
2890
+ $StateRefDirective.$inject = ['$state', '$timeout'];
2891
+ function $StateRefDirective($state, $timeout) {
2892
+ var allowedOptions = ['location', 'inherit', 'reload'];
2893
+
2894
+ return {
2895
+ restrict: 'A',
2896
+ require: '?^uiSrefActive',
2897
+ link: function(scope, element, attrs, uiSrefActive) {
2898
+ var ref = parseStateRef(attrs.uiSref);
2899
+ var params = null, url = null, base = stateContext(element) || $state.$current;
2900
+ var isForm = element[0].nodeName === "FORM";
2901
+ var attr = isForm ? "action" : "href", nav = true;
2902
+
2903
+ var options = {
2904
+ relative: base
2905
+ };
2906
+ var optionsOverride = scope.$eval(attrs.uiSrefOpts) || {};
2907
+ angular.forEach(allowedOptions, function(option) {
2908
+ if (option in optionsOverride) {
2909
+ options[option] = optionsOverride[option];
2910
+ }
2911
+ });
2912
+
2913
+ var update = function(newVal) {
2914
+ if (newVal) params = newVal;
2915
+ if (!nav) return;
2916
+
2917
+ var newHref = $state.href(ref.state, params, options);
2918
+
2919
+ if (uiSrefActive) {
2920
+ uiSrefActive.$$setStateInfo(ref.state, params);
2921
+ }
2922
+ if (!newHref) {
2923
+ nav = false;
2924
+ return false;
2925
+ }
2926
+ element[0][attr] = newHref;
2927
+ };
2928
+
2929
+ if (ref.paramExpr) {
2930
+ scope.$watch(ref.paramExpr, function(newVal, oldVal) {
2931
+ if (newVal !== params) update(newVal);
2932
+ }, true);
2933
+ params = scope.$eval(ref.paramExpr);
2934
+ }
2935
+ update();
2936
+
2937
+ if (isForm) return;
2938
+
2939
+ element.bind("click", function(e) {
2940
+ var button = e.which || e.button;
2941
+ if ( !(button > 1 || e.ctrlKey || e.metaKey || e.shiftKey || element.attr('target')) ) {
2942
+ // HACK: This is to allow ng-clicks to be processed before the transition is initiated:
2943
+ $timeout(function() {
2944
+ $state.go(ref.state, params, options);
2945
+ });
2946
+ e.preventDefault();
2947
+ }
2948
+ });
2949
+ }
2950
+ };
2951
+ }
2952
+
2953
+ /**
2954
+ * @ngdoc directive
2955
+ * @name ui.router.state.directive:ui-sref-active
2956
+ *
2957
+ * @requires ui.router.state.$state
2958
+ * @requires ui.router.state.$stateParams
2959
+ * @requires $interpolate
2960
+ *
2961
+ * @restrict A
2962
+ *
2963
+ * @description
2964
+ * A directive working alongside ui-sref to add classes to an element when the
2965
+ * related ui-sref directive's state is active, and removing them when it is inactive.
2966
+ * The primary use-case is to simplify the special appearance of navigation menus
2967
+ * relying on `ui-sref`, by having the "active" state's menu button appear different,
2968
+ * distinguishing it from the inactive menu items.
2969
+ *
2970
+ * @example
2971
+ * Given the following template:
2972
+ * <pre>
2973
+ * <ul>
2974
+ * <li ui-sref-active="active" class="item">
2975
+ * <a href ui-sref="app.user({user: 'bilbobaggins'})">@bilbobaggins</a>
2976
+ * </li>
2977
+ * </ul>
2978
+ * </pre>
2979
+ *
2980
+ * When the app state is "app.user", and contains the state parameter "user" with value "bilbobaggins",
2981
+ * the resulting HTML will appear as (note the 'active' class):
2982
+ * <pre>
2983
+ * <ul>
2984
+ * <li ui-sref-active="active" class="item active">
2985
+ * <a ui-sref="app.user({user: 'bilbobaggins'})" href="/users/bilbobaggins">@bilbobaggins</a>
2986
+ * </li>
2987
+ * </ul>
2988
+ * </pre>
2989
+ *
2990
+ * The class name is interpolated **once** during the directives link time (any further changes to the
2991
+ * interpolated value are ignored).
2992
+ *
2993
+ * Multiple classes may be specified in a space-separated format:
2994
+ * <pre>
2995
+ * <ul>
2996
+ * <li ui-sref-active='class1 class2 class3'>
2997
+ * <a ui-sref="app.user">link</a>
2998
+ * </li>
2999
+ * </ul>
3000
+ * </pre>
3001
+ */
3002
+ $StateActiveDirective.$inject = ['$state', '$stateParams', '$interpolate'];
3003
+ function $StateActiveDirective($state, $stateParams, $interpolate) {
3004
+ return {
3005
+ restrict: "A",
3006
+ controller: ['$scope', '$element', '$attrs', function($scope, $element, $attrs) {
3007
+ var state, params, activeClass;
3008
+
3009
+ // There probably isn't much point in $observing this
3010
+ activeClass = $interpolate($attrs.uiSrefActive || '', false)($scope);
3011
+
3012
+ // Allow uiSref to communicate with uiSrefActive
3013
+ this.$$setStateInfo = function(newState, newParams) {
3014
+ state = $state.get(newState, stateContext($element));
3015
+ params = newParams;
3016
+ update();
3017
+ };
3018
+
3019
+ $scope.$on('$stateChangeSuccess', update);
3020
+
3021
+ // Update route state
3022
+ function update() {
3023
+ if ($state.$current.self === state && matchesParams()) {
3024
+ $element.addClass(activeClass);
3025
+ } else {
3026
+ $element.removeClass(activeClass);
3027
+ }
3028
+ }
3029
+
3030
+ function matchesParams() {
3031
+ return !params || equalForKeys(params, $stateParams);
3032
+ }
3033
+ }]
3034
+ };
3035
+ }
3036
+
3037
+ angular.module('ui.router.state')
3038
+ .directive('uiSref', $StateRefDirective)
3039
+ .directive('uiSrefActive', $StateActiveDirective);
3040
+
3041
+ /**
3042
+ * @ngdoc filter
3043
+ * @name ui.router.state.filter:isState
3044
+ *
3045
+ * @requires ui.router.state.$state
3046
+ *
3047
+ * @description
3048
+ * Translates to {@link ui.router.state.$state#methods_is $state.is("stateName")}.
3049
+ */
3050
+ $IsStateFilter.$inject = ['$state'];
3051
+ function $IsStateFilter($state) {
3052
+ return function(state) {
3053
+ return $state.is(state);
3054
+ };
3055
+ }
3056
+
3057
+ /**
3058
+ * @ngdoc filter
3059
+ * @name ui.router.state.filter:includedByState
3060
+ *
3061
+ * @requires ui.router.state.$state
3062
+ *
3063
+ * @description
3064
+ * Translates to {@link ui.router.state.$state#methods_includes $state.includes('fullOrPartialStateName')}.
3065
+ */
3066
+ $IncludedByStateFilter.$inject = ['$state'];
3067
+ function $IncludedByStateFilter($state) {
3068
+ return function(state) {
3069
+ return $state.includes(state);
3070
+ };
3071
+ }
3072
+
3073
+ angular.module('ui.router.state')
3074
+ .filter('isState', $IsStateFilter)
3075
+ .filter('includedByState', $IncludedByStateFilter);
3076
+
3077
+ /*
3078
+ * @ngdoc object
3079
+ * @name ui.router.compat.$routeProvider
3080
+ *
3081
+ * @requires ui.router.state.$stateProvider
3082
+ * @requires ui.router.router.$urlRouterProvider
3083
+ *
3084
+ * @description
3085
+ * `$routeProvider` of the `ui.router.compat` module overwrites the existing
3086
+ * `routeProvider` from the core. This is done to provide compatibility between
3087
+ * the UI Router and the core router.
3088
+ *
3089
+ * It also provides a `when()` method to register routes that map to certain urls.
3090
+ * Behind the scenes it actually delegates either to
3091
+ * {@link ui.router.router.$urlRouterProvider $urlRouterProvider} or to the
3092
+ * {@link ui.router.state.$stateProvider $stateProvider} to postprocess the given
3093
+ * router definition object.
3094
+ */
3095
+ $RouteProvider.$inject = ['$stateProvider', '$urlRouterProvider'];
3096
+ function $RouteProvider( $stateProvider, $urlRouterProvider) {
3097
+
3098
+ var routes = [];
3099
+
3100
+ onEnterRoute.$inject = ['$$state'];
3101
+ function onEnterRoute( $$state) {
3102
+ /*jshint validthis: true */
3103
+ this.locals = $$state.locals.globals;
3104
+ this.params = this.locals.$stateParams;
3105
+ }
3106
+
3107
+ function onExitRoute() {
3108
+ /*jshint validthis: true */
3109
+ this.locals = null;
3110
+ this.params = null;
3111
+ }
3112
+
3113
+ this.when = when;
3114
+ /*
3115
+ * @ngdoc function
3116
+ * @name ui.router.compat.$routeProvider#when
3117
+ * @methodOf ui.router.compat.$routeProvider
3118
+ *
3119
+ * @description
3120
+ * Registers a route with a given route definition object. The route definition
3121
+ * object has the same interface the angular core route definition object has.
3122
+ *
3123
+ * @example
3124
+ * <pre>
3125
+ * var app = angular.module('app', ['ui.router.compat']);
3126
+ *
3127
+ * app.config(function ($routeProvider) {
3128
+ * $routeProvider.when('home', {
3129
+ * controller: function () { ... },
3130
+ * templateUrl: 'path/to/template'
3131
+ * });
3132
+ * });
3133
+ * </pre>
3134
+ *
3135
+ * @param {string} url URL as string
3136
+ * @param {object} route Route definition object
3137
+ *
3138
+ * @return {object} $routeProvider - $routeProvider instance
3139
+ */
3140
+ function when(url, route) {
3141
+ /*jshint validthis: true */
3142
+ if (route.redirectTo != null) {
3143
+ // Redirect, configure directly on $urlRouterProvider
3144
+ var redirect = route.redirectTo, handler;
3145
+ if (isString(redirect)) {
3146
+ handler = redirect; // leave $urlRouterProvider to handle
3147
+ } else if (isFunction(redirect)) {
3148
+ // Adapt to $urlRouterProvider API
3149
+ handler = function (params, $location) {
3150
+ return redirect(params, $location.path(), $location.search());
3151
+ };
3152
+ } else {
3153
+ throw new Error("Invalid 'redirectTo' in when()");
3154
+ }
3155
+ $urlRouterProvider.when(url, handler);
3156
+ } else {
3157
+ // Regular route, configure as state
3158
+ $stateProvider.state(inherit(route, {
3159
+ parent: null,
3160
+ name: 'route:' + encodeURIComponent(url),
3161
+ url: url,
3162
+ onEnter: onEnterRoute,
3163
+ onExit: onExitRoute
3164
+ }));
3165
+ }
3166
+ routes.push(route);
3167
+ return this;
3168
+ }
3169
+
3170
+ /*
3171
+ * @ngdoc object
3172
+ * @name ui.router.compat.$route
3173
+ *
3174
+ * @requires ui.router.state.$state
3175
+ * @requires $rootScope
3176
+ * @requires $routeParams
3177
+ *
3178
+ * @property {object} routes - Array of registered routes.
3179
+ * @property {object} params - Current route params as object.
3180
+ * @property {string} current - Name of the current route.
3181
+ *
3182
+ * @description
3183
+ * The `$route` service provides interfaces to access defined routes. It also let's
3184
+ * you access route params through `$routeParams` service, so you have fully
3185
+ * control over all the stuff you would actually get from angular's core `$route`
3186
+ * service.
3187
+ */
3188
+ this.$get = $get;
3189
+ $get.$inject = ['$state', '$rootScope', '$routeParams'];
3190
+ function $get( $state, $rootScope, $routeParams) {
3191
+
3192
+ var $route = {
3193
+ routes: routes,
3194
+ params: $routeParams,
3195
+ current: undefined
3196
+ };
3197
+
3198
+ function stateAsRoute(state) {
3199
+ return (state.name !== '') ? state : undefined;
3200
+ }
3201
+
3202
+ $rootScope.$on('$stateChangeStart', function (ev, to, toParams, from, fromParams) {
3203
+ $rootScope.$broadcast('$routeChangeStart', stateAsRoute(to), stateAsRoute(from));
3204
+ });
3205
+
3206
+ $rootScope.$on('$stateChangeSuccess', function (ev, to, toParams, from, fromParams) {
3207
+ $route.current = stateAsRoute(to);
3208
+ $rootScope.$broadcast('$routeChangeSuccess', stateAsRoute(to), stateAsRoute(from));
3209
+ copy(toParams, $route.params);
3210
+ });
3211
+
3212
+ $rootScope.$on('$stateChangeError', function (ev, to, toParams, from, fromParams, error) {
3213
+ $rootScope.$broadcast('$routeChangeError', stateAsRoute(to), stateAsRoute(from), error);
3214
+ });
3215
+
3216
+ return $route;
3217
+ }
3218
+ }
3219
+
3220
+ angular.module('ui.router.compat')
3221
+ .provider('$route', $RouteProvider)
3222
+ .directive('ngView', $ViewDirective);
3223
+ })(window, window.angular);