kms 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (196) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/kms/application.js +1 -0
  3. data/app/assets/javascripts/kms/application/controllers/assets_controller.coffee.erb +14 -4
  4. data/app/assets/javascripts/kms/application/controllers/pages_controller.coffee.erb +12 -2
  5. data/app/assets/javascripts/kms/application/controllers/snippets_controller.coffee.erb +13 -3
  6. data/app/assets/javascripts/kms/application/controllers/templates_controller.coffee.erb +13 -3
  7. data/app/assets/javascripts/kms/application/controllers/users_controller.coffee +5 -5
  8. data/app/assets/javascripts/kms/application/module.coffee +6 -2
  9. data/app/assets/javascripts/kms/application/routes.coffee.erb +10 -0
  10. data/app/assets/javascripts/templates/assets/edit.html.slim +2 -1
  11. data/app/assets/javascripts/templates/assets/form.html.slim +1 -1
  12. data/app/assets/javascripts/templates/pages/edit.html.slim +1 -0
  13. data/app/assets/javascripts/templates/shared/hotkey_notification.html.slim +6 -0
  14. data/app/assets/javascripts/templates/snippets/edit.html.slim +1 -0
  15. data/app/assets/javascripts/templates/templates/edit.html.slim +1 -0
  16. data/app/assets/javascripts/templates/users/edit.html.slim +5 -0
  17. data/app/assets/javascripts/templates/users/form.html.slim +3 -2
  18. data/app/assets/javascripts/templates/users/index.html.slim +2 -1
  19. data/app/assets/stylesheets/kms/custom.css.scss +10 -0
  20. data/app/controllers/kms/assets_controller.rb +6 -3
  21. data/app/controllers/kms/users_controller.rb +14 -0
  22. data/app/services/kms/resource_service.rb +3 -1
  23. data/app/views/layouts/kms/kms.html.erb +1 -1
  24. data/config/initializers/devise.rb +9 -0
  25. data/config/locales/en.yml +12 -0
  26. data/config/locales/ru.yml +12 -0
  27. data/config/routes.rb +1 -1
  28. data/lib/kms/engine.rb +1 -1
  29. data/lib/kms/version.rb +1 -1
  30. data/spec/controllers/kms/assets_controller_spec.rb +28 -10
  31. data/spec/controllers/kms/users_controller_spec.rb +23 -0
  32. data/spec/internal/config/routes.rb +1 -1
  33. data/spec/internal/log/test.log +0 -105823
  34. data/vendor/assets/bower.json +5 -4
  35. data/vendor/assets/bower_components/angular-cookies/angular-cookies.js +22 -18
  36. data/vendor/assets/bower_components/angular-cookies/angular-cookies.min.js +4 -4
  37. data/vendor/assets/bower_components/angular-cookies/angular-cookies.min.js.map +2 -2
  38. data/vendor/assets/bower_components/angular-cookies/bower.json +2 -2
  39. data/vendor/assets/bower_components/angular-cookies/package.json +1 -1
  40. data/vendor/assets/bower_components/angular-hotkeys/Gruntfile.js +118 -0
  41. data/vendor/assets/bower_components/angular-hotkeys/LICENSE +20 -0
  42. data/vendor/assets/bower_components/angular-hotkeys/README.md +248 -0
  43. data/vendor/assets/bower_components/angular-hotkeys/bower.json +19 -0
  44. data/vendor/assets/bower_components/angular-hotkeys/build/hotkeys.css +110 -0
  45. data/vendor/assets/bower_components/angular-hotkeys/build/hotkeys.js +1661 -0
  46. data/vendor/assets/bower_components/angular-hotkeys/build/hotkeys.min.css +1 -0
  47. data/vendor/assets/bower_components/angular-hotkeys/build/hotkeys.min.js +7 -0
  48. data/vendor/assets/bower_components/angular-hotkeys/package.json +45 -0
  49. data/vendor/assets/bower_components/angular-hotkeys/src/hotkeys.css +104 -0
  50. data/vendor/assets/bower_components/angular-hotkeys/src/hotkeys.js +633 -0
  51. data/vendor/assets/bower_components/angular-loading-bar/CHANGELOG.md +33 -0
  52. data/vendor/assets/bower_components/angular-loading-bar/CONTRIBUTING.md +17 -0
  53. data/vendor/assets/bower_components/angular-loading-bar/Gruntfile.js +9 -1
  54. data/vendor/assets/bower_components/angular-loading-bar/ISSUE_TEMPLATE.md +14 -0
  55. data/vendor/assets/bower_components/angular-loading-bar/PULL_REQUEST_TEMPLATE.md +13 -0
  56. data/vendor/assets/bower_components/angular-loading-bar/README.md +30 -3
  57. data/vendor/assets/bower_components/angular-loading-bar/bower.json +11 -6
  58. data/vendor/assets/bower_components/angular-loading-bar/build/loading-bar.css +5 -5
  59. data/vendor/assets/bower_components/angular-loading-bar/build/loading-bar.js +39 -12
  60. data/vendor/assets/bower_components/angular-loading-bar/build/loading-bar.min.css +1 -8
  61. data/vendor/assets/bower_components/angular-loading-bar/build/loading-bar.min.js +3 -3
  62. data/vendor/assets/bower_components/angular-loading-bar/index.js +2 -0
  63. data/vendor/assets/bower_components/angular-loading-bar/package.json +12 -15
  64. data/vendor/assets/bower_components/angular-loading-bar/src/loading-bar.css +3 -3
  65. data/vendor/assets/bower_components/angular-loading-bar/src/loading-bar.js +37 -10
  66. data/vendor/assets/bower_components/angular-sanitize/angular-sanitize.js +504 -386
  67. data/vendor/assets/bower_components/angular-sanitize/angular-sanitize.min.js +13 -12
  68. data/vendor/assets/bower_components/angular-sanitize/angular-sanitize.min.js.map +3 -3
  69. data/vendor/assets/bower_components/angular-sanitize/bower.json +2 -2
  70. data/vendor/assets/bower_components/angular-sanitize/package.json +1 -1
  71. data/vendor/assets/bower_components/angular-ui-router/CHANGELOG.md +1410 -0
  72. data/vendor/assets/bower_components/angular-ui-router/CONTRIBUTING.md +64 -16
  73. data/vendor/assets/bower_components/angular-ui-router/DOCS.md +48 -0
  74. data/vendor/assets/bower_components/angular-ui-router/ISSUE_TEMPLATE.md +53 -0
  75. data/vendor/assets/bower_components/angular-ui-router/LICENSE +1 -1
  76. data/vendor/assets/bower_components/angular-ui-router/README.md +24 -211
  77. data/vendor/assets/bower_components/angular-ui-router/artifacts.json +8 -0
  78. data/vendor/assets/bower_components/angular-ui-router/bower.json +1 -23
  79. data/vendor/assets/bower_components/angular-ui-router/karma.conf.js +105 -0
  80. data/vendor/assets/bower_components/angular-ui-router/release/angular-ui-router.js +9744 -3901
  81. data/vendor/assets/bower_components/angular-ui-router/release/angular-ui-router.js.map +192 -0
  82. data/vendor/assets/bower_components/angular-ui-router/release/angular-ui-router.min.js +9 -4
  83. data/vendor/assets/bower_components/angular-ui-router/release/angular-ui-router.min.js.map +1679 -0
  84. data/vendor/assets/bower_components/angular-ui-router/release/resolveService.js +83 -0
  85. data/vendor/assets/bower_components/angular-ui-router/release/resolveService.js.map +19 -0
  86. data/vendor/assets/bower_components/angular-ui-router/release/resolveService.min.js +8 -0
  87. data/vendor/assets/bower_components/angular-ui-router/release/resolveService.min.js.map +47 -0
  88. data/vendor/assets/bower_components/angular-ui-router/release/stateEvents.js +294 -0
  89. data/vendor/assets/bower_components/angular-ui-router/release/stateEvents.js.map +17 -0
  90. data/vendor/assets/bower_components/angular-ui-router/release/stateEvents.min.js +8 -0
  91. data/vendor/assets/bower_components/angular-ui-router/release/stateEvents.min.js.map +102 -0
  92. data/vendor/assets/bower_components/angular-ui-router/release/ui-router-angularjs.js +2014 -0
  93. data/vendor/assets/bower_components/angular-ui-router/release/ui-router-angularjs.js.map +70 -0
  94. data/vendor/assets/bower_components/angular-ui-router/release/ui-router-angularjs.min.js +9 -0
  95. data/vendor/assets/bower_components/angular-ui-router/release/ui-router-angularjs.min.js.map +541 -0
  96. data/vendor/assets/bower_components/angular-ui-router/rollup.config.js +116 -0
  97. data/vendor/assets/bower_components/angular-ui-router/tslint.json +60 -0
  98. data/vendor/assets/bower_components/angular-ui-router/yarn.lock +4146 -0
  99. data/vendor/assets/bower_components/angular-ui-tree/yarn.lock +4945 -0
  100. data/vendor/assets/bower_components/angular/angular.js +4019 -2449
  101. data/vendor/assets/bower_components/angular/angular.min.js +331 -319
  102. data/vendor/assets/bower_components/angular/angular.min.js.gzip +0 -0
  103. data/vendor/assets/bower_components/angular/angular.min.js.map +3 -3
  104. data/vendor/assets/bower_components/angular/bower.json +1 -1
  105. data/vendor/assets/bower_components/angular/package.json +1 -1
  106. data/vendor/assets/bower_components/angularjs-dropdown-multiselect/LICENSE +21 -0
  107. data/vendor/assets/bower_components/angularjs-dropdown-multiselect/README.md +14 -14
  108. data/vendor/assets/bower_components/angularjs-dropdown-multiselect/bower.json +25 -12
  109. data/vendor/assets/bower_components/angularjs-dropdown-multiselect/development_index.html +59 -52
  110. data/vendor/assets/bower_components/angularjs-dropdown-multiselect/dist/angularjs-dropdown-multiselect.min.js +1 -1
  111. data/vendor/assets/bower_components/angularjs-dropdown-multiselect/index.html +73 -0
  112. data/vendor/assets/bower_components/angularjs-dropdown-multiselect/package.json +19 -7
  113. data/vendor/assets/bower_components/angularjs-dropdown-multiselect/pages/javascripts/pages/home/ExampleCtrl.js +126 -3
  114. data/vendor/assets/bower_components/angularjs-dropdown-multiselect/pages/javascripts/pages/home/home.html +1262 -852
  115. data/vendor/assets/bower_components/angularjs-dropdown-multiselect/pages/stylesheets/stylesheet.css +10 -5
  116. data/vendor/assets/bower_components/angularjs-dropdown-multiselect/src/angularjs-dropdown-multiselect.js +612 -287
  117. metadata +66 -169
  118. data/spec/internal/config/database.yml +0 -7
  119. data/spec/internal/public/uploads/kms/asset/file/1/avatar.jpg +0 -0
  120. data/spec/internal/public/uploads/kms/asset/file/2/avatar.jpg +0 -0
  121. data/spec/internal/public/uploads/kms/asset/file/2/style.css +0 -1
  122. data/spec/internal/public/uploads/kms/asset/file/3/style.css +0 -1
  123. data/spec/internal/public/uploads/kms/asset/file/4/style.css +0 -1
  124. data/spec/internal/public/uploads/tmp/1500976987-41025-0002-0883/style.css +0 -1
  125. data/spec/internal/public/uploads/tmp/1500977082-41195-0002-6495/style.css +0 -1
  126. data/spec/internal/public/uploads/tmp/1500977109-41364-0002-4518/style.css +0 -1
  127. data/spec/internal/public/uploads/tmp/1500977152-41405-0002-2345/style.css +0 -1
  128. data/spec/internal/public/uploads/tmp/1500977327-41694-0002-5448/style.css +0 -1
  129. data/spec/internal/public/uploads/tmp/1500977376-41732-0002-7916/style.css +0 -1
  130. data/spec/internal/public/uploads/tmp/1500977392-41759-0002-7593/style.css +0 -1
  131. data/spec/internal/public/uploads/tmp/1500977410-42259-0002-7527/style.css +0 -1
  132. data/spec/internal/public/uploads/tmp/1500977429-42306-0002-5937/style.css +0 -1
  133. data/spec/internal/public/uploads/tmp/1500977437-42324-0002-5880/style.css +0 -1
  134. data/spec/internal/public/uploads/tmp/1500983228-53594-0002-4559/style.css +0 -1
  135. data/spec/internal/public/uploads/tmp/1500983284-53632-0002-6590/style.css +0 -1
  136. data/spec/internal/public/uploads/tmp/1500983360-53784-0002-7289/style.css +0 -1
  137. data/spec/internal/public/uploads/tmp/1500983469-54321-0002-0386/avatar.jpg +0 -0
  138. data/spec/internal/public/uploads/tmp/1500983469-54321-0004-5691/style.css +0 -1
  139. data/spec/internal/public/uploads/tmp/1500983511-54352-0002-5720/avatar.jpg +0 -0
  140. data/spec/internal/public/uploads/tmp/1500983511-54352-0004-1399/style.css +0 -1
  141. data/spec/internal/public/uploads/tmp/1500983610-54507-0002-4280/avatar.jpg +0 -0
  142. data/spec/internal/public/uploads/tmp/1500983610-54507-0004-9758/style.css +0 -1
  143. data/spec/internal/public/uploads/tmp/1500984466-57012-0002-4146/avatar.jpg +0 -0
  144. data/spec/internal/public/uploads/tmp/1500984466-57012-0004-5895/style.css +0 -1
  145. data/spec/internal/public/uploads/tmp/1500984509-57158-0002-9657/avatar.jpg +0 -0
  146. data/spec/internal/public/uploads/tmp/1500984509-57158-0004-5003/style.css +0 -1
  147. data/spec/internal/public/uploads/tmp/1500984616-57697-0002-7201/avatar.jpg +0 -0
  148. data/spec/internal/public/uploads/tmp/1500984616-57697-0004-6255/style.css +0 -1
  149. data/spec/internal/public/uploads/tmp/1500985257-58947-0002-3629/avatar.jpg +0 -0
  150. data/spec/internal/public/uploads/tmp/1500985257-58947-0004-5338/style.css +0 -1
  151. data/spec/internal/public/uploads/tmp/1500985407-58947-0006-5929/style.css +0 -1
  152. data/spec/internal/public/uploads/tmp/1500985473-59264-0002-0397/avatar.jpg +0 -0
  153. data/spec/internal/public/uploads/tmp/1500985473-59264-0004-6493/style.css +0 -1
  154. data/spec/internal/public/uploads/tmp/1500985475-59264-0007-8674/style.css +0 -1
  155. data/spec/internal/public/uploads/tmp/1500985538-59468-0002-9206/avatar.jpg +0 -0
  156. data/spec/internal/public/uploads/tmp/1500985538-59468-0004-2586/style.css +0 -1
  157. data/spec/internal/public/uploads/tmp/1500985538-59468-0007-6200/style.css +0 -1
  158. data/spec/internal/public/uploads/tmp/1500988358-65877-0002-4528/avatar.jpg +0 -0
  159. data/spec/internal/public/uploads/tmp/1500988358-65877-0004-5904/style.css +0 -1
  160. data/spec/internal/public/uploads/tmp/1500988358-65877-0007-7320/style.css +0 -1
  161. data/spec/internal/public/uploads/tmp/1500988407-65916-0002-3138/avatar.jpg +0 -0
  162. data/spec/internal/public/uploads/tmp/1500988407-65916-0004-5400/style.css +0 -1
  163. data/spec/internal/public/uploads/tmp/1500988407-65916-0007-1655/style.css +0 -1
  164. data/spec/internal/public/uploads/tmp/1500988421-65950-0002-9415/avatar.jpg +0 -0
  165. data/spec/internal/public/uploads/tmp/1500988421-65950-0004-7130/style.css +0 -1
  166. data/spec/internal/public/uploads/tmp/1500988421-65950-0007-9886/style.css +0 -1
  167. data/spec/internal/public/uploads/tmp/1500988435-65981-0002-3228/avatar.jpg +0 -0
  168. data/spec/internal/public/uploads/tmp/1500988435-65981-0004-3682/style.css +0 -1
  169. data/spec/internal/public/uploads/tmp/1500988435-65981-0007-1582/style.css +0 -1
  170. data/spec/internal/public/uploads/tmp/1500988475-66122-0002-9516/avatar.jpg +0 -0
  171. data/spec/internal/public/uploads/tmp/1500988475-66122-0004-5634/style.css +0 -1
  172. data/spec/internal/public/uploads/tmp/1500988530-66122-0007-2272/style.css +0 -1
  173. data/spec/internal/public/uploads/tmp/1500988554-66315-0002-6262/avatar.jpg +0 -0
  174. data/spec/internal/public/uploads/tmp/1500988554-66315-0004-6099/style.css +0 -1
  175. data/spec/internal/public/uploads/tmp/1500988554-66315-0007-1632/style.css +0 -1
  176. data/spec/internal/public/uploads/tmp/1500991751-73722-0002-9937/avatar.jpg +0 -0
  177. data/spec/internal/public/uploads/tmp/1500991751-73722-0004-8034/style.css +0 -1
  178. data/spec/internal/public/uploads/tmp/1500991751-73722-0007-7763/style.css +0 -1
  179. data/spec/internal/public/uploads/tmp/1501233238-34385-0002-3210/avatar.jpg +0 -0
  180. data/spec/internal/public/uploads/tmp/1501233238-34385-0004-5881/style.css +0 -1
  181. data/spec/internal/public/uploads/tmp/1501233238-34385-0007-6280/style.css +0 -1
  182. data/spec/internal/tmp/cache/assets/test/sprockets/v3.0/1XyAFYlYI0pK7WAgjR4PgXV6BgU6huJSviWmHetdCRs.cache +0 -1
  183. data/vendor/assets/bower_components/angular-ui-router/api/angular-ui-router.d.ts +0 -126
  184. data/vendor/assets/bower_components/angular-ui-router/src/common.js +0 -292
  185. data/vendor/assets/bower_components/angular-ui-router/src/resolve.js +0 -252
  186. data/vendor/assets/bower_components/angular-ui-router/src/state.js +0 -1373
  187. data/vendor/assets/bower_components/angular-ui-router/src/stateDirectives.js +0 -268
  188. data/vendor/assets/bower_components/angular-ui-router/src/stateFilters.js +0 -39
  189. data/vendor/assets/bower_components/angular-ui-router/src/templateFactory.js +0 -110
  190. data/vendor/assets/bower_components/angular-ui-router/src/urlMatcherFactory.js +0 -1036
  191. data/vendor/assets/bower_components/angular-ui-router/src/urlRouter.js +0 -413
  192. data/vendor/assets/bower_components/angular-ui-router/src/view.js +0 -71
  193. data/vendor/assets/bower_components/angular-ui-router/src/viewDirective.js +0 -302
  194. data/vendor/assets/bower_components/angular-ui-router/src/viewScroll.js +0 -52
  195. data/vendor/assets/bower_components/angularjs-dropdown-multiselect/pages/index.html +0 -67
  196. data/vendor/assets/bower_components/bootstrap/Gemfile.lock +0 -43
