kms 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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,268 +0,0 @@
1
- function parseStateRef(ref, current) {
2
- var preparsed = ref.match(/^\s*({[^}]*})\s*$/), parsed;
3
- if (preparsed) ref = current + '(' + preparsed[1] + ')';
4
- parsed = ref.replace(/\n/g, " ").match(/^([^(]+?)\s*(\((.*)\))?$/);
5
- if (!parsed || parsed.length !== 4) throw new Error("Invalid state ref '" + ref + "'");
6
- return { state: parsed[1], paramExpr: parsed[3] || null };
7
- }
8
-
9
- function stateContext(el) {
10
- var stateData = el.parent().inheritedData('$uiView');
11
-
12
- if (stateData && stateData.state && stateData.state.name) {
13
- return stateData.state;
14
- }
15
- }
16
-
17
- /**
18
- * @ngdoc directive
19
- * @name ui.router.state.directive:ui-sref
20
- *
21
- * @requires ui.router.state.$state
22
- * @requires $timeout
23
- *
24
- * @restrict A
25
- *
26
- * @description
27
- * A directive that binds a link (`<a>` tag) to a state. If the state has an associated
28
- * URL, the directive will automatically generate & update the `href` attribute via
29
- * the {@link ui.router.state.$state#methods_href $state.href()} method. Clicking
30
- * the link will trigger a state transition with optional parameters.
31
- *
32
- * Also middle-clicking, right-clicking, and ctrl-clicking on the link will be
33
- * handled natively by the browser.
34
- *
35
- * You can also use relative state paths within ui-sref, just like the relative
36
- * paths passed to `$state.go()`. You just need to be aware that the path is relative
37
- * to the state that the link lives in, in other words the state that loaded the
38
- * template containing the link.
39
- *
40
- * You can specify options to pass to {@link ui.router.state.$state#go $state.go()}
41
- * using the `ui-sref-opts` attribute. Options are restricted to `location`, `inherit`,
42
- * and `reload`.
43
- *
44
- * @example
45
- * Here's an example of how you'd use ui-sref and how it would compile. If you have the
46
- * following template:
47
- * <pre>
48
- * <a ui-sref="home">Home</a> | <a ui-sref="about">About</a> | <a ui-sref="{page: 2}">Next page</a>
49
- *
50
- * <ul>
51
- * <li ng-repeat="contact in contacts">
52
- * <a ui-sref="contacts.detail({ id: contact.id })">{{ contact.name }}</a>
53
- * </li>
54
- * </ul>
55
- * </pre>
56
- *
57
- * Then the compiled html would be (assuming Html5Mode is off and current state is contacts):
58
- * <pre>
59
- * <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>
60
- *
61
- * <ul>
62
- * <li ng-repeat="contact in contacts">
63
- * <a href="#/contacts/1" ui-sref="contacts.detail({ id: contact.id })">Joe</a>
64
- * </li>
65
- * <li ng-repeat="contact in contacts">
66
- * <a href="#/contacts/2" ui-sref="contacts.detail({ id: contact.id })">Alice</a>
67
- * </li>
68
- * <li ng-repeat="contact in contacts">
69
- * <a href="#/contacts/3" ui-sref="contacts.detail({ id: contact.id })">Bob</a>
70
- * </li>
71
- * </ul>
72
- *
73
- * <a ui-sref="home" ui-sref-opts="{reload: true}">Home</a>
74
- * </pre>
75
- *
76
- * @param {string} ui-sref 'stateName' can be any valid absolute or relative state
77
- * @param {Object} ui-sref-opts options to pass to {@link ui.router.state.$state#go $state.go()}
78
- */
79
- $StateRefDirective.$inject = ['$state', '$timeout'];
80
- function $StateRefDirective($state, $timeout) {
81
- var allowedOptions = ['location', 'inherit', 'reload'];
82
-
83
- return {
84
- restrict: 'A',
85
- require: ['?^uiSrefActive', '?^uiSrefActiveEq'],
86
- link: function(scope, element, attrs, uiSrefActive) {
87
- var ref = parseStateRef(attrs.uiSref, $state.current.name);
88
- var params = null, url = null, base = stateContext(element) || $state.$current;
89
- var newHref = null, isAnchor = element.prop("tagName") === "A";
90
- var isForm = element[0].nodeName === "FORM";
91
- var attr = isForm ? "action" : "href", nav = true;
92
-
93
- var options = { relative: base, inherit: true };
94
- var optionsOverride = scope.$eval(attrs.uiSrefOpts) || {};
95
-
96
- angular.forEach(allowedOptions, function(option) {
97
- if (option in optionsOverride) {
98
- options[option] = optionsOverride[option];
99
- }
100
- });
101
-
102
- var update = function(newVal) {
103
- if (newVal) params = angular.copy(newVal);
104
- if (!nav) return;
105
-
106
- newHref = $state.href(ref.state, params, options);
107
-
108
- var activeDirective = uiSrefActive[1] || uiSrefActive[0];
109
- if (activeDirective) {
110
- activeDirective.$$setStateInfo(ref.state, params);
111
- }
112
- if (newHref === null) {
113
- nav = false;
114
- return false;
115
- }
116
- attrs.$set(attr, newHref);
117
- };
118
-
119
- if (ref.paramExpr) {
120
- scope.$watch(ref.paramExpr, function(newVal, oldVal) {
121
- if (newVal !== params) update(newVal);
122
- }, true);
123
- params = angular.copy(scope.$eval(ref.paramExpr));
124
- }
125
- update();
126
-
127
- if (isForm) return;
128
-
129
- element.bind("click", function(e) {
130
- var button = e.which || e.button;
131
- if ( !(button > 1 || e.ctrlKey || e.metaKey || e.shiftKey || element.attr('target')) ) {
132
- // HACK: This is to allow ng-clicks to be processed before the transition is initiated:
133
- var transition = $timeout(function() {
134
- $state.go(ref.state, params, options);
135
- });
136
- e.preventDefault();
137
-
138
- // if the state has no URL, ignore one preventDefault from the <a> directive.
139
- var ignorePreventDefaultCount = isAnchor && !newHref ? 1: 0;
140
- e.preventDefault = function() {
141
- if (ignorePreventDefaultCount-- <= 0)
142
- $timeout.cancel(transition);
143
- };
144
- }
145
- });
146
- }
147
- };
148
- }
149
-
150
- /**
151
- * @ngdoc directive
152
- * @name ui.router.state.directive:ui-sref-active
153
- *
154
- * @requires ui.router.state.$state
155
- * @requires ui.router.state.$stateParams
156
- * @requires $interpolate
157
- *
158
- * @restrict A
159
- *
160
- * @description
161
- * A directive working alongside ui-sref to add classes to an element when the
162
- * related ui-sref directive's state is active, and removing them when it is inactive.
163
- * The primary use-case is to simplify the special appearance of navigation menus
164
- * relying on `ui-sref`, by having the "active" state's menu button appear different,
165
- * distinguishing it from the inactive menu items.
166
- *
167
- * ui-sref-active can live on the same element as ui-sref or on a parent element. The first
168
- * ui-sref-active found at the same level or above the ui-sref will be used.
169
- *
170
- * Will activate when the ui-sref's target state or any child state is active. If you
171
- * need to activate only when the ui-sref target state is active and *not* any of
172
- * it's children, then you will use
173
- * {@link ui.router.state.directive:ui-sref-active-eq ui-sref-active-eq}
174
- *
175
- * @example
176
- * Given the following template:
177
- * <pre>
178
- * <ul>
179
- * <li ui-sref-active="active" class="item">
180
- * <a href ui-sref="app.user({user: 'bilbobaggins'})">@bilbobaggins</a>
181
- * </li>
182
- * </ul>
183
- * </pre>
184
- *
185
- *
186
- * When the app state is "app.user" (or any children states), and contains the state parameter "user" with value "bilbobaggins",
187
- * the resulting HTML will appear as (note the 'active' class):
188
- * <pre>
189
- * <ul>
190
- * <li ui-sref-active="active" class="item active">
191
- * <a ui-sref="app.user({user: 'bilbobaggins'})" href="/users/bilbobaggins">@bilbobaggins</a>
192
- * </li>
193
- * </ul>
194
- * </pre>
195
- *
196
- * The class name is interpolated **once** during the directives link time (any further changes to the
197
- * interpolated value are ignored).
198
- *
199
- * Multiple classes may be specified in a space-separated format:
200
- * <pre>
201
- * <ul>
202
- * <li ui-sref-active='class1 class2 class3'>
203
- * <a ui-sref="app.user">link</a>
204
- * </li>
205
- * </ul>
206
- * </pre>
207
- */
208
-
209
- /**
210
- * @ngdoc directive
211
- * @name ui.router.state.directive:ui-sref-active-eq
212
- *
213
- * @requires ui.router.state.$state
214
- * @requires ui.router.state.$stateParams
215
- * @requires $interpolate
216
- *
217
- * @restrict A
218
- *
219
- * @description
220
- * The same as {@link ui.router.state.directive:ui-sref-active ui-sref-active} but will only activate
221
- * when the exact target state used in the `ui-sref` is active; no child states.
222
- *
223
- */
224
- $StateRefActiveDirective.$inject = ['$state', '$stateParams', '$interpolate'];
225
- function $StateRefActiveDirective($state, $stateParams, $interpolate) {
226
- return {
227
- restrict: "A",
228
- controller: ['$scope', '$element', '$attrs', function ($scope, $element, $attrs) {
229
- var state, params, activeClass;
230
-
231
- // There probably isn't much point in $observing this
232
- // uiSrefActive and uiSrefActiveEq share the same directive object with some
233
- // slight difference in logic routing
234
- activeClass = $interpolate($attrs.uiSrefActiveEq || $attrs.uiSrefActive || '', false)($scope);
235
-
236
- // Allow uiSref to communicate with uiSrefActive[Equals]
237
- this.$$setStateInfo = function (newState, newParams) {
238
- state = $state.get(newState, stateContext($element));
239
- params = newParams;
240
- update();
241
- };
242
-
243
- $scope.$on('$stateChangeSuccess', update);
244
-
245
- // Update route state
246
- function update() {
247
- if (isMatch()) {
248
- $element.addClass(activeClass);
249
- } else {
250
- $element.removeClass(activeClass);
251
- }
252
- }
253
-
254
- function isMatch() {
255
- if (typeof $attrs.uiSrefActiveEq !== 'undefined') {
256
- return state && $state.is(state.name, params);
257
- } else {
258
- return state && $state.includes(state.name, params);
259
- }
260
- }
261
- }]
262
- };
263
- }
264
-
265
- angular.module('ui.router.state')
266
- .directive('uiSref', $StateRefDirective)
267
- .directive('uiSrefActive', $StateRefActiveDirective)
268
- .directive('uiSrefActiveEq', $StateRefActiveDirective);
@@ -1,39 +0,0 @@
1
- /**
2
- * @ngdoc filter
3
- * @name ui.router.state.filter:isState
4
- *
5
- * @requires ui.router.state.$state
6
- *
7
- * @description
8
- * Translates to {@link ui.router.state.$state#methods_is $state.is("stateName")}.
9
- */
10
- $IsStateFilter.$inject = ['$state'];
11
- function $IsStateFilter($state) {
12
- var isFilter = function (state) {
13
- return $state.is(state);
14
- };
15
- isFilter.$stateful = true;
16
- return isFilter;
17
- }
18
-
19
- /**
20
- * @ngdoc filter
21
- * @name ui.router.state.filter:includedByState
22
- *
23
- * @requires ui.router.state.$state
24
- *
25
- * @description
26
- * Translates to {@link ui.router.state.$state#methods_includes $state.includes('fullOrPartialStateName')}.
27
- */
28
- $IncludedByStateFilter.$inject = ['$state'];
29
- function $IncludedByStateFilter($state) {
30
- var includesFilter = function (state) {
31
- return $state.includes(state);
32
- };
33
- includesFilter.$stateful = true;
34
- return includesFilter;
35
- }
36
-
37
- angular.module('ui.router.state')
38
- .filter('isState', $IsStateFilter)
39
- .filter('includedByState', $IncludedByStateFilter);
@@ -1,110 +0,0 @@
1
- /**
2
- * @ngdoc object
3
- * @name ui.router.util.$templateFactory
4
- *
5
- * @requires $http
6
- * @requires $templateCache
7
- * @requires $injector
8
- *
9
- * @description
10
- * Service. Manages loading of templates.
11
- */
12
- $TemplateFactory.$inject = ['$http', '$templateCache', '$injector'];
13
- function $TemplateFactory( $http, $templateCache, $injector) {
14
-
15
- /**
16
- * @ngdoc function
17
- * @name ui.router.util.$templateFactory#fromConfig
18
- * @methodOf ui.router.util.$templateFactory
19
- *
20
- * @description
21
- * Creates a template from a configuration object.
22
- *
23
- * @param {object} config Configuration object for which to load a template.
24
- * The following properties are search in the specified order, and the first one
25
- * that is defined is used to create the template:
26
- *
27
- * @param {string|object} config.template html string template or function to
28
- * load via {@link ui.router.util.$templateFactory#fromString fromString}.
29
- * @param {string|object} config.templateUrl url to load or a function returning
30
- * the url to load via {@link ui.router.util.$templateFactory#fromUrl fromUrl}.
31
- * @param {Function} config.templateProvider function to invoke via
32
- * {@link ui.router.util.$templateFactory#fromProvider fromProvider}.
33
- * @param {object} params Parameters to pass to the template function.
34
- * @param {object} locals Locals to pass to `invoke` if the template is loaded
35
- * via a `templateProvider`. Defaults to `{ params: params }`.
36
- *
37
- * @return {string|object} The template html as a string, or a promise for
38
- * that string,or `null` if no template is configured.
39
- */
40
- this.fromConfig = function (config, params, locals) {
41
- return (
42
- isDefined(config.template) ? this.fromString(config.template, params) :
43
- isDefined(config.templateUrl) ? this.fromUrl(config.templateUrl, params) :
44
- isDefined(config.templateProvider) ? this.fromProvider(config.templateProvider, params, locals) :
45
- null
46
- );
47
- };
48
-
49
- /**
50
- * @ngdoc function
51
- * @name ui.router.util.$templateFactory#fromString
52
- * @methodOf ui.router.util.$templateFactory
53
- *
54
- * @description
55
- * Creates a template from a string or a function returning a string.
56
- *
57
- * @param {string|object} template html template as a string or function that
58
- * returns an html template as a string.
59
- * @param {object} params Parameters to pass to the template function.
60
- *
61
- * @return {string|object} The template html as a string, or a promise for that
62
- * string.
63
- */
64
- this.fromString = function (template, params) {
65
- return isFunction(template) ? template(params) : template;
66
- };
67
-
68
- /**
69
- * @ngdoc function
70
- * @name ui.router.util.$templateFactory#fromUrl
71
- * @methodOf ui.router.util.$templateFactory
72
- *
73
- * @description
74
- * Loads a template from the a URL via `$http` and `$templateCache`.
75
- *
76
- * @param {string|Function} url url of the template to load, or a function
77
- * that returns a url.
78
- * @param {Object} params Parameters to pass to the url function.
79
- * @return {string|Promise.<string>} The template html as a string, or a promise
80
- * for that string.
81
- */
82
- this.fromUrl = function (url, params) {
83
- if (isFunction(url)) url = url(params);
84
- if (url == null) return null;
85
- else return $http
86
- .get(url, { cache: $templateCache, headers: { Accept: 'text/html' }})
87
- .then(function(response) { return response.data; });
88
- };
89
-
90
- /**
91
- * @ngdoc function
92
- * @name ui.router.util.$templateFactory#fromProvider
93
- * @methodOf ui.router.util.$templateFactory
94
- *
95
- * @description
96
- * Creates a template by invoking an injectable provider function.
97
- *
98
- * @param {Function} provider Function to invoke via `$injector.invoke`
99
- * @param {Object} params Parameters for the template.
100
- * @param {Object} locals Locals to pass to `invoke`. Defaults to
101
- * `{ params: params }`.
102
- * @return {string|Promise.<string>} The template html as a string, or a promise
103
- * for that string.
104
- */
105
- this.fromProvider = function (provider, params, locals) {
106
- return $injector.invoke(provider, null, locals || { params: params });
107
- };
108
- }
109
-
110
- angular.module('ui.router.util').service('$templateFactory', $TemplateFactory);
@@ -1,1036 +0,0 @@
1
- var $$UMFP; // reference to $UrlMatcherFactoryProvider
2
-
3
- /**
4
- * @ngdoc object
5
- * @name ui.router.util.type:UrlMatcher
6
- *
7
- * @description
8
- * Matches URLs against patterns and extracts named parameters from the path or the search
9
- * part of the URL. A URL pattern consists of a path pattern, optionally followed by '?' and a list
10
- * of search parameters. Multiple search parameter names are separated by '&'. Search parameters
11
- * do not influence whether or not a URL is matched, but their values are passed through into
12
- * the matched parameters returned by {@link ui.router.util.type:UrlMatcher#methods_exec exec}.
13
- *
14
- * Path parameter placeholders can be specified using simple colon/catch-all syntax or curly brace
15
- * syntax, which optionally allows a regular expression for the parameter to be specified:
16
- *
17
- * * `':'` name - colon placeholder
18
- * * `'*'` name - catch-all placeholder
19
- * * `'{' name '}'` - curly placeholder
20
- * * `'{' name ':' regexp|type '}'` - curly placeholder with regexp or type name. Should the
21
- * regexp itself contain curly braces, they must be in matched pairs or escaped with a backslash.
22
- *
23
- * Parameter names may contain only word characters (latin letters, digits, and underscore) and
24
- * must be unique within the pattern (across both path and search parameters). For colon
25
- * placeholders or curly placeholders without an explicit regexp, a path parameter matches any
26
- * number of characters other than '/'. For catch-all placeholders the path parameter matches
27
- * any number of characters.
28
- *
29
- * Examples:
30
- *
31
- * * `'/hello/'` - Matches only if the path is exactly '/hello/'. There is no special treatment for
32
- * trailing slashes, and patterns have to match the entire path, not just a prefix.
33
- * * `'/user/:id'` - Matches '/user/bob' or '/user/1234!!!' or even '/user/' but not '/user' or
34
- * '/user/bob/details'. The second path segment will be captured as the parameter 'id'.
35
- * * `'/user/{id}'` - Same as the previous example, but using curly brace syntax.
36
- * * `'/user/{id:[^/]*}'` - Same as the previous example.
37
- * * `'/user/{id:[0-9a-fA-F]{1,8}}'` - Similar to the previous example, but only matches if the id
38
- * parameter consists of 1 to 8 hex digits.
39
- * * `'/files/{path:.*}'` - Matches any URL starting with '/files/' and captures the rest of the
40
- * path into the parameter 'path'.
41
- * * `'/files/*path'` - ditto.
42
- * * `'/calendar/{start:date}'` - Matches "/calendar/2014-11-12" (because the pattern defined
43
- * in the built-in `date` Type matches `2014-11-12`) and provides a Date object in $stateParams.start
44
- *
45
- * @param {string} pattern The pattern to compile into a matcher.
46
- * @param {Object} config A configuration object hash:
47
- * @param {Object=} parentMatcher Used to concatenate the pattern/config onto
48
- * an existing UrlMatcher
49
- *
50
- * * `caseInsensitive` - `true` if URL matching should be case insensitive, otherwise `false`, the default value (for backward compatibility) is `false`.
51
- * * `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`.
52
- *
53
- * @property {string} prefix A static prefix of this pattern. The matcher guarantees that any
54
- * URL matching this matcher (i.e. any string for which {@link ui.router.util.type:UrlMatcher#methods_exec exec()} returns
55
- * non-null) will start with this prefix.
56
- *
57
- * @property {string} source The pattern that was passed into the constructor
58
- *
59
- * @property {string} sourcePath The path portion of the source property
60
- *
61
- * @property {string} sourceSearch The search portion of the source property
62
- *
63
- * @property {string} regex The constructed regex that will be used to match against the url when
64
- * it is time to determine which url will match.
65
- *
66
- * @returns {Object} New `UrlMatcher` object
67
- */
68
- function UrlMatcher(pattern, config, parentMatcher) {
69
- config = extend({ params: {} }, isObject(config) ? config : {});
70
-
71
- // Find all placeholders and create a compiled pattern, using either classic or curly syntax:
72
- // '*' name
73
- // ':' name
74
- // '{' name '}'
75
- // '{' name ':' regexp '}'
76
- // The regular expression is somewhat complicated due to the need to allow curly braces
77
- // inside the regular expression. The placeholder regexp breaks down as follows:
78
- // ([:*])([\w\[\]]+) - classic placeholder ($1 / $2) (search version has - for snake-case)
79
- // \{([\w\[\]]+)(?:\:( ... ))?\} - curly brace placeholder ($3) with optional regexp/type ... ($4) (search version has - for snake-case
80
- // (?: ... | ... | ... )+ - the regexp consists of any number of atoms, an atom being either
81
- // [^{}\\]+ - anything other than curly braces or backslash
82
- // \\. - a backslash escape
83
- // \{(?:[^{}\\]+|\\.)*\} - a matched set of curly braces containing other atoms
84
- var placeholder = /([:*])([\w\[\]]+)|\{([\w\[\]]+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,
85
- searchPlaceholder = /([:]?)([\w\[\]-]+)|\{([\w\[\]-]+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,
86
- compiled = '^', last = 0, m,
87
- segments = this.segments = [],
88
- parentParams = parentMatcher ? parentMatcher.params : {},
89
- params = this.params = parentMatcher ? parentMatcher.params.$$new() : new $$UMFP.ParamSet(),
90
- paramNames = [];
91
-
92
- function addParameter(id, type, config, location) {
93
- paramNames.push(id);
94
- if (parentParams[id]) return parentParams[id];
95
- if (!/^\w+(-+\w+)*(?:\[\])?$/.test(id)) throw new Error("Invalid parameter name '" + id + "' in pattern '" + pattern + "'");
96
- if (params[id]) throw new Error("Duplicate parameter name '" + id + "' in pattern '" + pattern + "'");
97
- params[id] = new $$UMFP.Param(id, type, config, location);
98
- return params[id];
99
- }
100
-
101
- function quoteRegExp(string, pattern, squash) {
102
- var surroundPattern = ['',''], result = string.replace(/[\\\[\]\^$*+?.()|{}]/g, "\\$&");
103
- if (!pattern) return result;
104
- switch(squash) {
105
- case false: surroundPattern = ['(', ')']; break;
106
- case true: surroundPattern = ['?(', ')?']; break;
107
- default: surroundPattern = ['(' + squash + "|", ')?']; break;
108
- }
109
- return result + surroundPattern[0] + pattern + surroundPattern[1];
110
- }
111
-
112
- this.source = pattern;
113
-
114
- // Split into static segments separated by path parameter placeholders.
115
- // The number of segments is always 1 more than the number of parameters.
116
- function matchDetails(m, isSearch) {
117
- var id, regexp, segment, type, cfg, arrayMode;
118
- id = m[2] || m[3]; // IE[78] returns '' for unmatched groups instead of null
119
- cfg = config.params[id];
120
- segment = pattern.substring(last, m.index);
121
- regexp = isSearch ? m[4] : m[4] || (m[1] == '*' ? '.*' : null);
122
- type = $$UMFP.type(regexp || "string") || inherit($$UMFP.type("string"), { pattern: new RegExp(regexp) });
123
- return {
124
- id: id, regexp: regexp, segment: segment, type: type, cfg: cfg
125
- };
126
- }
127
-
128
- var p, param, segment;
129
- while ((m = placeholder.exec(pattern))) {
130
- p = matchDetails(m, false);
131
- if (p.segment.indexOf('?') >= 0) break; // we're into the search part
132
-
133
- param = addParameter(p.id, p.type, p.cfg, "path");
134
- compiled += quoteRegExp(p.segment, param.type.pattern.source, param.squash);
135
- segments.push(p.segment);
136
- last = placeholder.lastIndex;
137
- }
138
- segment = pattern.substring(last);
139
-
140
- // Find any search parameter names and remove them from the last segment
141
- var i = segment.indexOf('?');
142
-
143
- if (i >= 0) {
144
- var search = this.sourceSearch = segment.substring(i);
145
- segment = segment.substring(0, i);
146
- this.sourcePath = pattern.substring(0, last + i);
147
-
148
- if (search.length > 0) {
149
- last = 0;
150
- while ((m = searchPlaceholder.exec(search))) {
151
- p = matchDetails(m, true);
152
- param = addParameter(p.id, p.type, p.cfg, "search");
153
- last = placeholder.lastIndex;
154
- // check if ?&
155
- }
156
- }
157
- } else {
158
- this.sourcePath = pattern;
159
- this.sourceSearch = '';
160
- }
161
-
162
- compiled += quoteRegExp(segment) + (config.strict === false ? '\/?' : '') + '$';
163
- segments.push(segment);
164
-
165
- this.regexp = new RegExp(compiled, config.caseInsensitive ? 'i' : undefined);
166
- this.prefix = segments[0];
167
- this.$$paramNames = paramNames;
168
- }
169
-
170
- /**
171
- * @ngdoc function
172
- * @name ui.router.util.type:UrlMatcher#concat
173
- * @methodOf ui.router.util.type:UrlMatcher
174
- *
175
- * @description
176
- * Returns a new matcher for a pattern constructed by appending the path part and adding the
177
- * search parameters of the specified pattern to this pattern. The current pattern is not
178
- * modified. This can be understood as creating a pattern for URLs that are relative to (or
179
- * suffixes of) the current pattern.
180
- *
181
- * @example
182
- * The following two matchers are equivalent:
183
- * <pre>
184
- * new UrlMatcher('/user/{id}?q').concat('/details?date');
185
- * new UrlMatcher('/user/{id}/details?q&date');
186
- * </pre>
187
- *
188
- * @param {string} pattern The pattern to append.
189
- * @param {Object} config An object hash of the configuration for the matcher.
190
- * @returns {UrlMatcher} A matcher for the concatenated pattern.
191
- */
192
- UrlMatcher.prototype.concat = function (pattern, config) {
193
- // Because order of search parameters is irrelevant, we can add our own search
194
- // parameters to the end of the new pattern. Parse the new pattern by itself
195
- // and then join the bits together, but it's much easier to do this on a string level.
196
- var defaultConfig = {
197
- caseInsensitive: $$UMFP.caseInsensitive(),
198
- strict: $$UMFP.strictMode(),
199
- squash: $$UMFP.defaultSquashPolicy()
200
- };
201
- return new UrlMatcher(this.sourcePath + pattern + this.sourceSearch, extend(defaultConfig, config), this);
202
- };
203
-
204
- UrlMatcher.prototype.toString = function () {
205
- return this.source;
206
- };
207
-
208
- /**
209
- * @ngdoc function
210
- * @name ui.router.util.type:UrlMatcher#exec
211
- * @methodOf ui.router.util.type:UrlMatcher
212
- *
213
- * @description
214
- * Tests the specified path against this matcher, and returns an object containing the captured
215
- * parameter values, or null if the path does not match. The returned object contains the values
216
- * of any search parameters that are mentioned in the pattern, but their value may be null if
217
- * they are not present in `searchParams`. This means that search parameters are always treated
218
- * as optional.
219
- *
220
- * @example
221
- * <pre>
222
- * new UrlMatcher('/user/{id}?q&r').exec('/user/bob', {
223
- * x: '1', q: 'hello'
224
- * });
225
- * // returns { id: 'bob', q: 'hello', r: null }
226
- * </pre>
227
- *
228
- * @param {string} path The URL path to match, e.g. `$location.path()`.
229
- * @param {Object} searchParams URL search parameters, e.g. `$location.search()`.
230
- * @returns {Object} The captured parameter values.
231
- */
232
- UrlMatcher.prototype.exec = function (path, searchParams) {
233
- var m = this.regexp.exec(path);
234
- if (!m) return null;
235
- searchParams = searchParams || {};
236
-
237
- var paramNames = this.parameters(), nTotal = paramNames.length,
238
- nPath = this.segments.length - 1,
239
- values = {}, i, j, cfg, paramName;
240
-
241
- if (nPath !== m.length - 1) throw new Error("Unbalanced capture group in route '" + this.source + "'");
242
-
243
- function decodePathArray(string) {
244
- function reverseString(str) { return str.split("").reverse().join(""); }
245
- function unquoteDashes(str) { return str.replace(/\\-/, "-"); }
246
-
247
- var split = reverseString(string).split(/-(?!\\)/);
248
- var allReversed = map(split, reverseString);
249
- return map(allReversed, unquoteDashes).reverse();
250
- }
251
-
252
- for (i = 0; i < nPath; i++) {
253
- paramName = paramNames[i];
254
- var param = this.params[paramName];
255
- var paramVal = m[i+1];
256
- // if the param value matches a pre-replace pair, replace the value before decoding.
257
- for (j = 0; j < param.replace; j++) {
258
- if (param.replace[j].from === paramVal) paramVal = param.replace[j].to;
259
- }
260
- if (paramVal && param.array === true) paramVal = decodePathArray(paramVal);
261
- values[paramName] = param.value(paramVal);
262
- }
263
- for (/**/; i < nTotal; i++) {
264
- paramName = paramNames[i];
265
- values[paramName] = this.params[paramName].value(searchParams[paramName]);
266
- }
267
-
268
- return values;
269
- };
270
-
271
- /**
272
- * @ngdoc function
273
- * @name ui.router.util.type:UrlMatcher#parameters
274
- * @methodOf ui.router.util.type:UrlMatcher
275
- *
276
- * @description
277
- * Returns the names of all path and search parameters of this pattern in an unspecified order.
278
- *
279
- * @returns {Array.<string>} An array of parameter names. Must be treated as read-only. If the
280
- * pattern has no parameters, an empty array is returned.
281
- */
282
- UrlMatcher.prototype.parameters = function (param) {
283
- if (!isDefined(param)) return this.$$paramNames;
284
- return this.params[param] || null;
285
- };
286
-
287
- /**
288
- * @ngdoc function
289
- * @name ui.router.util.type:UrlMatcher#validate
290
- * @methodOf ui.router.util.type:UrlMatcher
291
- *
292
- * @description
293
- * Checks an object hash of parameters to validate their correctness according to the parameter
294
- * types of this `UrlMatcher`.
295
- *
296
- * @param {Object} params The object hash of parameters to validate.
297
- * @returns {boolean} Returns `true` if `params` validates, otherwise `false`.
298
- */
299
- UrlMatcher.prototype.validates = function (params) {
300
- return this.params.$$validates(params);
301
- };
302
-
303
- /**
304
- * @ngdoc function
305
- * @name ui.router.util.type:UrlMatcher#format
306
- * @methodOf ui.router.util.type:UrlMatcher
307
- *
308
- * @description
309
- * Creates a URL that matches this pattern by substituting the specified values
310
- * for the path and search parameters. Null values for path parameters are
311
- * treated as empty strings.
312
- *
313
- * @example
314
- * <pre>
315
- * new UrlMatcher('/user/{id}?q').format({ id:'bob', q:'yes' });
316
- * // returns '/user/bob?q=yes'
317
- * </pre>
318
- *
319
- * @param {Object} values the values to substitute for the parameters in this pattern.
320
- * @returns {string} the formatted URL (path and optionally search part).
321
- */
322
- UrlMatcher.prototype.format = function (values) {
323
- values = values || {};
324
- var segments = this.segments, params = this.parameters(), paramset = this.params;
325
- if (!this.validates(values)) return null;
326
-
327
- var i, search = false, nPath = segments.length - 1, nTotal = params.length, result = segments[0];
328
-
329
- function encodeDashes(str) { // Replace dashes with encoded "\-"
330
- return encodeURIComponent(str).replace(/-/g, function(c) { return '%5C%' + c.charCodeAt(0).toString(16).toUpperCase(); });
331
- }
332
-
333
- for (i = 0; i < nTotal; i++) {
334
- var isPathParam = i < nPath;
335
- var name = params[i], param = paramset[name], value = param.value(values[name]);
336
- var isDefaultValue = param.isOptional && param.type.equals(param.value(), value);
337
- var squash = isDefaultValue ? param.squash : false;
338
- var encoded = param.type.encode(value);
339
-
340
- if (isPathParam) {
341
- var nextSegment = segments[i + 1];
342
- if (squash === false) {
343
- if (encoded != null) {
344
- if (isArray(encoded)) {
345
- result += map(encoded, encodeDashes).join("-");
346
- } else {
347
- result += encodeURIComponent(encoded);
348
- }
349
- }
350
- result += nextSegment;
351
- } else if (squash === true) {
352
- var capture = result.match(/\/$/) ? /\/?(.*)/ : /(.*)/;
353
- result += nextSegment.match(capture)[1];
354
- } else if (isString(squash)) {
355
- result += squash + nextSegment;
356
- }
357
- } else {
358
- if (encoded == null || (isDefaultValue && squash !== false)) continue;
359
- if (!isArray(encoded)) encoded = [ encoded ];
360
- encoded = map(encoded, encodeURIComponent).join('&' + name + '=');
361
- result += (search ? '&' : '?') + (name + '=' + encoded);
362
- search = true;
363
- }
364
- }
365
-
366
- return result;
367
- };
368
-
369
- /**
370
- * @ngdoc object
371
- * @name ui.router.util.type:Type
372
- *
373
- * @description
374
- * Implements an interface to define custom parameter types that can be decoded from and encoded to
375
- * string parameters matched in a URL. Used by {@link ui.router.util.type:UrlMatcher `UrlMatcher`}
376
- * objects when matching or formatting URLs, or comparing or validating parameter values.
377
- *
378
- * See {@link ui.router.util.$urlMatcherFactory#methods_type `$urlMatcherFactory#type()`} for more
379
- * information on registering custom types.
380
- *
381
- * @param {Object} config A configuration object which contains the custom type definition. The object's
382
- * properties will override the default methods and/or pattern in `Type`'s public interface.
383
- * @example
384
- * <pre>
385
- * {
386
- * decode: function(val) { return parseInt(val, 10); },
387
- * encode: function(val) { return val && val.toString(); },
388
- * equals: function(a, b) { return this.is(a) && a === b; },
389
- * is: function(val) { return angular.isNumber(val) isFinite(val) && val % 1 === 0; },
390
- * pattern: /\d+/
391
- * }
392
- * </pre>
393
- *
394
- * @property {RegExp} pattern The regular expression pattern used to match values of this type when
395
- * coming from a substring of a URL.
396
- *
397
- * @returns {Object} Returns a new `Type` object.
398
- */
399
- function Type(config) {
400
- extend(this, config);
401
- }
402
-
403
- /**
404
- * @ngdoc function
405
- * @name ui.router.util.type:Type#is
406
- * @methodOf ui.router.util.type:Type
407
- *
408
- * @description
409
- * Detects whether a value is of a particular type. Accepts a native (decoded) value
410
- * and determines whether it matches the current `Type` object.
411
- *
412
- * @param {*} val The value to check.
413
- * @param {string} key Optional. If the type check is happening in the context of a specific
414
- * {@link ui.router.util.type:UrlMatcher `UrlMatcher`} object, this is the name of the
415
- * parameter in which `val` is stored. Can be used for meta-programming of `Type` objects.
416
- * @returns {Boolean} Returns `true` if the value matches the type, otherwise `false`.
417
- */
418
- Type.prototype.is = function(val, key) {
419
- return true;
420
- };
421
-
422
- /**
423
- * @ngdoc function
424
- * @name ui.router.util.type:Type#encode
425
- * @methodOf ui.router.util.type:Type
426
- *
427
- * @description
428
- * Encodes a custom/native type value to a string that can be embedded in a URL. Note that the
429
- * return value does *not* need to be URL-safe (i.e. passed through `encodeURIComponent()`), it
430
- * only needs to be a representation of `val` that has been coerced to a string.
431
- *
432
- * @param {*} val The value to encode.
433
- * @param {string} key The name of the parameter in which `val` is stored. Can be used for
434
- * meta-programming of `Type` objects.
435
- * @returns {string} Returns a string representation of `val` that can be encoded in a URL.
436
- */
437
- Type.prototype.encode = function(val, key) {
438
- return val;
439
- };
440
-
441
- /**
442
- * @ngdoc function
443
- * @name ui.router.util.type:Type#decode
444
- * @methodOf ui.router.util.type:Type
445
- *
446
- * @description
447
- * Converts a parameter value (from URL string or transition param) to a custom/native value.
448
- *
449
- * @param {string} val The URL parameter value to decode.
450
- * @param {string} key The name of the parameter in which `val` is stored. Can be used for
451
- * meta-programming of `Type` objects.
452
- * @returns {*} Returns a custom representation of the URL parameter value.
453
- */
454
- Type.prototype.decode = function(val, key) {
455
- return val;
456
- };
457
-
458
- /**
459
- * @ngdoc function
460
- * @name ui.router.util.type:Type#equals
461
- * @methodOf ui.router.util.type:Type
462
- *
463
- * @description
464
- * Determines whether two decoded values are equivalent.
465
- *
466
- * @param {*} a A value to compare against.
467
- * @param {*} b A value to compare against.
468
- * @returns {Boolean} Returns `true` if the values are equivalent/equal, otherwise `false`.
469
- */
470
- Type.prototype.equals = function(a, b) {
471
- return a == b;
472
- };
473
-
474
- Type.prototype.$subPattern = function() {
475
- var sub = this.pattern.toString();
476
- return sub.substr(1, sub.length - 2);
477
- };
478
-
479
- Type.prototype.pattern = /.*/;
480
-
481
- Type.prototype.toString = function() { return "{Type:" + this.name + "}"; };
482
-
483
- /*
484
- * Wraps an existing custom Type as an array of Type, depending on 'mode'.
485
- * e.g.:
486
- * - urlmatcher pattern "/path?{queryParam[]:int}"
487
- * - url: "/path?queryParam=1&queryParam=2
488
- * - $stateParams.queryParam will be [1, 2]
489
- * if `mode` is "auto", then
490
- * - url: "/path?queryParam=1 will create $stateParams.queryParam: 1
491
- * - url: "/path?queryParam=1&queryParam=2 will create $stateParams.queryParam: [1, 2]
492
- */
493
- Type.prototype.$asArray = function(mode, isSearch) {
494
- if (!mode) return this;
495
- if (mode === "auto" && !isSearch) throw new Error("'auto' array mode is for query parameters only");
496
- return new ArrayType(this, mode);
497
-
498
- function ArrayType(type, mode) {
499
- function bindTo(type, callbackName) {
500
- return function() {
501
- return type[callbackName].apply(type, arguments);
502
- };
503
- }
504
-
505
- // Wrap non-array value as array
506
- function arrayWrap(val) { return isArray(val) ? val : (isDefined(val) ? [ val ] : []); }
507
- // Unwrap array value for "auto" mode. Return undefined for empty array.
508
- function arrayUnwrap(val) {
509
- switch(val.length) {
510
- case 0: return undefined;
511
- case 1: return mode === "auto" ? val[0] : val;
512
- default: return val;
513
- }
514
- }
515
- function falsey(val) { return !val; }
516
-
517
- // Wraps type (.is/.encode/.decode) functions to operate on each value of an array
518
- function arrayHandler(callback, allTruthyMode) {
519
- return function handleArray(val) {
520
- val = arrayWrap(val);
521
- var result = map(val, callback);
522
- if (allTruthyMode === true)
523
- return filter(result, falsey).length === 0;
524
- return arrayUnwrap(result);
525
- };
526
- }
527
-
528
- // Wraps type (.equals) functions to operate on each value of an array
529
- function arrayEqualsHandler(callback) {
530
- return function handleArray(val1, val2) {
531
- var left = arrayWrap(val1), right = arrayWrap(val2);
532
- if (left.length !== right.length) return false;
533
- for (var i = 0; i < left.length; i++) {
534
- if (!callback(left[i], right[i])) return false;
535
- }
536
- return true;
537
- };
538
- }
539
-
540
- this.encode = arrayHandler(bindTo(type, 'encode'));
541
- this.decode = arrayHandler(bindTo(type, 'decode'));
542
- this.is = arrayHandler(bindTo(type, 'is'), true);
543
- this.equals = arrayEqualsHandler(bindTo(type, 'equals'));
544
- this.pattern = type.pattern;
545
- this.$arrayMode = mode;
546
- }
547
- };
548
-
549
-
550
-
551
- /**
552
- * @ngdoc object
553
- * @name ui.router.util.$urlMatcherFactory
554
- *
555
- * @description
556
- * Factory for {@link ui.router.util.type:UrlMatcher `UrlMatcher`} instances. The factory
557
- * is also available to providers under the name `$urlMatcherFactoryProvider`.
558
- */
559
- function $UrlMatcherFactory() {
560
- $$UMFP = this;
561
-
562
- var isCaseInsensitive = false, isStrictMode = true, defaultSquashPolicy = false;
563
-
564
- function valToString(val) { return val != null ? val.toString().replace(/\//g, "%2F") : val; }
565
- function valFromString(val) { return val != null ? val.toString().replace(/%2F/g, "/") : val; }
566
- // TODO: in 1.0, make string .is() return false if value is undefined by default.
567
- // function regexpMatches(val) { /*jshint validthis:true */ return isDefined(val) && this.pattern.test(val); }
568
- function regexpMatches(val) { /*jshint validthis:true */ return this.pattern.test(val); }
569
-
570
- var $types = {}, enqueue = true, typeQueue = [], injector, defaultTypes = {
571
- string: {
572
- encode: valToString,
573
- decode: valFromString,
574
- is: regexpMatches,
575
- pattern: /[^/]*/
576
- },
577
- int: {
578
- encode: valToString,
579
- decode: function(val) { return parseInt(val, 10); },
580
- is: function(val) { return isDefined(val) && this.decode(val.toString()) === val; },
581
- pattern: /\d+/
582
- },
583
- bool: {
584
- encode: function(val) { return val ? 1 : 0; },
585
- decode: function(val) { return parseInt(val, 10) !== 0; },
586
- is: function(val) { return val === true || val === false; },
587
- pattern: /0|1/
588
- },
589
- date: {
590
- encode: function (val) {
591
- if (!this.is(val))
592
- return undefined;
593
- return [ val.getFullYear(),
594
- ('0' + (val.getMonth() + 1)).slice(-2),
595
- ('0' + val.getDate()).slice(-2)
596
- ].join("-");
597
- },
598
- decode: function (val) {
599
- if (this.is(val)) return val;
600
- var match = this.capture.exec(val);
601
- return match ? new Date(match[1], match[2] - 1, match[3]) : undefined;
602
- },
603
- is: function(val) { return val instanceof Date && !isNaN(val.valueOf()); },
604
- equals: function (a, b) { return this.is(a) && this.is(b) && a.toISOString() === b.toISOString(); },
605
- pattern: /[0-9]{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[1-2][0-9]|3[0-1])/,
606
- capture: /([0-9]{4})-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])/
607
- },
608
- json: {
609
- encode: angular.toJson,
610
- decode: angular.fromJson,
611
- is: angular.isObject,
612
- equals: angular.equals,
613
- pattern: /[^/]*/
614
- },
615
- any: { // does not encode/decode
616
- encode: angular.identity,
617
- decode: angular.identity,
618
- is: angular.identity,
619
- equals: angular.equals,
620
- pattern: /.*/
621
- }
622
- };
623
-
624
- function getDefaultConfig() {
625
- return {
626
- strict: isStrictMode,
627
- caseInsensitive: isCaseInsensitive
628
- };
629
- }
630
-
631
- function isInjectable(value) {
632
- return (isFunction(value) || (isArray(value) && isFunction(value[value.length - 1])));
633
- }
634
-
635
- /**
636
- * [Internal] Get the default value of a parameter, which may be an injectable function.
637
- */
638
- $UrlMatcherFactory.$$getDefaultValue = function(config) {
639
- if (!isInjectable(config.value)) return config.value;
640
- if (!injector) throw new Error("Injectable functions cannot be called at configuration time");
641
- return injector.invoke(config.value);
642
- };
643
-
644
- /**
645
- * @ngdoc function
646
- * @name ui.router.util.$urlMatcherFactory#caseInsensitive
647
- * @methodOf ui.router.util.$urlMatcherFactory
648
- *
649
- * @description
650
- * Defines whether URL matching should be case sensitive (the default behavior), or not.
651
- *
652
- * @param {boolean} value `false` to match URL in a case sensitive manner; otherwise `true`;
653
- * @returns {boolean} the current value of caseInsensitive
654
- */
655
- this.caseInsensitive = function(value) {
656
- if (isDefined(value))
657
- isCaseInsensitive = value;
658
- return isCaseInsensitive;
659
- };
660
-
661
- /**
662
- * @ngdoc function
663
- * @name ui.router.util.$urlMatcherFactory#strictMode
664
- * @methodOf ui.router.util.$urlMatcherFactory
665
- *
666
- * @description
667
- * Defines whether URLs should match trailing slashes, or not (the default behavior).
668
- *
669
- * @param {boolean=} value `false` to match trailing slashes in URLs, otherwise `true`.
670
- * @returns {boolean} the current value of strictMode
671
- */
672
- this.strictMode = function(value) {
673
- if (isDefined(value))
674
- isStrictMode = value;
675
- return isStrictMode;
676
- };
677
-
678
- /**
679
- * @ngdoc function
680
- * @name ui.router.util.$urlMatcherFactory#defaultSquashPolicy
681
- * @methodOf ui.router.util.$urlMatcherFactory
682
- *
683
- * @description
684
- * Sets the default behavior when generating or matching URLs with default parameter values.
685
- *
686
- * @param {string} value A string that defines the default parameter URL squashing behavior.
687
- * `nosquash`: When generating an href with a default parameter value, do not squash the parameter value from the URL
688
- * `slash`: When generating an href with a default parameter value, squash (remove) the parameter value, and, if the
689
- * parameter is surrounded by slashes, squash (remove) one slash from the URL
690
- * any other string, e.g. "~": When generating an href with a default parameter value, squash (remove)
691
- * the parameter value from the URL and replace it with this string.
692
- */
693
- this.defaultSquashPolicy = function(value) {
694
- if (!isDefined(value)) return defaultSquashPolicy;
695
- if (value !== true && value !== false && !isString(value))
696
- throw new Error("Invalid squash policy: " + value + ". Valid policies: false, true, arbitrary-string");
697
- defaultSquashPolicy = value;
698
- return value;
699
- };
700
-
701
- /**
702
- * @ngdoc function
703
- * @name ui.router.util.$urlMatcherFactory#compile
704
- * @methodOf ui.router.util.$urlMatcherFactory
705
- *
706
- * @description
707
- * Creates a {@link ui.router.util.type:UrlMatcher `UrlMatcher`} for the specified pattern.
708
- *
709
- * @param {string} pattern The URL pattern.
710
- * @param {Object} config The config object hash.
711
- * @returns {UrlMatcher} The UrlMatcher.
712
- */
713
- this.compile = function (pattern, config) {
714
- return new UrlMatcher(pattern, extend(getDefaultConfig(), config));
715
- };
716
-
717
- /**
718
- * @ngdoc function
719
- * @name ui.router.util.$urlMatcherFactory#isMatcher
720
- * @methodOf ui.router.util.$urlMatcherFactory
721
- *
722
- * @description
723
- * Returns true if the specified object is a `UrlMatcher`, or false otherwise.
724
- *
725
- * @param {Object} object The object to perform the type check against.
726
- * @returns {Boolean} Returns `true` if the object matches the `UrlMatcher` interface, by
727
- * implementing all the same methods.
728
- */
729
- this.isMatcher = function (o) {
730
- if (!isObject(o)) return false;
731
- var result = true;
732
-
733
- forEach(UrlMatcher.prototype, function(val, name) {
734
- if (isFunction(val)) {
735
- result = result && (isDefined(o[name]) && isFunction(o[name]));
736
- }
737
- });
738
- return result;
739
- };
740
-
741
- /**
742
- * @ngdoc function
743
- * @name ui.router.util.$urlMatcherFactory#type
744
- * @methodOf ui.router.util.$urlMatcherFactory
745
- *
746
- * @description
747
- * Registers a custom {@link ui.router.util.type:Type `Type`} object that can be used to
748
- * generate URLs with typed parameters.
749
- *
750
- * @param {string} name The type name.
751
- * @param {Object|Function} definition The type definition. See
752
- * {@link ui.router.util.type:Type `Type`} for information on the values accepted.
753
- * @param {Object|Function} definitionFn (optional) A function that is injected before the app
754
- * runtime starts. The result of this function is merged into the existing `definition`.
755
- * See {@link ui.router.util.type:Type `Type`} for information on the values accepted.
756
- *
757
- * @returns {Object} Returns `$urlMatcherFactoryProvider`.
758
- *
759
- * @example
760
- * This is a simple example of a custom type that encodes and decodes items from an
761
- * array, using the array index as the URL-encoded value:
762
- *
763
- * <pre>
764
- * var list = ['John', 'Paul', 'George', 'Ringo'];
765
- *
766
- * $urlMatcherFactoryProvider.type('listItem', {
767
- * encode: function(item) {
768
- * // Represent the list item in the URL using its corresponding index
769
- * return list.indexOf(item);
770
- * },
771
- * decode: function(item) {
772
- * // Look up the list item by index
773
- * return list[parseInt(item, 10)];
774
- * },
775
- * is: function(item) {
776
- * // Ensure the item is valid by checking to see that it appears
777
- * // in the list
778
- * return list.indexOf(item) > -1;
779
- * }
780
- * });
781
- *
782
- * $stateProvider.state('list', {
783
- * url: "/list/{item:listItem}",
784
- * controller: function($scope, $stateParams) {
785
- * console.log($stateParams.item);
786
- * }
787
- * });
788
- *
789
- * // ...
790
- *
791
- * // Changes URL to '/list/3', logs "Ringo" to the console
792
- * $state.go('list', { item: "Ringo" });
793
- * </pre>
794
- *
795
- * This is a more complex example of a type that relies on dependency injection to
796
- * interact with services, and uses the parameter name from the URL to infer how to
797
- * handle encoding and decoding parameter values:
798
- *
799
- * <pre>
800
- * // Defines a custom type that gets a value from a service,
801
- * // where each service gets different types of values from
802
- * // a backend API:
803
- * $urlMatcherFactoryProvider.type('dbObject', {}, function(Users, Posts) {
804
- *
805
- * // Matches up services to URL parameter names
806
- * var services = {
807
- * user: Users,
808
- * post: Posts
809
- * };
810
- *
811
- * return {
812
- * encode: function(object) {
813
- * // Represent the object in the URL using its unique ID
814
- * return object.id;
815
- * },
816
- * decode: function(value, key) {
817
- * // Look up the object by ID, using the parameter
818
- * // name (key) to call the correct service
819
- * return services[key].findById(value);
820
- * },
821
- * is: function(object, key) {
822
- * // Check that object is a valid dbObject
823
- * return angular.isObject(object) && object.id && services[key];
824
- * }
825
- * equals: function(a, b) {
826
- * // Check the equality of decoded objects by comparing
827
- * // their unique IDs
828
- * return a.id === b.id;
829
- * }
830
- * };
831
- * });
832
- *
833
- * // In a config() block, you can then attach URLs with
834
- * // type-annotated parameters:
835
- * $stateProvider.state('users', {
836
- * url: "/users",
837
- * // ...
838
- * }).state('users.item', {
839
- * url: "/{user:dbObject}",
840
- * controller: function($scope, $stateParams) {
841
- * // $stateParams.user will now be an object returned from
842
- * // the Users service
843
- * },
844
- * // ...
845
- * });
846
- * </pre>
847
- */
848
- this.type = function (name, definition, definitionFn) {
849
- if (!isDefined(definition)) return $types[name];
850
- if ($types.hasOwnProperty(name)) throw new Error("A type named '" + name + "' has already been defined.");
851
-
852
- $types[name] = new Type(extend({ name: name }, definition));
853
- if (definitionFn) {
854
- typeQueue.push({ name: name, def: definitionFn });
855
- if (!enqueue) flushTypeQueue();
856
- }
857
- return this;
858
- };
859
-
860
- // `flushTypeQueue()` waits until `$urlMatcherFactory` is injected before invoking the queued `definitionFn`s
861
- function flushTypeQueue() {
862
- while(typeQueue.length) {
863
- var type = typeQueue.shift();
864
- if (type.pattern) throw new Error("You cannot override a type's .pattern at runtime.");
865
- angular.extend($types[type.name], injector.invoke(type.def));
866
- }
867
- }
868
-
869
- // Register default types. Store them in the prototype of $types.
870
- forEach(defaultTypes, function(type, name) { $types[name] = new Type(extend({name: name}, type)); });
871
- $types = inherit($types, {});
872
-
873
- /* No need to document $get, since it returns this */
874
- this.$get = ['$injector', function ($injector) {
875
- injector = $injector;
876
- enqueue = false;
877
- flushTypeQueue();
878
-
879
- forEach(defaultTypes, function(type, name) {
880
- if (!$types[name]) $types[name] = new Type(type);
881
- });
882
- return this;
883
- }];
884
-
885
- this.Param = function Param(id, type, config, location) {
886
- var self = this;
887
- config = unwrapShorthand(config);
888
- type = getType(config, type, location);
889
- var arrayMode = getArrayMode();
890
- type = arrayMode ? type.$asArray(arrayMode, location === "search") : type;
891
- if (type.name === "string" && !arrayMode && location === "path" && config.value === undefined)
892
- config.value = ""; // for 0.2.x; in 0.3.0+ do not automatically default to ""
893
- var isOptional = config.value !== undefined;
894
- var squash = getSquashPolicy(config, isOptional);
895
- var replace = getReplace(config, arrayMode, isOptional, squash);
896
-
897
- function unwrapShorthand(config) {
898
- var keys = isObject(config) ? objectKeys(config) : [];
899
- var isShorthand = indexOf(keys, "value") === -1 && indexOf(keys, "type") === -1 &&
900
- indexOf(keys, "squash") === -1 && indexOf(keys, "array") === -1;
901
- if (isShorthand) config = { value: config };
902
- config.$$fn = isInjectable(config.value) ? config.value : function () { return config.value; };
903
- return config;
904
- }
905
-
906
- function getType(config, urlType, location) {
907
- if (config.type && urlType) throw new Error("Param '"+id+"' has two type configurations.");
908
- if (urlType) return urlType;
909
- if (!config.type) return (location === "config" ? $types.any : $types.string);
910
- return config.type instanceof Type ? config.type : new Type(config.type);
911
- }
912
-
913
- // array config: param name (param[]) overrides default settings. explicit config overrides param name.
914
- function getArrayMode() {
915
- var arrayDefaults = { array: (location === "search" ? "auto" : false) };
916
- var arrayParamNomenclature = id.match(/\[\]$/) ? { array: true } : {};
917
- return extend(arrayDefaults, arrayParamNomenclature, config).array;
918
- }
919
-
920
- /**
921
- * returns false, true, or the squash value to indicate the "default parameter url squash policy".
922
- */
923
- function getSquashPolicy(config, isOptional) {
924
- var squash = config.squash;
925
- if (!isOptional || squash === false) return false;
926
- if (!isDefined(squash) || squash == null) return defaultSquashPolicy;
927
- if (squash === true || isString(squash)) return squash;
928
- throw new Error("Invalid squash policy: '" + squash + "'. Valid policies: false, true, or arbitrary string");
929
- }
930
-
931
- function getReplace(config, arrayMode, isOptional, squash) {
932
- var replace, configuredKeys, defaultPolicy = [
933
- { from: "", to: (isOptional || arrayMode ? undefined : "") },
934
- { from: null, to: (isOptional || arrayMode ? undefined : "") }
935
- ];
936
- replace = isArray(config.replace) ? config.replace : [];
937
- if (isString(squash))
938
- replace.push({ from: squash, to: undefined });
939
- configuredKeys = map(replace, function(item) { return item.from; } );
940
- return filter(defaultPolicy, function(item) { return indexOf(configuredKeys, item.from) === -1; }).concat(replace);
941
- }
942
-
943
- /**
944
- * [Internal] Get the default value of a parameter, which may be an injectable function.
945
- */
946
- function $$getDefaultValue() {
947
- if (!injector) throw new Error("Injectable functions cannot be called at configuration time");
948
- return injector.invoke(config.$$fn);
949
- }
950
-
951
- /**
952
- * [Internal] Gets the decoded representation of a value if the value is defined, otherwise, returns the
953
- * default value, which may be the result of an injectable function.
954
- */
955
- function $value(value) {
956
- function hasReplaceVal(val) { return function(obj) { return obj.from === val; }; }
957
- function $replace(value) {
958
- var replacement = map(filter(self.replace, hasReplaceVal(value)), function(obj) { return obj.to; });
959
- return replacement.length ? replacement[0] : value;
960
- }
961
- value = $replace(value);
962
- return isDefined(value) ? self.type.decode(value) : $$getDefaultValue();
963
- }
964
-
965
- function toString() { return "{Param:" + id + " " + type + " squash: '" + squash + "' optional: " + isOptional + "}"; }
966
-
967
- extend(this, {
968
- id: id,
969
- type: type,
970
- location: location,
971
- array: arrayMode,
972
- squash: squash,
973
- replace: replace,
974
- isOptional: isOptional,
975
- value: $value,
976
- dynamic: undefined,
977
- config: config,
978
- toString: toString
979
- });
980
- };
981
-
982
- function ParamSet(params) {
983
- extend(this, params || {});
984
- }
985
-
986
- ParamSet.prototype = {
987
- $$new: function() {
988
- return inherit(this, extend(new ParamSet(), { $$parent: this}));
989
- },
990
- $$keys: function () {
991
- var keys = [], chain = [], parent = this,
992
- ignore = objectKeys(ParamSet.prototype);
993
- while (parent) { chain.push(parent); parent = parent.$$parent; }
994
- chain.reverse();
995
- forEach(chain, function(paramset) {
996
- forEach(objectKeys(paramset), function(key) {
997
- if (indexOf(keys, key) === -1 && indexOf(ignore, key) === -1) keys.push(key);
998
- });
999
- });
1000
- return keys;
1001
- },
1002
- $$values: function(paramValues) {
1003
- var values = {}, self = this;
1004
- forEach(self.$$keys(), function(key) {
1005
- values[key] = self[key].value(paramValues && paramValues[key]);
1006
- });
1007
- return values;
1008
- },
1009
- $$equals: function(paramValues1, paramValues2) {
1010
- var equal = true, self = this;
1011
- forEach(self.$$keys(), function(key) {
1012
- var left = paramValues1 && paramValues1[key], right = paramValues2 && paramValues2[key];
1013
- if (!self[key].type.equals(left, right)) equal = false;
1014
- });
1015
- return equal;
1016
- },
1017
- $$validates: function $$validate(paramValues) {
1018
- var result = true, isOptional, val, param, self = this;
1019
-
1020
- forEach(this.$$keys(), function(key) {
1021
- param = self[key];
1022
- val = paramValues[key];
1023
- isOptional = !val && param.isOptional;
1024
- result = result && (isOptional || !!param.type.is(val));
1025
- });
1026
- return result;
1027
- },
1028
- $$parent: undefined
1029
- };
1030
-
1031
- this.ParamSet = ParamSet;
1032
- }
1033
-
1034
- // Register as a provider so it's available to other providers
1035
- angular.module('ui.router.util').provider('$urlMatcherFactory', $UrlMatcherFactory);
1036
- angular.module('ui.router.util').run(['$urlMatcherFactory', function($urlMatcherFactory) { }]);