voltar 0.0.23

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