@@ -1,252 +0,0 @@
1
- /**
2
- * @ngdoc object
3
- * @name ui.router.util.$resolve
4
- *
5
- * @requires $q
6
- * @requires $injector
7
- *
8
- * @description
9
- * Manages resolution of (acyclic) graphs of promises.
10
- */
11
- $Resolve.$inject = ['$q', '$injector'];
12
- function $Resolve( $q, $injector) {
13
-
14
- var VISIT_IN_PROGRESS = 1,
15
- VISIT_DONE = 2,
16
- NOTHING = {},
17
- NO_DEPENDENCIES = [],
18
- NO_LOCALS = NOTHING,
19
- NO_PARENT = extend($q.when(NOTHING), { $$promises: NOTHING, $$values: NOTHING });
20
-
21
-
22
- /**
23
- * @ngdoc function
24
- * @name ui.router.util.$resolve#study
25
- * @methodOf ui.router.util.$resolve
26
- *
27
- * @description
28
- * Studies a set of invocables that are likely to be used multiple times.
29
- * <pre>
30
- * $resolve.study(invocables)(locals, parent, self)
31
- * </pre>
32
- * is equivalent to
33
- * <pre>
34
- * $resolve.resolve(invocables, locals, parent, self)
35
- * </pre>
36
- * but the former is more efficient (in fact `resolve` just calls `study`
37
- * internally).
38
- *
39
- * @param {object} invocables Invocable objects
40
- * @return {function} a function to pass in locals, parent and self
41
- */
42
- this.study = function (invocables) {
43
- if (!isObject(invocables)) throw new Error("'invocables' must be an object");
44
- var invocableKeys = objectKeys(invocables || {});
45
-
46
- // Perform a topological sort of invocables to build an ordered plan
47
- var plan = [], cycle = [], visited = {};
48
- function visit(value, key) {
49
- if (visited[key] === VISIT_DONE) return;
50
-
51
- cycle.push(key);
52
- if (visited[key] === VISIT_IN_PROGRESS) {
53
- cycle.splice(0, indexOf(cycle, key));
54
- throw new Error("Cyclic dependency: " + cycle.join(" -> "));
55
- }
56
- visited[key] = VISIT_IN_PROGRESS;
57
-
58
- if (isString(value)) {
59
- plan.push(key, [ function() { return $injector.get(value); }], NO_DEPENDENCIES);
60
- } else {
61
- var params = $injector.annotate(value);
62
- forEach(params, function (param) {
63
- if (param !== key && invocables.hasOwnProperty(param)) visit(invocables[param], param);
64
- });
65
- plan.push(key, value, params);
66
- }
67
-
68
- cycle.pop();
69
- visited[key] = VISIT_DONE;
70
- }
71
- forEach(invocables, visit);
72
- invocables = cycle = visited = null; // plan is all that's required
73
-
74
- function isResolve(value) {
75
- return isObject(value) && value.then && value.$$promises;
76
- }
77
-
78
- return function (locals, parent, self) {
79
- if (isResolve(locals) && self === undefined) {
80
- self = parent; parent = locals; locals = null;
81
- }
82
- if (!locals) locals = NO_LOCALS;
83
- else if (!isObject(locals)) {
84
- throw new Error("'locals' must be an object");
85
- }
86
- if (!parent) parent = NO_PARENT;
87
- else if (!isResolve(parent)) {
88
- throw new Error("'parent' must be a promise returned by $resolve.resolve()");
89
- }
90
-
91
- // To complete the overall resolution, we have to wait for the parent
92
- // promise and for the promise for each invokable in our plan.
93
- var resolution = $q.defer(),
94
- result = resolution.promise,
95
- promises = result.$$promises = {},
96
- values = extend({}, locals),
97
- wait = 1 + plan.length/3,
98
- merged = false;
99
-
100
- function done() {
101
- // Merge parent values we haven't got yet and publish our own $$values
102
- if (!--wait) {
103
- if (!merged) merge(values, parent.$$values);
104
- result.$$values = values;
105
- result.$$promises = result.$$promises || true; // keep for isResolve()
106
- delete result.$$inheritedValues;
107
- resolution.resolve(values);
108
- }
109
- }
110
-
111
- function fail(reason) {
112
- result.$$failure = reason;
113
- resolution.reject(reason);
114
- }
115
-
116
- // Short-circuit if parent has already failed
117
- if (isDefined(parent.$$failure)) {
118
- fail(parent.$$failure);
119
- return result;
120
- }
121
-
122
- if (parent.$$inheritedValues) {
123
- merge(values, omit(parent.$$inheritedValues, invocableKeys));
124
- }
125
-
126
- // Merge parent values if the parent has already resolved, or merge
127
- // parent promises and wait if the parent resolve is still in progress.
128
- extend(promises, parent.$$promises);
129
- if (parent.$$values) {
130
- merged = merge(values, omit(parent.$$values, invocableKeys));
131
- result.$$inheritedValues = omit(parent.$$values, invocableKeys);
132
- done();
133
- } else {
134
- if (parent.$$inheritedValues) {
135
- result.$$inheritedValues = omit(parent.$$inheritedValues, invocableKeys);
136
- }
137
- parent.then(done, fail);
138
- }
139
-
140
- // Process each invocable in the plan, but ignore any where a local of the same name exists.
141
- for (var i=0, ii=plan.length; i<ii; i+=3) {
142
- if (locals.hasOwnProperty(plan[i])) done();
143
- else invoke(plan[i], plan[i+1], plan[i+2]);
144
- }
145
-
146
- function invoke(key, invocable, params) {
147
- // Create a deferred for this invocation. Failures will propagate to the resolution as well.
148
- var invocation = $q.defer(), waitParams = 0;
149
- function onfailure(reason) {
150
- invocation.reject(reason);
151
- fail(reason);
152
- }
153
- // Wait for any parameter that we have a promise for (either from parent or from this
154
- // resolve; in that case study() will have made sure it's ordered before us in the plan).
155
- forEach(params, function (dep) {
156
- if (promises.hasOwnProperty(dep) && !locals.hasOwnProperty(dep)) {
157
- waitParams++;
158
- promises[dep].then(function (result) {
159
- values[dep] = result;
160
- if (!(--waitParams)) proceed();
161
- }, onfailure);
162
- }
163
- });
164
- if (!waitParams) proceed();
165
- function proceed() {
166
- if (isDefined(result.$$failure)) return;
167
- try {
168
- invocation.resolve($injector.invoke(invocable, self, values));
169
- invocation.promise.then(function (result) {
170
- values[key] = result;
171
- done();
172
- }, onfailure);
173
- } catch (e) {
174
- onfailure(e);
175
- }
176
- }
177
- // Publish promise synchronously; invocations further down in the plan may depend on it.
178
- promises[key] = invocation.promise;
179
- }
180
-
181
- return result;
182
- };
183
- };
184
-
185
- /**
186
- * @ngdoc function
187
- * @name ui.router.util.$resolve#resolve
188
- * @methodOf ui.router.util.$resolve
189
- *
190
- * @description
191
- * Resolves a set of invocables. An invocable is a function to be invoked via
192
- * `$injector.invoke()`, and can have an arbitrary number of dependencies.
193
- * An invocable can either return a value directly,
194
- * or a `$q` promise. If a promise is returned it will be resolved and the
195
- * resulting value will be used instead. Dependencies of invocables are resolved
196
- * (in this order of precedence)
197
- *
198
- * - from the specified `locals`
199
- * - from another invocable that is part of this `$resolve` call
200
- * - from an invocable that is inherited from a `parent` call to `$resolve`
201
- * (or recursively
202
- * - from any ancestor `$resolve` of that parent).
203
- *
204
- * The return value of `$resolve` is a promise for an object that contains
205
- * (in this order of precedence)
206
- *
207
- * - any `locals` (if specified)
208
- * - the resolved return values of all injectables
209
- * - any values inherited from a `parent` call to `$resolve` (if specified)
210
- *
211
- * The promise will resolve after the `parent` promise (if any) and all promises
212
- * returned by injectables have been resolved. If any invocable
213
- * (or `$injector.invoke`) throws an exception, or if a promise returned by an
214
- * invocable is rejected, the `$resolve` promise is immediately rejected with the
215
- * same error. A rejection of a `parent` promise (if specified) will likewise be
216
- * propagated immediately. Once the `$resolve` promise has been rejected, no
217
- * further invocables will be called.
218
- *
219
- * Cyclic dependencies between invocables are not permitted and will caues `$resolve`
220
- * to throw an error. As a special case, an injectable can depend on a parameter
221
- * with the same name as the injectable, which will be fulfilled from the `parent`
222
- * injectable of the same name. This allows inherited values to be decorated.
223
- * Note that in this case any other injectable in the same `$resolve` with the same
224
- * dependency would see the decorated value, not the inherited value.
225
- *
226
- * Note that missing dependencies -- unlike cyclic dependencies -- will cause an
227
- * (asynchronous) rejection of the `$resolve` promise rather than a (synchronous)
228
- * exception.
229
- *
230
- * Invocables are invoked eagerly as soon as all dependencies are available.
231
- * This is true even for dependencies inherited from a `parent` call to `$resolve`.
232
- *
233
- * As a special case, an invocable can be a string, in which case it is taken to
234
- * be a service name to be passed to `$injector.get()`. This is supported primarily
235
- * for backwards-compatibility with the `resolve` property of `$routeProvider`
236
- * routes.
237
- *
238
- * @param {object} invocables functions to invoke or
239
- * `$injector` services to fetch.
240
- * @param {object} locals values to make available to the injectables
241
- * @param {object} parent a promise returned by another call to `$resolve`.
242
- * @param {object} self the `this` for the invoked methods
243
- * @return {object} Promise for an object that contains the resolved return value
244
- * of all invocables, as well as any inherited and local values.
245
- */
246
- this.resolve = function (invocables, locals, parent, self) {
247
- return this.study(invocables)(locals, parent, self);
248
- };
249
- }
250
-
251
- angular.module('ui.router.util').service('$resolve', $Resolve);
252
-
@@ -1,1373 +0,0 @@
1
- /**
2
- * @ngdoc object
3
- * @name ui.router.state.$stateProvider
4
- *
5
- * @requires ui.router.router.$urlRouterProvider
6
- * @requires ui.router.util.$urlMatcherFactoryProvider
7
- *
8
- * @description
9
- * The new `$stateProvider` works similar to Angular's v1 router, but it focuses purely
10
- * on state.
11
- *
12
- * A state corresponds to a "place" in the application in terms of the overall UI and
13
- * navigation. A state describes (via the controller / template / view properties) what
14
- * the UI looks like and does at that place.
15
- *
16
- * States often have things in common, and the primary way of factoring out these
17
- * commonalities in this model is via the state hierarchy, i.e. parent/child states aka
18
- * nested states.
19
- *
20
- * The `$stateProvider` provides interfaces to declare these states for your app.
21
- */
22
- $StateProvider.$inject = ['$urlRouterProvider', '$urlMatcherFactoryProvider'];
23
- function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
24
-
25
- var root, states = {}, $state, queue = {}, abstractKey = 'abstract';
26
-
27
- // Builds state properties from definition passed to registerState()
28
- var stateBuilder = {
29
-
30
- // Derive parent state from a hierarchical name only if 'parent' is not explicitly defined.
31
- // state.children = [];
32
- // if (parent) parent.children.push(state);
33
- parent: function(state) {
34
- if (isDefined(state.parent) && state.parent) return findState(state.parent);
35
- // regex matches any valid composite state name
36
- // would match "contact.list" but not "contacts"
37
- var compositeName = /^(.+)\.[^.]+$/.exec(state.name);
38
- return compositeName ? findState(compositeName[1]) : root;
39
- },
40
-
41
- // inherit 'data' from parent and override by own values (if any)
42
- data: function(state) {
43
- if (state.parent && state.parent.data) {
44
- state.data = state.self.data = extend({}, state.parent.data, state.data);
45
- }
46
- return state.data;
47
- },
48
-
49
- // Build a URLMatcher if necessary, either via a relative or absolute URL
50
- url: function(state) {
51
- var url = state.url, config = { params: state.params || {} };
52
-
53
- if (isString(url)) {
54
- if (url.charAt(0) == '^') return $urlMatcherFactory.compile(url.substring(1), config);
55
- return (state.parent.navigable || root).url.concat(url, config);
56
- }
57
-
58
- if (!url || $urlMatcherFactory.isMatcher(url)) return url;
59
- throw new Error("Invalid url '" + url + "' in state '" + state + "'");
60
- },
61
-
62
- // Keep track of the closest ancestor state that has a URL (i.e. is navigable)
63
- navigable: function(state) {
64
- return state.url ? state : (state.parent ? state.parent.navigable : null);
65
- },
66
-
67
- // Own parameters for this state. state.url.params is already built at this point. Create and add non-url params
68
- ownParams: function(state) {
69
- var params = state.url && state.url.params || new $$UMFP.ParamSet();
70
- forEach(state.params || {}, function(config, id) {
71
- if (!params[id]) params[id] = new $$UMFP.Param(id, null, config, "config");
72
- });
73
- return params;
74
- },
75
-
76
- // Derive parameters for this state and ensure they're a super-set of parent's parameters
77
- params: function(state) {
78
- return state.parent && state.parent.params ? extend(state.parent.params.$$new(), state.ownParams) : new $$UMFP.ParamSet();
79
- },
80
-
81
- // If there is no explicit multi-view configuration, make one up so we don't have
82
- // to handle both cases in the view directive later. Note that having an explicit
83
- // 'views' property will mean the default unnamed view properties are ignored. This
84
- // is also a good time to resolve view names to absolute names, so everything is a
85
- // straight lookup at link time.
86
- views: function(state) {
87
- var views = {};
88
-
89
- forEach(isDefined(state.views) ? state.views : { '': state }, function (view, name) {
90
- if (name.indexOf('@') < 0) name += '@' + state.parent.name;
91
- views[name] = view;
92
- });
93
- return views;
94
- },
95
-
96
- // Keep a full path from the root down to this state as this is needed for state activation.
97
- path: function(state) {
98
- return state.parent ? state.parent.path.concat(state) : []; // exclude root from path
99
- },
100
-
101
- // Speed up $state.contains() as it's used a lot
102
- includes: function(state) {
103
- var includes = state.parent ? extend({}, state.parent.includes) : {};
104
- includes[state.name] = true;
105
- return includes;
106
- },
107
-
108
- $delegates: {}
109
- };
110
-
111
- function isRelative(stateName) {
112
- return stateName.indexOf(".") === 0 || stateName.indexOf("^") === 0;
113
- }
114
-
115
- function findState(stateOrName, base) {
116
- if (!stateOrName) return undefined;
117
-
118
- var isStr = isString(stateOrName),
119
- name = isStr ? stateOrName : stateOrName.name,
120
- path = isRelative(name);
121
-
122
- if (path) {
123
- if (!base) throw new Error("No reference point given for path '" + name + "'");
124
- base = findState(base);
125
-
126
- var rel = name.split("."), i = 0, pathLength = rel.length, current = base;
127
-
128
- for (; i < pathLength; i++) {
129
- if (rel[i] === "" && i === 0) {
130
- current = base;
131
- continue;
132
- }
133
- if (rel[i] === "^") {
134
- if (!current.parent) throw new Error("Path '" + name + "' not valid for state '" + base.name + "'");
135
- current = current.parent;
136
- continue;
137
- }
138
- break;
139
- }
140
- rel = rel.slice(i).join(".");
141
- name = current.name + (current.name && rel ? "." : "") + rel;
142
- }
143
- var state = states[name];
144
-
145
- if (state && (isStr || (!isStr && (state === stateOrName || state.self === stateOrName)))) {
146
- return state;
147
- }
148
- return undefined;
149
- }
150
-
151
- function queueState(parentName, state) {
152
- if (!queue[parentName]) {
153
- queue[parentName] = [];
154
- }
155
- queue[parentName].push(state);
156
- }
157
-
158
- function flushQueuedChildren(parentName) {
159
- var queued = queue[parentName] || [];
160
- while(queued.length) {
161
- registerState(queued.shift());
162
- }
163
- }
164
-
165
- function registerState(state) {
166
- // Wrap a new object around the state so we can store our private details easily.
167
- state = inherit(state, {
168
- self: state,
169
- resolve: state.resolve || {},
170
- toString: function() { return this.name; }
171
- });
172
-
173
- var name = state.name;
174
- if (!isString(name) || name.indexOf('@') >= 0) throw new Error("State must have a valid name");
175
- if (states.hasOwnProperty(name)) throw new Error("State '" + name + "'' is already defined");
176
-
177
- // Get parent name
178
- var parentName = (name.indexOf('.') !== -1) ? name.substring(0, name.lastIndexOf('.'))
179
- : (isString(state.parent)) ? state.parent
180
- : (isObject(state.parent) && isString(state.parent.name)) ? state.parent.name
181
- : '';
182
-
183
- // If parent is not registered yet, add state to queue and register later
184
- if (parentName && !states[parentName]) {
185
- return queueState(parentName, state.self);
186
- }
187
-
188
- for (var key in stateBuilder) {
189
- if (isFunction(stateBuilder[key])) state[key] = stateBuilder[key](state, stateBuilder.$delegates[key]);
190
- }
191
- states[name] = state;
192
-
193
- // Register the state in the global state list and with $urlRouter if necessary.
194
- if (!state[abstractKey] && state.url) {
195
- $urlRouterProvider.when(state.url, ['$match', '$stateParams', function ($match, $stateParams) {
196
- if ($state.$current.navigable != state || !equalForKeys($match, $stateParams)) {
197
- $state.transitionTo(state, $match, { inherit: true, location: false });
198
- }
199
- }]);
200
- }
201
-
202
- // Register any queued children
203
- flushQueuedChildren(name);
204
-
205
- return state;
206
- }
207
-
208
- // Checks text to see if it looks like a glob.
209
- function isGlob (text) {
210
- return text.indexOf('*') > -1;
211
- }
212
-
213
- // Returns true if glob matches current $state name.
214
- function doesStateMatchGlob (glob) {
215
- var globSegments = glob.split('.'),
216
- segments = $state.$current.name.split('.');
217
-
218
- //match greedy starts
219
- if (globSegments[0] === '**') {
220
- segments = segments.slice(indexOf(segments, globSegments[1]));
221
- segments.unshift('**');
222
- }
223
- //match greedy ends
224
- if (globSegments[globSegments.length - 1] === '**') {
225
- segments.splice(indexOf(segments, globSegments[globSegments.length - 2]) + 1, Number.MAX_VALUE);
226
- segments.push('**');
227
- }
228
-
229
- if (globSegments.length != segments.length) {
230
- return false;
231
- }
232
-
233
- //match single stars
234
- for (var i = 0, l = globSegments.length; i < l; i++) {
235
- if (globSegments[i] === '*') {
236
- segments[i] = '*';
237
- }
238
- }
239
-
240
- return segments.join('') === globSegments.join('');
241
- }
242
-
243
-
244
- // Implicit root state that is always active
245
- root = registerState({
246
- name: '',
247
- url: '^',
248
- views: null,
249
- 'abstract': true
250
- });
251
- root.navigable = null;
252
-
253
-
254
- /**
255
- * @ngdoc function
256
- * @name ui.router.state.$stateProvider#decorator
257
- * @methodOf ui.router.state.$stateProvider
258
- *
259
- * @description
260
- * Allows you to extend (carefully) or override (at your own peril) the
261
- * `stateBuilder` object used internally by `$stateProvider`. This can be used
262
- * to add custom functionality to ui-router, for example inferring templateUrl
263
- * based on the state name.
264
- *
265
- * When passing only a name, it returns the current (original or decorated) builder
266
- * function that matches `name`.
267
- *
268
- * The builder functions that can be decorated are listed below. Though not all
269
- * necessarily have a good use case for decoration, that is up to you to decide.
270
- *
271
- * In addition, users can attach custom decorators, which will generate new
272
- * properties within the state's internal definition. There is currently no clear
273
- * use-case for this beyond accessing internal states (i.e. $state.$current),
274
- * however, expect this to become increasingly relevant as we introduce additional
275
- * meta-programming features.
276
- *
277
- * **Warning**: Decorators should not be interdependent because the order of
278
- * execution of the builder functions in non-deterministic. Builder functions
279
- * should only be dependent on the state definition object and super function.
280
- *
281
- *
282
- * Existing builder functions and current return values:
283
- *
284
- * - **parent** `{object}` - returns the parent state object.
285
- * - **data** `{object}` - returns state data, including any inherited data that is not
286
- * overridden by own values (if any).
287
- * - **url** `{object}` - returns a {@link ui.router.util.type:UrlMatcher UrlMatcher}
288
- * or `null`.
289
- * - **navigable** `{object}` - returns closest ancestor state that has a URL (aka is
290
- * navigable).
291
- * - **params** `{object}` - returns an array of state params that are ensured to
292
- * be a super-set of parent's params.
293
- * - **views** `{object}` - returns a views object where each key is an absolute view
294
- * name (i.e. "viewName@stateName") and each value is the config object
295
- * (template, controller) for the view. Even when you don't use the views object
296
- * explicitly on a state config, one is still created for you internally.
297
- * So by decorating this builder function you have access to decorating template
298
- * and controller properties.
299
- * - **ownParams** `{object}` - returns an array of params that belong to the state,
300
- * not including any params defined by ancestor states.
301
- * - **path** `{string}` - returns the full path from the root down to this state.
302
- * Needed for state activation.
303
- * - **includes** `{object}` - returns an object that includes every state that
304
- * would pass a `$state.includes()` test.
305
- *
306
- * @example
307
- * <pre>
308
- * // Override the internal 'views' builder with a function that takes the state
309
- * // definition, and a reference to the internal function being overridden:
310
- * $stateProvider.decorator('views', function (state, parent) {
311
- * var result = {},
312
- * views = parent(state);
313
- *
314
- * angular.forEach(views, function (config, name) {
315
- * var autoName = (state.name + '.' + name).replace('.', '/');
316
- * config.templateUrl = config.templateUrl || '/partials/' + autoName + '.html';
317
- * result[name] = config;
318
- * });
319
- * return result;
320
- * });
321
- *
322
- * $stateProvider.state('home', {
323
- * views: {
324
- * 'contact.list': { controller: 'ListController' },
325
- * 'contact.item': { controller: 'ItemController' }
326
- * }
327
- * });
328
- *
329
- * // ...
330
- *
331
- * $state.go('home');
332
- * // Auto-populates list and item views with /partials/home/contact/list.html,
333
- * // and /partials/home/contact/item.html, respectively.
334
- * </pre>
335
- *
336
- * @param {string} name The name of the builder function to decorate.
337
- * @param {object} func A function that is responsible for decorating the original
338
- * builder function. The function receives two parameters:
339
- *
340
- * - `{object}` - state - The state config object.
341
- * - `{object}` - super - The original builder function.
342
- *
343
- * @return {object} $stateProvider - $stateProvider instance
344
- */
345
- this.decorator = decorator;
346
- function decorator(name, func) {
347
- /*jshint validthis: true */
348
- if (isString(name) && !isDefined(func)) {
349
- return stateBuilder[name];
350
- }
351
- if (!isFunction(func) || !isString(name)) {
352
- return this;
353
- }
354
- if (stateBuilder[name] && !stateBuilder.$delegates[name]) {
355
- stateBuilder.$delegates[name] = stateBuilder[name];
356
- }
357
- stateBuilder[name] = func;
358
- return this;
359
- }
360
-
361
- /**
362
- * @ngdoc function
363
- * @name ui.router.state.$stateProvider#state
364
- * @methodOf ui.router.state.$stateProvider
365
- *
366
- * @description
367
- * Registers a state configuration under a given state name. The stateConfig object
368
- * has the following acceptable properties.
369
- *
370
- * @param {string} name A unique state name, e.g. "home", "about", "contacts".
371
- * To create a parent/child state use a dot, e.g. "about.sales", "home.newest".
372
- * @param {object} stateConfig State configuration object.
373
- * @param {string|function=} stateConfig.template
374
- * <a id='template'></a>
375
- * html template as a string or a function that returns
376
- * an html template as a string which should be used by the uiView directives. This property
377
- * takes precedence over templateUrl.
378
- *
379
- * If `template` is a function, it will be called with the following parameters:
380
- *
381
- * - {array.&lt;object&gt;} - state parameters extracted from the current $location.path() by
382
- * applying the current state
383
- *
384
- * <pre>template:
385
- * "<h1>inline template definition</h1>" +
386
- * "<div ui-view></div>"</pre>
387
- * <pre>template: function(params) {
388
- * return "<h1>generated template</h1>"; }</pre>
389
- * </div>
390
- *
391
- * @param {string|function=} stateConfig.templateUrl
392
- * <a id='templateUrl'></a>
393
- *
394
- * path or function that returns a path to an html
395
- * template that should be used by uiView.
396
- *
397
- * If `templateUrl` is a function, it will be called with the following parameters:
398
- *
399
- * - {array.&lt;object&gt;} - state parameters extracted from the current $location.path() by
400
- * applying the current state
401
- *
402
- * <pre>templateUrl: "home.html"</pre>
403
- * <pre>templateUrl: function(params) {
404
- * return myTemplates[params.pageId]; }</pre>
405
- *
406
- * @param {function=} stateConfig.templateProvider
407
- * <a id='templateProvider'></a>
408
- * Provider function that returns HTML content string.
409
- * <pre> templateProvider:
410
- * function(MyTemplateService, params) {
411
- * return MyTemplateService.getTemplate(params.pageId);
412
- * }</pre>
413
- *
414
- * @param {string|function=} stateConfig.controller
415
- * <a id='controller'></a>
416
- *
417
- * Controller fn that should be associated with newly
418
- * related scope or the name of a registered controller if passed as a string.
419
- * Optionally, the ControllerAs may be declared here.
420
- * <pre>controller: "MyRegisteredController"</pre>
421
- * <pre>controller:
422
- * "MyRegisteredController as fooCtrl"}</pre>
423
- * <pre>controller: function($scope, MyService) {
424
- * $scope.data = MyService.getData(); }</pre>
425
- *
426
- * @param {function=} stateConfig.controllerProvider
427
- * <a id='controllerProvider'></a>
428
- *
429
- * Injectable provider function that returns the actual controller or string.
430
- * <pre>controllerProvider:
431
- * function(MyResolveData) {
432
- * if (MyResolveData.foo)
433
- * return "FooCtrl"
434
- * else if (MyResolveData.bar)
435
- * return "BarCtrl";
436
- * else return function($scope) {
437
- * $scope.baz = "Qux";
438
- * }
439
- * }</pre>
440
- *
441
- * @param {string=} stateConfig.controllerAs
442
- * <a id='controllerAs'></a>
443
- *
444
- * A controller alias name. If present the controller will be
445
- * published to scope under the controllerAs name.
446
- * <pre>controllerAs: "myCtrl"</pre>
447
- *
448
- * @param {object=} stateConfig.resolve
449
- * <a id='resolve'></a>
450
- *
451
- * An optional map&lt;string, function&gt; of dependencies which
452
- * should be injected into the controller. If any of these dependencies are promises,
453
- * the router will wait for them all to be resolved before the controller is instantiated.
454
- * If all the promises are resolved successfully, the $stateChangeSuccess event is fired
455
- * and the values of the resolved promises are injected into any controllers that reference them.
456
- * If any of the promises are rejected the $stateChangeError event is fired.
457
- *
458
- * The map object is:
459
- *
460
- * - key - {string}: name of dependency to be injected into controller
461
- * - factory - {string|function}: If string then it is alias for service. Otherwise if function,
462
- * it is injected and return value it treated as dependency. If result is a promise, it is
463
- * resolved before its value is injected into controller.
464
- *
465
- * <pre>resolve: {
466
- * myResolve1:
467
- * function($http, $stateParams) {
468
- * return $http.get("/api/foos/"+stateParams.fooID);
469
- * }
470
- * }</pre>
471
- *
472
- * @param {string=} stateConfig.url
473
- * <a id='url'></a>
474
- *
475
- * A url fragment with optional parameters. When a state is navigated or
476
- * transitioned to, the `$stateParams` service will be populated with any
477
- * parameters that were passed.
478
- *
479
- * examples:
480
- * <pre>url: "/home"
481
- * url: "/users/:userid"
482
- * url: "/books/{bookid:[a-zA-Z_-]}"
483
- * url: "/books/{categoryid:int}"
484
- * url: "/books/{publishername:string}/{categoryid:int}"
485
- * url: "/messages?before&after"
486
- * url: "/messages?{before:date}&{after:date}"</pre>
487
- * url: "/messages/:mailboxid?{before:date}&{after:date}"
488
- *
489
- * @param {object=} stateConfig.views
490
- * <a id='views'></a>
491
- * an optional map&lt;string, object&gt; which defined multiple views, or targets views
492
- * manually/explicitly.
493
- *
494
- * Examples:
495
- *
496
- * Targets three named `ui-view`s in the parent state's template
497
- * <pre>views: {
498
- * header: {
499
- * controller: "headerCtrl",
500
- * templateUrl: "header.html"
501
- * }, body: {
502
- * controller: "bodyCtrl",
503
- * templateUrl: "body.html"
504
- * }, footer: {
505
- * controller: "footCtrl",
506
- * templateUrl: "footer.html"
507
- * }
508
- * }</pre>
509
- *
510
- * Targets named `ui-view="header"` from grandparent state 'top''s template, and named `ui-view="body" from parent state's template.
511
- * <pre>views: {
512
- * 'header@top': {
513
- * controller: "msgHeaderCtrl",
514
- * templateUrl: "msgHeader.html"
515
- * }, 'body': {
516
- * controller: "messagesCtrl",
517
- * templateUrl: "messages.html"
518
- * }
519
- * }</pre>
520
- *
521
- * @param {boolean=} [stateConfig.abstract=false]
522
- * <a id='abstract'></a>
523
- * An abstract state will never be directly activated,
524
- * but can provide inherited properties to its common children states.
525
- * <pre>abstract: true</pre>
526
- *
527
- * @param {function=} stateConfig.onEnter
528
- * <a id='onEnter'></a>
529
- *
530
- * Callback function for when a state is entered. Good way
531
- * to trigger an action or dispatch an event, such as opening a dialog.
532
- * If minifying your scripts, make sure to explictly annotate this function,
533
- * because it won't be automatically annotated by your build tools.
534
- *
535
- * <pre>onEnter: function(MyService, $stateParams) {
536
- * MyService.foo($stateParams.myParam);
537
- * }</pre>
538
- *
539
- * @param {function=} stateConfig.onExit
540
- * <a id='onExit'></a>
541
- *
542
- * Callback function for when a state is exited. Good way to
543
- * trigger an action or dispatch an event, such as opening a dialog.
544
- * If minifying your scripts, make sure to explictly annotate this function,
545
- * because it won't be automatically annotated by your build tools.
546
- *
547
- * <pre>onExit: function(MyService, $stateParams) {
548
- * MyService.cleanup($stateParams.myParam);
549
- * }</pre>
550
- *
551
- * @param {boolean=} [stateConfig.reloadOnSearch=true]
552
- * <a id='reloadOnSearch'></a>
553
- *
554
- * If `false`, will not retrigger the same state
555
- * just because a search/query parameter has changed (via $location.search() or $location.hash()).
556
- * Useful for when you'd like to modify $location.search() without triggering a reload.
557
- * <pre>reloadOnSearch: false</pre>
558
- *
559
- * @param {object=} stateConfig.data
560
- * <a id='data'></a>
561
- *
562
- * Arbitrary data object, useful for custom configuration. The parent state's `data` is
563
- * prototypally inherited. In other words, adding a data property to a state adds it to
564
- * the entire subtree via prototypal inheritance.
565
- *
566
- * <pre>data: {
567
- * requiredRole: 'foo'
568
- * } </pre>
569
- *
570
- * @param {object=} stateConfig.params
571
- * <a id='params'></a>
572
- *
573
- * A map which optionally configures parameters declared in the `url`, or
574
- * defines additional non-url parameters. For each parameter being
575
- * configured, add a configuration object keyed to the name of the parameter.
576
- *
577
- * Each parameter configuration object may contain the following properties:
578
- *
579
- * - ** value ** - {object|function=}: specifies the default value for this
580
- * parameter. This implicitly sets this parameter as optional.
581
- *
582
- * When UI-Router routes to a state and no value is
583
- * specified for this parameter in the URL or transition, the
584
- * default value will be used instead. If `value` is a function,
585
- * it will be injected and invoked, and the return value used.
586
- *
587
- * *Note*: `undefined` is treated as "no default value" while `null`
588
- * is treated as "the default value is `null`".
589
- *
590
- * *Shorthand*: If you only need to configure the default value of the
591
- * parameter, you may use a shorthand syntax. In the **`params`**
592
- * map, instead mapping the param name to a full parameter configuration
593
- * object, simply set map it to the default parameter value, e.g.:
594
- *
595
- * <pre>// define a parameter's default value
596
- * params: {
597
- * param1: { value: "defaultValue" }
598
- * }
599
- * // shorthand default values
600
- * params: {
601
- * param1: "defaultValue",
602
- * param2: "param2Default"
603
- * }</pre>
604
- *
605
- * - ** array ** - {boolean=}: *(default: false)* If true, the param value will be
606
- * treated as an array of values. If you specified a Type, the value will be
607
- * treated as an array of the specified Type. Note: query parameter values
608
- * default to a special `"auto"` mode.
609
- *
610
- * For query parameters in `"auto"` mode, if multiple values for a single parameter
611
- * are present in the URL (e.g.: `/foo?bar=1&bar=2&bar=3`) then the values
612
- * are mapped to an array (e.g.: `{ foo: [ '1', '2', '3' ] }`). However, if
613
- * only one value is present (e.g.: `/foo?bar=1`) then the value is treated as single
614
- * value (e.g.: `{ foo: '1' }`).
615
- *
616
- * <pre>params: {
617
- * param1: { array: true }
618
- * }</pre>
619
- *
620
- * - ** squash ** - {bool|string=}: `squash` configures how a default parameter value is represented in the URL when
621
- * the current parameter value is the same as the default value. If `squash` is not set, it uses the
622
- * configured default squash policy.
623
- * (See {@link ui.router.util.$urlMatcherFactory#methods_defaultSquashPolicy `defaultSquashPolicy()`})
624
- *
625
- * There are three squash settings:
626
- *
627
- * - false: The parameter's default value is not squashed. It is encoded and included in the URL
628
- * - true: The parameter's default value is omitted from the URL. If the parameter is preceeded and followed
629
- * by slashes in the state's `url` declaration, then one of those slashes are omitted.
630
- * This can allow for cleaner looking URLs.
631
- * - `"<arbitrary string>"`: The parameter's default value is replaced with an arbitrary placeholder of your choice.
632
- *
633
- * <pre>params: {
634
- * param1: {
635
- * value: "defaultId",
636
- * squash: true
637
- * } }
638
- * // squash "defaultValue" to "~"
639
- * params: {
640
- * param1: {
641
- * value: "defaultValue",
642
- * squash: "~"
643
- * } }
644
- * </pre>
645
- *
646
- *
647
- * @example
648
- * <pre>
649
- * // Some state name examples
650
- *
651
- * // stateName can be a single top-level name (must be unique).
652
- * $stateProvider.state("home", {});
653
- *
654
- * // Or it can be a nested state name. This state is a child of the
655
- * // above "home" state.
656
- * $stateProvider.state("home.newest", {});
657
- *
658
- * // Nest states as deeply as needed.
659
- * $stateProvider.state("home.newest.abc.xyz.inception", {});
660
- *
661
- * // state() returns $stateProvider, so you can chain state declarations.
662
- * $stateProvider
663
- * .state("home", {})
664
- * .state("about", {})
665
- * .state("contacts", {});
666
- * </pre>
667
- *
668
- */
669
- this.state = state;
670
- function state(name, definition) {
671
- /*jshint validthis: true */
672
- if (isObject(name)) definition = name;
673
- else definition.name = name;
674
- registerState(definition);
675
- return this;
676
- }
677
-
678
- /**
679
- * @ngdoc object
680
- * @name ui.router.state.$state
681
- *
682
- * @requires $rootScope
683
- * @requires $q
684
- * @requires ui.router.state.$view
685
- * @requires $injector
686
- * @requires ui.router.util.$resolve
687
- * @requires ui.router.state.$stateParams
688
- * @requires ui.router.router.$urlRouter
689
- *
690
- * @property {object} params A param object, e.g. {sectionId: section.id)}, that
691
- * you'd like to test against the current active state.
692
- * @property {object} current A reference to the state's config object. However
693
- * you passed it in. Useful for accessing custom data.
694
- * @property {object} transition Currently pending transition. A promise that'll
695
- * resolve or reject.
696
- *
697
- * @description
698
- * `$state` service is responsible for representing states as well as transitioning
699
- * between them. It also provides interfaces to ask for current state or even states
700
- * you're coming from.
701
- */
702
- this.$get = $get;
703
- $get.$inject = ['$rootScope', '$q', '$view', '$injector', '$resolve', '$stateParams', '$urlRouter', '$location', '$urlMatcherFactory'];
704
- function $get( $rootScope, $q, $view, $injector, $resolve, $stateParams, $urlRouter, $location, $urlMatcherFactory) {
705
-
706
- var TransitionSuperseded = $q.reject(new Error('transition superseded'));
707
- var TransitionPrevented = $q.reject(new Error('transition prevented'));
708
- var TransitionAborted = $q.reject(new Error('transition aborted'));
709
- var TransitionFailed = $q.reject(new Error('transition failed'));
710
-
711
- // Handles the case where a state which is the target of a transition is not found, and the user
712
- // can optionally retry or defer the transition
713
- function handleRedirect(redirect, state, params, options) {
714
- /**
715
- * @ngdoc event
716
- * @name ui.router.state.$state#$stateNotFound
717
- * @eventOf ui.router.state.$state
718
- * @eventType broadcast on root scope
719
- * @description
720
- * Fired when a requested state **cannot be found** using the provided state name during transition.
721
- * The event is broadcast allowing any handlers a single chance to deal with the error (usually by
722
- * lazy-loading the unfound state). A special `unfoundState` object is passed to the listener handler,
723
- * you can see its three properties in the example. You can use `event.preventDefault()` to abort the
724
- * transition and the promise returned from `go` will be rejected with a `'transition aborted'` value.
725
- *
726
- * @param {Object} event Event object.
727
- * @param {Object} unfoundState Unfound State information. Contains: `to, toParams, options` properties.
728
- * @param {State} fromState Current state object.
729
- * @param {Object} fromParams Current state params.
730
- *
731
- * @example
732
- *
733
- * <pre>
734
- * // somewhere, assume lazy.state has not been defined
735
- * $state.go("lazy.state", {a:1, b:2}, {inherit:false});
736
- *
737
- * // somewhere else
738
- * $scope.$on('$stateNotFound',
739
- * function(event, unfoundState, fromState, fromParams){
740
- * console.log(unfoundState.to); // "lazy.state"
741
- * console.log(unfoundState.toParams); // {a:1, b:2}
742
- * console.log(unfoundState.options); // {inherit:false} + default options
743
- * })
744
- * </pre>
745
- */
746
- var evt = $rootScope.$broadcast('$stateNotFound', redirect, state, params);
747
-
748
- if (evt.defaultPrevented) {
749
- $urlRouter.update();
750
- return TransitionAborted;
751
- }
752
-
753
- if (!evt.retry) {
754
- return null;
755
- }
756
-
757
- // Allow the handler to return a promise to defer state lookup retry
758
- if (options.$retry) {
759
- $urlRouter.update();
760
- return TransitionFailed;
761
- }
762
- var retryTransition = $state.transition = $q.when(evt.retry);
763
-
764
- retryTransition.then(function() {
765
- if (retryTransition !== $state.transition) return TransitionSuperseded;
766
- redirect.options.$retry = true;
767
- return $state.transitionTo(redirect.to, redirect.toParams, redirect.options);
768
- }, function() {
769
- return TransitionAborted;
770
- });
771
- $urlRouter.update();
772
-
773
- return retryTransition;
774
- }
775
-
776
- root.locals = { resolve: null, globals: { $stateParams: {} } };
777
-
778
- $state = {
779
- params: {},
780
- current: root.self,
781
- $current: root,
782
- transition: null
783
- };
784
-
785
- /**
786
- * @ngdoc function
787
- * @name ui.router.state.$state#reload
788
- * @methodOf ui.router.state.$state
789
- *
790
- * @description
791
- * A method that force reloads the current state. All resolves are re-resolved, events are not re-fired,
792
- * and controllers reinstantiated (bug with controllers reinstantiating right now, fixing soon).
793
- *
794
- * @example
795
- * <pre>
796
- * var app angular.module('app', ['ui.router']);
797
- *
798
- * app.controller('ctrl', function ($scope, $state) {
799
- * $scope.reload = function(){
800
- * $state.reload();
801
- * }
802
- * });
803
- * </pre>
804
- *
805
- * `reload()` is just an alias for:
806
- * <pre>
807
- * $state.transitionTo($state.current, $stateParams, {
808
- * reload: true, inherit: false, notify: true
809
- * });
810
- * </pre>
811
- *
812
- * @returns {promise} A promise representing the state of the new transition. See
813
- * {@link ui.router.state.$state#methods_go $state.go}.
814
- */
815
- $state.reload = function reload() {
816
- return $state.transitionTo($state.current, $stateParams, { reload: true, inherit: false, notify: true });
817
- };
818
-
819
- /**
820
- * @ngdoc function
821
- * @name ui.router.state.$state#go
822
- * @methodOf ui.router.state.$state
823
- *
824
- * @description
825
- * Convenience method for transitioning to a new state. `$state.go` calls
826
- * `$state.transitionTo` internally but automatically sets options to
827
- * `{ location: true, inherit: true, relative: $state.$current, notify: true }`.
828
- * This allows you to easily use an absolute or relative to path and specify
829
- * only the parameters you'd like to update (while letting unspecified parameters
830
- * inherit from the currently active ancestor states).
831
- *
832
- * @example
833
- * <pre>
834
- * var app = angular.module('app', ['ui.router']);
835
- *
836
- * app.controller('ctrl', function ($scope, $state) {
837
- * $scope.changeState = function () {
838
- * $state.go('contact.detail');
839
- * };
840
- * });
841
- * </pre>
842
- * <img src='../ngdoc_assets/StateGoExamples.png'/>
843
- *
844
- * @param {string} to Absolute state name or relative state path. Some examples:
845
- *
846
- * - `$state.go('contact.detail')` - will go to the `contact.detail` state
847
- * - `$state.go('^')` - will go to a parent state
848
- * - `$state.go('^.sibling')` - will go to a sibling state
849
- * - `$state.go('.child.grandchild')` - will go to grandchild state
850
- *
851
- * @param {object=} params A map of the parameters that will be sent to the state,
852
- * will populate $stateParams. Any parameters that are not specified will be inherited from currently
853
- * defined parameters. This allows, for example, going to a sibling state that shares parameters
854
- * specified in a parent state. Parameter inheritance only works between common ancestor states, I.e.
855
- * transitioning to a sibling will get you the parameters for all parents, transitioning to a child
856
- * will get you all current parameters, etc.
857
- * @param {object=} options Options object. The options are:
858
- *
859
- * - **`location`** - {boolean=true|string=} - If `true` will update the url in the location bar, if `false`
860
- * will not. If string, must be `"replace"`, which will update url and also replace last history record.
861
- * - **`inherit`** - {boolean=true}, If `true` will inherit url parameters from current url.
862
- * - **`relative`** - {object=$state.$current}, When transitioning with relative path (e.g '^'),
863
- * defines which state to be relative from.
864
- * - **`notify`** - {boolean=true}, If `true` will broadcast $stateChangeStart and $stateChangeSuccess events.
865
- * - **`reload`** (v0.2.5) - {boolean=false}, If `true` will force transition even if the state or params
866
- * have not changed, aka a reload of the same state. It differs from reloadOnSearch because you'd
867
- * use this when you want to force a reload when *everything* is the same, including search params.
868
- *
869
- * @returns {promise} A promise representing the state of the new transition.
870
- *
871
- * Possible success values:
872
- *
873
- * - $state.current
874
- *
875
- * <br/>Possible rejection values:
876
- *
877
- * - 'transition superseded' - when a newer transition has been started after this one
878
- * - 'transition prevented' - when `event.preventDefault()` has been called in a `$stateChangeStart` listener
879
- * - 'transition aborted' - when `event.preventDefault()` has been called in a `$stateNotFound` listener or
880
- * when a `$stateNotFound` `event.retry` promise errors.
881
- * - 'transition failed' - when a state has been unsuccessfully found after 2 tries.
882
- * - *resolve error* - when an error has occurred with a `resolve`
883
- *
884
- */
885
- $state.go = function go(to, params, options) {
886
- return $state.transitionTo(to, params, extend({ inherit: true, relative: $state.$current }, options));
887
- };
888
-
889
- /**
890
- * @ngdoc function
891
- * @name ui.router.state.$state#transitionTo
892
- * @methodOf ui.router.state.$state
893
- *
894
- * @description
895
- * Low-level method for transitioning to a new state. {@link ui.router.state.$state#methods_go $state.go}
896
- * uses `transitionTo` internally. `$state.go` is recommended in most situations.
897
- *
898
- * @example
899
- * <pre>
900
- * var app = angular.module('app', ['ui.router']);
901
- *
902
- * app.controller('ctrl', function ($scope, $state) {
903
- * $scope.changeState = function () {
904
- * $state.transitionTo('contact.detail');
905
- * };
906
- * });
907
- * </pre>
908
- *
909
- * @param {string} to State name.
910
- * @param {object=} toParams A map of the parameters that will be sent to the state,
911
- * will populate $stateParams.
912
- * @param {object=} options Options object. The options are:
913
- *
914
- * - **`location`** - {boolean=true|string=} - If `true` will update the url in the location bar, if `false`
915
- * will not. If string, must be `"replace"`, which will update url and also replace last history record.
916
- * - **`inherit`** - {boolean=false}, If `true` will inherit url parameters from current url.
917
- * - **`relative`** - {object=}, When transitioning with relative path (e.g '^'),
918
- * defines which state to be relative from.
919
- * - **`notify`** - {boolean=true}, If `true` will broadcast $stateChangeStart and $stateChangeSuccess events.
920
- * - **`reload`** (v0.2.5) - {boolean=false}, If `true` will force transition even if the state or params
921
- * have not changed, aka a reload of the same state. It differs from reloadOnSearch because you'd
922
- * use this when you want to force a reload when *everything* is the same, including search params.
923
- *
924
- * @returns {promise} A promise representing the state of the new transition. See
925
- * {@link ui.router.state.$state#methods_go $state.go}.
926
- */
927
- $state.transitionTo = function transitionTo(to, toParams, options) {
928
- toParams = toParams || {};
929
- options = extend({
930
- location: true, inherit: false, relative: null, notify: true, reload: false, $retry: false
931
- }, options || {});
932
-
933
- var from = $state.$current, fromParams = $state.params, fromPath = from.path;
934
- var evt, toState = findState(to, options.relative);
935
-
936
- if (!isDefined(toState)) {
937
- var redirect = { to: to, toParams: toParams, options: options };
938
- var redirectResult = handleRedirect(redirect, from.self, fromParams, options);
939
-
940
- if (redirectResult) {
941
- return redirectResult;
942
- }
943
-
944
- // Always retry once if the $stateNotFound was not prevented
945
- // (handles either redirect changed or state lazy-definition)
946
- to = redirect.to;
947
- toParams = redirect.toParams;
948
- options = redirect.options;
949
- toState = findState(to, options.relative);
950
-
951
- if (!isDefined(toState)) {
952
- if (!options.relative) throw new Error("No such state '" + to + "'");
953
- throw new Error("Could not resolve '" + to + "' from state '" + options.relative + "'");
954
- }
955
- }
956
- if (toState[abstractKey]) throw new Error("Cannot transition to abstract state '" + to + "'");
957
- if (options.inherit) toParams = inheritParams($stateParams, toParams || {}, $state.$current, toState);
958
- if (!toState.params.$$validates(toParams)) return TransitionFailed;
959
-
960
- toParams = toState.params.$$values(toParams);
961
- to = toState;
962
-
963
- var toPath = to.path;
964
-
965
- // Starting from the root of the path, keep all levels that haven't changed
966
- var keep = 0, state = toPath[keep], locals = root.locals, toLocals = [];
967
-
968
- if (!options.reload) {
969
- while (state && state === fromPath[keep] && state.ownParams.$$equals(toParams, fromParams)) {
970
- locals = toLocals[keep] = state.locals;
971
- keep++;
972
- state = toPath[keep];
973
- }
974
- }
975
-
976
- // If we're going to the same state and all locals are kept, we've got nothing to do.
977
- // But clear 'transition', as we still want to cancel any other pending transitions.
978
- // TODO: We may not want to bump 'transition' if we're called from a location change
979
- // that we've initiated ourselves, because we might accidentally abort a legitimate
980
- // transition initiated from code?
981
- if (shouldTriggerReload(to, from, locals, options)) {
982
- if (to.self.reloadOnSearch !== false) $urlRouter.update();
983
- $state.transition = null;
984
- return $q.when($state.current);
985
- }
986
-
987
- // Filter parameters before we pass them to event handlers etc.
988
- toParams = filterByKeys(to.params.$$keys(), toParams || {});
989
-
990
- // Broadcast start event and cancel the transition if requested
991
- if (options.notify) {
992
- /**
993
- * @ngdoc event
994
- * @name ui.router.state.$state#$stateChangeStart
995
- * @eventOf ui.router.state.$state
996
- * @eventType broadcast on root scope
997
- * @description
998
- * Fired when the state transition **begins**. You can use `event.preventDefault()`
999
- * to prevent the transition from happening and then the transition promise will be
1000
- * rejected with a `'transition prevented'` value.
1001
- *
1002
- * @param {Object} event Event object.
1003
- * @param {State} toState The state being transitioned to.
1004
- * @param {Object} toParams The params supplied to the `toState`.
1005
- * @param {State} fromState The current state, pre-transition.
1006
- * @param {Object} fromParams The params supplied to the `fromState`.
1007
- *
1008
- * @example
1009
- *
1010
- * <pre>
1011
- * $rootScope.$on('$stateChangeStart',
1012
- * function(event, toState, toParams, fromState, fromParams){
1013
- * event.preventDefault();
1014
- * // transitionTo() promise will be rejected with
1015
- * // a 'transition prevented' error
1016
- * })
1017
- * </pre>
1018
- */
1019
- if ($rootScope.$broadcast('$stateChangeStart', to.self, toParams, from.self, fromParams).defaultPrevented) {
1020
- $urlRouter.update();
1021
- return TransitionPrevented;
1022
- }
1023
- }
1024
-
1025
- // Resolve locals for the remaining states, but don't update any global state just
1026
- // yet -- if anything fails to resolve the current state needs to remain untouched.
1027
- // We also set up an inheritance chain for the locals here. This allows the view directive
1028
- // to quickly look up the correct definition for each view in the current state. Even
1029
- // though we create the locals object itself outside resolveState(), it is initially
1030
- // empty and gets filled asynchronously. We need to keep track of the promise for the
1031
- // (fully resolved) current locals, and pass this down the chain.
1032
- var resolved = $q.when(locals);
1033
-
1034
- for (var l = keep; l < toPath.length; l++, state = toPath[l]) {
1035
- locals = toLocals[l] = inherit(locals);
1036
- resolved = resolveState(state, toParams, state === to, resolved, locals, options);
1037
- }
1038
-
1039
- // Once everything is resolved, we are ready to perform the actual transition
1040
- // and return a promise for the new state. We also keep track of what the
1041
- // current promise is, so that we can detect overlapping transitions and
1042
- // keep only the outcome of the last transition.
1043
- var transition = $state.transition = resolved.then(function () {
1044
- var l, entering, exiting;
1045
-
1046
- if ($state.transition !== transition) return TransitionSuperseded;
1047
-
1048
- // Exit 'from' states not kept
1049
- for (l = fromPath.length - 1; l >= keep; l--) {
1050
- exiting = fromPath[l];
1051
- if (exiting.self.onExit) {
1052
- $injector.invoke(exiting.self.onExit, exiting.self, exiting.locals.globals);
1053
- }
1054
- exiting.locals = null;
1055
- }
1056
-
1057
- // Enter 'to' states not kept
1058
- for (l = keep; l < toPath.length; l++) {
1059
- entering = toPath[l];
1060
- entering.locals = toLocals[l];
1061
- if (entering.self.onEnter) {
1062
- $injector.invoke(entering.self.onEnter, entering.self, entering.locals.globals);
1063
- }
1064
- }
1065
-
1066
- // Run it again, to catch any transitions in callbacks
1067
- if ($state.transition !== transition) return TransitionSuperseded;
1068
-
1069
- // Update globals in $state
1070
- $state.$current = to;
1071
- $state.current = to.self;
1072
- $state.params = toParams;
1073
- copy($state.params, $stateParams);
1074
- $state.transition = null;
1075
-
1076
- if (options.location && to.navigable) {
1077
- $urlRouter.push(to.navigable.url, to.navigable.locals.globals.$stateParams, {
1078
- $$avoidResync: true, replace: options.location === 'replace'
1079
- });
1080
- }
1081
-
1082
- if (options.notify) {
1083
- /**
1084
- * @ngdoc event
1085
- * @name ui.router.state.$state#$stateChangeSuccess
1086
- * @eventOf ui.router.state.$state
1087
- * @eventType broadcast on root scope
1088
- * @description
1089
- * Fired once the state transition is **complete**.
1090
- *
1091
- * @param {Object} event Event object.
1092
- * @param {State} toState The state being transitioned to.
1093
- * @param {Object} toParams The params supplied to the `toState`.
1094
- * @param {State} fromState The current state, pre-transition.
1095
- * @param {Object} fromParams The params supplied to the `fromState`.
1096
- */
1097
- $rootScope.$broadcast('$stateChangeSuccess', to.self, toParams, from.self, fromParams);
1098
- }
1099
- $urlRouter.update(true);
1100
-
1101
- return $state.current;
1102
- }, function (error) {
1103
- if ($state.transition !== transition) return TransitionSuperseded;
1104
-
1105
- $state.transition = null;
1106
- /**
1107
- * @ngdoc event
1108
- * @name ui.router.state.$state#$stateChangeError
1109
- * @eventOf ui.router.state.$state
1110
- * @eventType broadcast on root scope
1111
- * @description
1112
- * Fired when an **error occurs** during transition. It's important to note that if you
1113
- * have any errors in your resolve functions (javascript errors, non-existent services, etc)
1114
- * they will not throw traditionally. You must listen for this $stateChangeError event to
1115
- * catch **ALL** errors.
1116
- *
1117
- * @param {Object} event Event object.
1118
- * @param {State} toState The state being transitioned to.
1119
- * @param {Object} toParams The params supplied to the `toState`.
1120
- * @param {State} fromState The current state, pre-transition.
1121
- * @param {Object} fromParams The params supplied to the `fromState`.
1122
- * @param {Error} error The resolve error object.
1123
- */
1124
- evt = $rootScope.$broadcast('$stateChangeError', to.self, toParams, from.self, fromParams, error);
1125
-
1126
- if (!evt.defaultPrevented) {
1127
- $urlRouter.update();
1128
- }
1129
-
1130
- return $q.reject(error);
1131
- });
1132
-
1133
- return transition;
1134
- };
1135
-
1136
- /**
1137
- * @ngdoc function
1138
- * @name ui.router.state.$state#is
1139
- * @methodOf ui.router.state.$state
1140
- *
1141
- * @description
1142
- * Similar to {@link ui.router.state.$state#methods_includes $state.includes},
1143
- * but only checks for the full state name. If params is supplied then it will be
1144
- * tested for strict equality against the current active params object, so all params
1145
- * must match with none missing and no extras.
1146
- *
1147
- * @example
1148
- * <pre>
1149
- * $state.$current.name = 'contacts.details.item';
1150
- *
1151
- * // absolute name
1152
- * $state.is('contact.details.item'); // returns true
1153
- * $state.is(contactDetailItemStateObject); // returns true
1154
- *
1155
- * // relative name (. and ^), typically from a template
1156
- * // E.g. from the 'contacts.details' template
1157
- * <div ng-class="{highlighted: $state.is('.item')}">Item</div>
1158
- * </pre>
1159
- *
1160
- * @param {string|object} stateOrName The state name (absolute or relative) or state object you'd like to check.
1161
- * @param {object=} params A param object, e.g. `{sectionId: section.id}`, that you'd like
1162
- * to test against the current active state.
1163
- * @param {object=} options An options object. The options are:
1164
- *
1165
- * - **`relative`** - {string|object} - If `stateOrName` is a relative state name and `options.relative` is set, .is will
1166
- * test relative to `options.relative` state (or name).
1167
- *
1168
- * @returns {boolean} Returns true if it is the state.
1169
- */
1170
- $state.is = function is(stateOrName, params, options) {
1171
- options = extend({ relative: $state.$current }, options || {});
1172
- var state = findState(stateOrName, options.relative);
1173
-
1174
- if (!isDefined(state)) { return undefined; }
1175
- if ($state.$current !== state) { return false; }
1176
- return params ? equalForKeys(state.params.$$values(params), $stateParams) : true;
1177
- };
1178
-
1179
- /**
1180
- * @ngdoc function
1181
- * @name ui.router.state.$state#includes
1182
- * @methodOf ui.router.state.$state
1183
- *
1184
- * @description
1185
- * A method to determine if the current active state is equal to or is the child of the
1186
- * state stateName. If any params are passed then they will be tested for a match as well.
1187
- * Not all the parameters need to be passed, just the ones you'd like to test for equality.
1188
- *
1189
- * @example
1190
- * Partial and relative names
1191
- * <pre>
1192
- * $state.$current.name = 'contacts.details.item';
1193
- *
1194
- * // Using partial names
1195
- * $state.includes("contacts"); // returns true
1196
- * $state.includes("contacts.details"); // returns true
1197
- * $state.includes("contacts.details.item"); // returns true
1198
- * $state.includes("contacts.list"); // returns false
1199
- * $state.includes("about"); // returns false
1200
- *
1201
- * // Using relative names (. and ^), typically from a template
1202
- * // E.g. from the 'contacts.details' template
1203
- * <div ng-class="{highlighted: $state.includes('.item')}">Item</div>
1204
- * </pre>
1205
- *
1206
- * Basic globbing patterns
1207
- * <pre>
1208
- * $state.$current.name = 'contacts.details.item.url';
1209
- *
1210
- * $state.includes("*.details.*.*"); // returns true
1211
- * $state.includes("*.details.**"); // returns true
1212
- * $state.includes("**.item.**"); // returns true
1213
- * $state.includes("*.details.item.url"); // returns true
1214
- * $state.includes("*.details.*.url"); // returns true
1215
- * $state.includes("*.details.*"); // returns false
1216
- * $state.includes("item.**"); // returns false
1217
- * </pre>
1218
- *
1219
- * @param {string} stateOrName A partial name, relative name, or glob pattern
1220
- * to be searched for within the current state name.
1221
- * @param {object=} params A param object, e.g. `{sectionId: section.id}`,
1222
- * that you'd like to test against the current active state.
1223
- * @param {object=} options An options object. The options are:
1224
- *
1225
- * - **`relative`** - {string|object=} - If `stateOrName` is a relative state reference and `options.relative` is set,
1226
- * .includes will test relative to `options.relative` state (or name).
1227
- *
1228
- * @returns {boolean} Returns true if it does include the state
1229
- */
1230
- $state.includes = function includes(stateOrName, params, options) {
1231
- options = extend({ relative: $state.$current }, options || {});
1232
- if (isString(stateOrName) && isGlob(stateOrName)) {
1233
- if (!doesStateMatchGlob(stateOrName)) {
1234
- return false;
1235
- }
1236
- stateOrName = $state.$current.name;
1237
- }
1238
-
1239
- var state = findState(stateOrName, options.relative);
1240
- if (!isDefined(state)) { return undefined; }
1241
- if (!isDefined($state.$current.includes[state.name])) { return false; }
1242
- return params ? equalForKeys(state.params.$$values(params), $stateParams, objectKeys(params)) : true;
1243
- };
1244
-
1245
-
1246
- /**
1247
- * @ngdoc function
1248
- * @name ui.router.state.$state#href
1249
- * @methodOf ui.router.state.$state
1250
- *
1251
- * @description
1252
- * A url generation method that returns the compiled url for the given state populated with the given params.
1253
- *
1254
- * @example
1255
- * <pre>
1256
- * expect($state.href("about.person", { person: "bob" })).toEqual("/about/bob");
1257
- * </pre>
1258
- *
1259
- * @param {string|object} stateOrName The state name or state object you'd like to generate a url from.
1260
- * @param {object=} params An object of parameter values to fill the state's required parameters.
1261
- * @param {object=} options Options object. The options are:
1262
- *
1263
- * - **`lossy`** - {boolean=true} - If true, and if there is no url associated with the state provided in the
1264
- * first parameter, then the constructed href url will be built from the first navigable ancestor (aka
1265
- * ancestor with a valid url).
1266
- * - **`inherit`** - {boolean=true}, If `true` will inherit url parameters from current url.
1267
- * - **`relative`** - {object=$state.$current}, When transitioning with relative path (e.g '^'),
1268
- * defines which state to be relative from.
1269
- * - **`absolute`** - {boolean=false}, If true will generate an absolute url, e.g. "http://www.example.com/fullurl".
1270
- *
1271
- * @returns {string} compiled state url
1272
- */
1273
- $state.href = function href(stateOrName, params, options) {
1274
- options = extend({
1275
- lossy: true,
1276
- inherit: true,
1277
- absolute: false,
1278
- relative: $state.$current
1279
- }, options || {});
1280
-
1281
- var state = findState(stateOrName, options.relative);
1282
-
1283
- if (!isDefined(state)) return null;
1284
- if (options.inherit) params = inheritParams($stateParams, params || {}, $state.$current, state);
1285
-
1286
- var nav = (state && options.lossy) ? state.navigable : state;
1287
-
1288
- if (!nav || nav.url === undefined || nav.url === null) {
1289
- return null;
1290
- }
1291
- return $urlRouter.href(nav.url, filterByKeys(state.params.$$keys(), params || {}), {
1292
- absolute: options.absolute
1293
- });
1294
- };
1295
-
1296
- /**
1297
- * @ngdoc function
1298
- * @name ui.router.state.$state#get
1299
- * @methodOf ui.router.state.$state
1300
- *
1301
- * @description
1302
- * Returns the state configuration object for any specific state or all states.
1303
- *
1304
- * @param {string|object=} stateOrName (absolute or relative) If provided, will only get the config for
1305
- * the requested state. If not provided, returns an array of ALL state configs.
1306
- * @param {string|object=} context When stateOrName is a relative state reference, the state will be retrieved relative to context.
1307
- * @returns {Object|Array} State configuration object or array of all objects.
1308
- */
1309
- $state.get = function (stateOrName, context) {
1310
- if (arguments.length === 0) return map(objectKeys(states), function(name) { return states[name].self; });
1311
- var state = findState(stateOrName, context || $state.$current);
1312
- return (state && state.self) ? state.self : null;
1313
- };
1314
-
1315
- function resolveState(state, params, paramsAreFiltered, inherited, dst, options) {
1316
- // Make a restricted $stateParams with only the parameters that apply to this state if
1317
- // necessary. In addition to being available to the controller and onEnter/onExit callbacks,
1318
- // we also need $stateParams to be available for any $injector calls we make during the
1319
- // dependency resolution process.
1320
- var $stateParams = (paramsAreFiltered) ? params : filterByKeys(state.params.$$keys(), params);
1321
- var locals = { $stateParams: $stateParams };
1322
-
1323
- // Resolve 'global' dependencies for the state, i.e. those not specific to a view.
1324
- // We're also including $stateParams in this; that way the parameters are restricted
1325
- // to the set that should be visible to the state, and are independent of when we update
1326
- // the global $state and $stateParams values.
1327
- dst.resolve = $resolve.resolve(state.resolve, locals, dst.resolve, state);
1328
- var promises = [dst.resolve.then(function (globals) {
1329
- dst.globals = globals;
1330
- })];
1331
- if (inherited) promises.push(inherited);
1332
-
1333
- // Resolve template and dependencies for all views.
1334
- forEach(state.views, function (view, name) {
1335
- var injectables = (view.resolve && view.resolve !== state.resolve ? view.resolve : {});
1336
- injectables.$template = [ function () {
1337
- return $view.load(name, { view: view, locals: locals, params: $stateParams, notify: options.notify }) || '';
1338
- }];
1339
-
1340
- promises.push($resolve.resolve(injectables, locals, dst.resolve, state).then(function (result) {
1341
- // References to the controller (only instantiated at link time)
1342
- if (isFunction(view.controllerProvider) || isArray(view.controllerProvider)) {
1343
- var injectLocals = angular.extend({}, injectables, locals);
1344
- result.$$controller = $injector.invoke(view.controllerProvider, null, injectLocals);
1345
- } else {
1346
- result.$$controller = view.controller;
1347
- }
1348
- // Provide access to the state itself for internal use
1349
- result.$$state = state;
1350
- result.$$controllerAs = view.controllerAs;
1351
- dst[name] = result;
1352
- }));
1353
- });
1354
-
1355
- // Wait for all the promises and then return the activation object
1356
- return $q.all(promises).then(function (values) {
1357
- return dst;
1358
- });
1359
- }
1360
-
1361
- return $state;
1362
- }
1363
-
1364
- function shouldTriggerReload(to, from, locals, options) {
1365
- if (to === from && ((locals === from.locals && !options.reload) || (to.self.reloadOnSearch === false))) {
1366
- return true;
1367
- }
1368
- }
1369
- }
1370
-
1371
- angular.module('ui.router.state')
1372
- .value('$stateParams', {})
1373
- .provider('$state', $StateProvider);