praxis 0.19.0 → 0.20.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (266) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +5 -0
  3. data/CHANGELOG.md +16 -0
  4. data/lib/api_browser/Gruntfile.js +125 -16
  5. data/lib/api_browser/app/index.html +5 -1
  6. data/lib/api_browser/app/js/directives/fixed_if_fits.js +28 -17
  7. data/lib/api_browser/app/js/directives/highlight.js +14 -0
  8. data/lib/api_browser/app/js/directives/request_examples.js +29 -0
  9. data/lib/api_browser/app/js/factories/Documentation.js +6 -0
  10. data/lib/api_browser/app/js/factories/Example.js +47 -0
  11. data/lib/api_browser/app/js/factories/prepare_template.js +15 -0
  12. data/lib/api_browser/app/js/factories/template_for.js +2 -12
  13. data/lib/api_browser/app/sass/modules/_sidebar.scss +2 -0
  14. data/lib/api_browser/app/views/action.html +6 -24
  15. data/lib/api_browser/app/views/examples/general.html +26 -0
  16. data/lib/api_browser/{bower.json → bower_template.json} +13 -3
  17. data/lib/api_browser/package.json +3 -1
  18. data/lib/praxis/application.rb +2 -0
  19. data/lib/praxis/docs/generator.rb +4 -2
  20. data/lib/praxis/extensions/field_selection/field_selector.rb +1 -0
  21. data/lib/praxis/media_type.rb +1 -1
  22. data/lib/praxis/plugin.rb +4 -0
  23. data/lib/praxis/plugin_concern.rb +2 -1
  24. data/lib/praxis/tasks/api_docs.rb +9 -2
  25. data/lib/praxis/version.rb +1 -1
  26. data/praxis.gemspec +2 -2
  27. data/spec/praxis/action_definition_spec.rb +26 -1
  28. data/spec/praxis/extensions/field_selection/field_selector_spec.rb +4 -0
  29. data/spec/praxis/media_type_spec.rb +4 -3
  30. data/spec/support/spec_media_types.rb +5 -1
  31. metadata +12 -242
  32. data/lib/api_browser/app/bower_components/angular-mocks/.bower.json +0 -19
  33. data/lib/api_browser/app/bower_components/angular-mocks/README.md +0 -63
  34. data/lib/api_browser/app/bower_components/angular-mocks/angular-mocks.js +0 -2452
  35. data/lib/api_browser/app/bower_components/angular-mocks/bower.json +0 -9
  36. data/lib/api_browser/app/bower_components/angular-mocks/ngAnimateMock.js +0 -2
  37. data/lib/api_browser/app/bower_components/angular-mocks/ngMock.js +0 -2
  38. data/lib/api_browser/app/bower_components/angular-mocks/ngMockE2E.js +0 -2
  39. data/lib/api_browser/app/bower_components/angular-mocks/package.json +0 -27
  40. data/lib/api_browser/app/bower_components/angular-sanitize/.bower.json +0 -19
  41. data/lib/api_browser/app/bower_components/angular-sanitize/README.md +0 -68
  42. data/lib/api_browser/app/bower_components/angular-sanitize/angular-sanitize.js +0 -683
  43. data/lib/api_browser/app/bower_components/angular-sanitize/angular-sanitize.min.js +0 -16
  44. data/lib/api_browser/app/bower_components/angular-sanitize/angular-sanitize.min.js.map +0 -8
  45. data/lib/api_browser/app/bower_components/angular-sanitize/bower.json +0 -9
  46. data/lib/api_browser/app/bower_components/angular-sanitize/index.js +0 -2
  47. data/lib/api_browser/app/bower_components/angular-sanitize/package.json +0 -26
  48. data/lib/api_browser/app/bower_components/angular-ui-bootstrap-bower/.bower.json +0 -31
  49. data/lib/api_browser/app/bower_components/angular-ui-bootstrap-bower/bower.json +0 -19
  50. data/lib/api_browser/app/bower_components/angular-ui-bootstrap-bower/ui-bootstrap-csp.css +0 -6
  51. data/lib/api_browser/app/bower_components/angular-ui-bootstrap-bower/ui-bootstrap-tpls.js +0 -4840
  52. data/lib/api_browser/app/bower_components/angular-ui-bootstrap-bower/ui-bootstrap-tpls.min.js +0 -10
  53. data/lib/api_browser/app/bower_components/angular-ui-bootstrap-bower/ui-bootstrap.js +0 -4461
  54. data/lib/api_browser/app/bower_components/angular-ui-bootstrap-bower/ui-bootstrap.min.js +0 -9
  55. data/lib/api_browser/app/bower_components/angular-ui-router/.bower.json +0 -33
  56. data/lib/api_browser/app/bower_components/angular-ui-router/CHANGELOG.md +0 -228
  57. data/lib/api_browser/app/bower_components/angular-ui-router/CONTRIBUTING.md +0 -65
  58. data/lib/api_browser/app/bower_components/angular-ui-router/LICENSE +0 -21
  59. data/lib/api_browser/app/bower_components/angular-ui-router/README.md +0 -245
  60. data/lib/api_browser/app/bower_components/angular-ui-router/api/angular-ui-router.d.ts +0 -126
  61. data/lib/api_browser/app/bower_components/angular-ui-router/bower.json +0 -23
  62. data/lib/api_browser/app/bower_components/angular-ui-router/release/angular-ui-router.js +0 -4370
  63. data/lib/api_browser/app/bower_components/angular-ui-router/release/angular-ui-router.min.js +0 -7
  64. data/lib/api_browser/app/bower_components/angular-ui-router/src/common.js +0 -292
  65. data/lib/api_browser/app/bower_components/angular-ui-router/src/resolve.js +0 -252
  66. data/lib/api_browser/app/bower_components/angular-ui-router/src/state.js +0 -1465
  67. data/lib/api_browser/app/bower_components/angular-ui-router/src/stateDirectives.js +0 -285
  68. data/lib/api_browser/app/bower_components/angular-ui-router/src/stateFilters.js +0 -39
  69. data/lib/api_browser/app/bower_components/angular-ui-router/src/templateFactory.js +0 -110
  70. data/lib/api_browser/app/bower_components/angular-ui-router/src/urlMatcherFactory.js +0 -1050
  71. data/lib/api_browser/app/bower_components/angular-ui-router/src/urlRouter.js +0 -427
  72. data/lib/api_browser/app/bower_components/angular-ui-router/src/view.js +0 -71
  73. data/lib/api_browser/app/bower_components/angular-ui-router/src/viewDirective.js +0 -303
  74. data/lib/api_browser/app/bower_components/angular-ui-router/src/viewScroll.js +0 -52
  75. data/lib/api_browser/app/bower_components/angular/.bower.json +0 -17
  76. data/lib/api_browser/app/bower_components/angular/README.md +0 -64
  77. data/lib/api_browser/app/bower_components/angular/angular-csp.css +0 -21
  78. data/lib/api_browser/app/bower_components/angular/angular.js +0 -28133
  79. data/lib/api_browser/app/bower_components/angular/angular.min.js +0 -289
  80. data/lib/api_browser/app/bower_components/angular/angular.min.js.gzip +0 -0
  81. data/lib/api_browser/app/bower_components/angular/angular.min.js.map +0 -8
  82. data/lib/api_browser/app/bower_components/angular/bower.json +0 -8
  83. data/lib/api_browser/app/bower_components/angular/index.js +0 -2
  84. data/lib/api_browser/app/bower_components/angular/package.json +0 -25
  85. data/lib/api_browser/app/bower_components/bootstrap-sass/.bower.json +0 -41
  86. data/lib/api_browser/app/bower_components/bootstrap-sass/CHANGELOG.md +0 -108
  87. data/lib/api_browser/app/bower_components/bootstrap-sass/CONTRIBUTING.md +0 -79
  88. data/lib/api_browser/app/bower_components/bootstrap-sass/README.md +0 -218
  89. data/lib/api_browser/app/bower_components/bootstrap-sass/bower.json +0 -22
  90. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/fonts/bootstrap/glyphicons-halflings-regular.eot +0 -0
  91. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/fonts/bootstrap/glyphicons-halflings-regular.svg +0 -229
  92. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/fonts/bootstrap/glyphicons-halflings-regular.ttf +0 -0
  93. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/fonts/bootstrap/glyphicons-halflings-regular.woff +0 -0
  94. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/javascripts/bootstrap.js +0 -12
  95. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/javascripts/bootstrap/affix.js +0 -126
  96. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/javascripts/bootstrap/alert.js +0 -98
  97. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/javascripts/bootstrap/button.js +0 -115
  98. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/javascripts/bootstrap/carousel.js +0 -217
  99. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/javascripts/bootstrap/collapse.js +0 -179
  100. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/javascripts/bootstrap/dropdown.js +0 -154
  101. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/javascripts/bootstrap/modal.js +0 -246
  102. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/javascripts/bootstrap/popover.js +0 -117
  103. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/javascripts/bootstrap/scrollspy.js +0 -158
  104. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/javascripts/bootstrap/tab.js +0 -135
  105. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/javascripts/bootstrap/tooltip.js +0 -386
  106. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/javascripts/bootstrap/transition.js +0 -56
  107. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap.scss +0 -1
  108. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap/_alerts.scss +0 -67
  109. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap/_badges.scss +0 -51
  110. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap/_breadcrumbs.scss +0 -23
  111. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap/_button-groups.scss +0 -227
  112. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap/_buttons.scss +0 -155
  113. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap/_carousel.scss +0 -232
  114. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap/_close.scss +0 -35
  115. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap/_code.scss +0 -53
  116. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap/_component-animations.scss +0 -29
  117. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap/_dropdowns.scss +0 -188
  118. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap/_forms.scss +0 -374
  119. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap/_glyphicons.scss +0 -237
  120. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap/_grid.scss +0 -79
  121. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap/_input-groups.scss +0 -136
  122. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap/_jumbotron.scss +0 -46
  123. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap/_labels.scss +0 -64
  124. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap/_list-group.scss +0 -88
  125. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap/_media.scss +0 -56
  126. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap/_mixins.scss +0 -848
  127. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap/_modals.scss +0 -129
  128. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap/_navbar.scss +0 -616
  129. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap/_navs.scss +0 -242
  130. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap/_normalize.scss +0 -406
  131. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap/_pager.scss +0 -55
  132. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap/_pagination.scss +0 -85
  133. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap/_panels.scss +0 -182
  134. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap/_popovers.scss +0 -133
  135. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap/_print.scss +0 -105
  136. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap/_progress-bars.scss +0 -80
  137. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap/_responsive-utilities.scss +0 -198
  138. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap/_scaffolding.scss +0 -119
  139. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap/_tables.scss +0 -231
  140. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap/_theme.scss +0 -247
  141. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap/_thumbnails.scss +0 -38
  142. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap/_tooltip.scss +0 -95
  143. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap/_type.scss +0 -281
  144. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap/_utilities.scss +0 -56
  145. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap/_variables.scss +0 -646
  146. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap/_wells.scss +0 -29
  147. data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap/bootstrap.scss +0 -49
  148. data/lib/api_browser/app/bower_components/jquery/.bower.json +0 -38
  149. data/lib/api_browser/app/bower_components/jquery/MIT-LICENSE.txt +0 -21
  150. data/lib/api_browser/app/bower_components/jquery/bower.json +0 -27
  151. data/lib/api_browser/app/bower_components/jquery/dist/jquery.js +0 -9190
  152. data/lib/api_browser/app/bower_components/jquery/dist/jquery.min.js +0 -5
  153. data/lib/api_browser/app/bower_components/jquery/dist/jquery.min.map +0 -1
  154. data/lib/api_browser/app/bower_components/jquery/src/ajax.js +0 -806
  155. data/lib/api_browser/app/bower_components/jquery/src/ajax/jsonp.js +0 -89
  156. data/lib/api_browser/app/bower_components/jquery/src/ajax/load.js +0 -75
  157. data/lib/api_browser/app/bower_components/jquery/src/ajax/parseJSON.js +0 -13
  158. data/lib/api_browser/app/bower_components/jquery/src/ajax/parseXML.js +0 -28
  159. data/lib/api_browser/app/bower_components/jquery/src/ajax/script.js +0 -64
  160. data/lib/api_browser/app/bower_components/jquery/src/ajax/var/nonce.js +0 -5
  161. data/lib/api_browser/app/bower_components/jquery/src/ajax/var/rquery.js +0 -3
  162. data/lib/api_browser/app/bower_components/jquery/src/ajax/xhr.js +0 -135
  163. data/lib/api_browser/app/bower_components/jquery/src/attributes.js +0 -11
  164. data/lib/api_browser/app/bower_components/jquery/src/attributes/attr.js +0 -143
  165. data/lib/api_browser/app/bower_components/jquery/src/attributes/classes.js +0 -158
  166. data/lib/api_browser/app/bower_components/jquery/src/attributes/prop.js +0 -96
  167. data/lib/api_browser/app/bower_components/jquery/src/attributes/support.js +0 -35
  168. data/lib/api_browser/app/bower_components/jquery/src/attributes/val.js +0 -163
  169. data/lib/api_browser/app/bower_components/jquery/src/callbacks.js +0 -205
  170. data/lib/api_browser/app/bower_components/jquery/src/core.js +0 -498
  171. data/lib/api_browser/app/bower_components/jquery/src/core/access.js +0 -60
  172. data/lib/api_browser/app/bower_components/jquery/src/core/init.js +0 -123
  173. data/lib/api_browser/app/bower_components/jquery/src/core/parseHTML.js +0 -39
  174. data/lib/api_browser/app/bower_components/jquery/src/core/ready.js +0 -97
  175. data/lib/api_browser/app/bower_components/jquery/src/core/var/rsingleTag.js +0 -4
  176. data/lib/api_browser/app/bower_components/jquery/src/css.js +0 -451
  177. data/lib/api_browser/app/bower_components/jquery/src/css/addGetHookIf.js +0 -24
  178. data/lib/api_browser/app/bower_components/jquery/src/css/curCSS.js +0 -57
  179. data/lib/api_browser/app/bower_components/jquery/src/css/defaultDisplay.js +0 -70
  180. data/lib/api_browser/app/bower_components/jquery/src/css/hiddenVisibleSelectors.js +0 -15
  181. data/lib/api_browser/app/bower_components/jquery/src/css/support.js +0 -91
  182. data/lib/api_browser/app/bower_components/jquery/src/css/swap.js +0 -28
  183. data/lib/api_browser/app/bower_components/jquery/src/css/var/cssExpand.js +0 -3
  184. data/lib/api_browser/app/bower_components/jquery/src/css/var/getStyles.js +0 -5
  185. data/lib/api_browser/app/bower_components/jquery/src/css/var/isHidden.js +0 -13
  186. data/lib/api_browser/app/bower_components/jquery/src/css/var/rmargin.js +0 -3
  187. data/lib/api_browser/app/bower_components/jquery/src/css/var/rnumnonpx.js +0 -5
  188. data/lib/api_browser/app/bower_components/jquery/src/data.js +0 -179
  189. data/lib/api_browser/app/bower_components/jquery/src/data/Data.js +0 -181
  190. data/lib/api_browser/app/bower_components/jquery/src/data/accepts.js +0 -20
  191. data/lib/api_browser/app/bower_components/jquery/src/data/var/data_priv.js +0 -5
  192. data/lib/api_browser/app/bower_components/jquery/src/data/var/data_user.js +0 -5
  193. data/lib/api_browser/app/bower_components/jquery/src/deferred.js +0 -149
  194. data/lib/api_browser/app/bower_components/jquery/src/deprecated.js +0 -13
  195. data/lib/api_browser/app/bower_components/jquery/src/dimensions.js +0 -50
  196. data/lib/api_browser/app/bower_components/jquery/src/effects.js +0 -649
  197. data/lib/api_browser/app/bower_components/jquery/src/effects/Tween.js +0 -114
  198. data/lib/api_browser/app/bower_components/jquery/src/effects/animatedSelector.js +0 -13
  199. data/lib/api_browser/app/bower_components/jquery/src/event.js +0 -868
  200. data/lib/api_browser/app/bower_components/jquery/src/event/alias.js +0 -39
  201. data/lib/api_browser/app/bower_components/jquery/src/event/support.js +0 -9
  202. data/lib/api_browser/app/bower_components/jquery/src/exports/amd.js +0 -24
  203. data/lib/api_browser/app/bower_components/jquery/src/exports/global.js +0 -32
  204. data/lib/api_browser/app/bower_components/jquery/src/intro.js +0 -44
  205. data/lib/api_browser/app/bower_components/jquery/src/jquery.js +0 -36
  206. data/lib/api_browser/app/bower_components/jquery/src/manipulation.js +0 -582
  207. data/lib/api_browser/app/bower_components/jquery/src/manipulation/_evalUrl.js +0 -18
  208. data/lib/api_browser/app/bower_components/jquery/src/manipulation/support.js +0 -31
  209. data/lib/api_browser/app/bower_components/jquery/src/manipulation/var/rcheckableType.js +0 -3
  210. data/lib/api_browser/app/bower_components/jquery/src/offset.js +0 -204
  211. data/lib/api_browser/app/bower_components/jquery/src/outro.js +0 -1
  212. data/lib/api_browser/app/bower_components/jquery/src/queue.js +0 -142
  213. data/lib/api_browser/app/bower_components/jquery/src/queue/delay.js +0 -22
  214. data/lib/api_browser/app/bower_components/jquery/src/selector-native.js +0 -172
  215. data/lib/api_browser/app/bower_components/jquery/src/selector-sizzle.js +0 -14
  216. data/lib/api_browser/app/bower_components/jquery/src/selector.js +0 -1
  217. data/lib/api_browser/app/bower_components/jquery/src/serialize.js +0 -111
  218. data/lib/api_browser/app/bower_components/jquery/src/sizzle/dist/sizzle.js +0 -2044
  219. data/lib/api_browser/app/bower_components/jquery/src/sizzle/dist/sizzle.min.js +0 -3
  220. data/lib/api_browser/app/bower_components/jquery/src/sizzle/dist/sizzle.min.map +0 -1
  221. data/lib/api_browser/app/bower_components/jquery/src/traversing.js +0 -200
  222. data/lib/api_browser/app/bower_components/jquery/src/traversing/findFilter.js +0 -100
  223. data/lib/api_browser/app/bower_components/jquery/src/traversing/var/rneedsContext.js +0 -6
  224. data/lib/api_browser/app/bower_components/jquery/src/var/arr.js +0 -3
  225. data/lib/api_browser/app/bower_components/jquery/src/var/class2type.js +0 -4
  226. data/lib/api_browser/app/bower_components/jquery/src/var/concat.js +0 -5
  227. data/lib/api_browser/app/bower_components/jquery/src/var/hasOwn.js +0 -5
  228. data/lib/api_browser/app/bower_components/jquery/src/var/indexOf.js +0 -5
  229. data/lib/api_browser/app/bower_components/jquery/src/var/pnum.js +0 -3
  230. data/lib/api_browser/app/bower_components/jquery/src/var/push.js +0 -5
  231. data/lib/api_browser/app/bower_components/jquery/src/var/rnotwhite.js +0 -3
  232. data/lib/api_browser/app/bower_components/jquery/src/var/slice.js +0 -5
  233. data/lib/api_browser/app/bower_components/jquery/src/var/strundefined.js +0 -3
  234. data/lib/api_browser/app/bower_components/jquery/src/var/support.js +0 -4
  235. data/lib/api_browser/app/bower_components/jquery/src/var/toString.js +0 -5
  236. data/lib/api_browser/app/bower_components/jquery/src/wrap.js +0 -78
  237. data/lib/api_browser/app/bower_components/lodash/.bower.json +0 -30
  238. data/lib/api_browser/app/bower_components/lodash/LICENSE.txt +0 -22
  239. data/lib/api_browser/app/bower_components/lodash/bower.json +0 -20
  240. data/lib/api_browser/app/bower_components/lodash/lodash.js +0 -12235
  241. data/lib/api_browser/app/bower_components/lodash/lodash.min.js +0 -98
  242. data/lib/api_browser/app/bower_components/showdown/.bower.json +0 -39
  243. data/lib/api_browser/app/bower_components/showdown/.jshintignore +0 -2
  244. data/lib/api_browser/app/bower_components/showdown/.travis.yml +0 -8
  245. data/lib/api_browser/app/bower_components/showdown/Gruntfile.js +0 -100
  246. data/lib/api_browser/app/bower_components/showdown/README.md +0 -317
  247. data/lib/api_browser/app/bower_components/showdown/bower.json +0 -26
  248. data/lib/api_browser/app/bower_components/showdown/compressed/Showdown.js +0 -1606
  249. data/lib/api_browser/app/bower_components/showdown/compressed/Showdown.js.map +0 -1
  250. data/lib/api_browser/app/bower_components/showdown/compressed/Showdown.min.js +0 -2
  251. data/lib/api_browser/app/bower_components/showdown/compressed/extensions/github.min.js +0 -2
  252. data/lib/api_browser/app/bower_components/showdown/compressed/extensions/github.min.js.map +0 -1
  253. data/lib/api_browser/app/bower_components/showdown/compressed/extensions/prettify.min.js +0 -2
  254. data/lib/api_browser/app/bower_components/showdown/compressed/extensions/prettify.min.js.map +0 -1
  255. data/lib/api_browser/app/bower_components/showdown/compressed/extensions/table.min.js +0 -2
  256. data/lib/api_browser/app/bower_components/showdown/compressed/extensions/table.min.js.map +0 -1
  257. data/lib/api_browser/app/bower_components/showdown/compressed/extensions/twitter.min.js +0 -2
  258. data/lib/api_browser/app/bower_components/showdown/compressed/extensions/twitter.min.js.map +0 -1
  259. data/lib/api_browser/app/bower_components/showdown/license.txt +0 -34
  260. data/lib/api_browser/app/bower_components/showdown/package.json +0 -47
  261. data/lib/api_browser/app/bower_components/showdown/src/extensions/github.js +0 -25
  262. data/lib/api_browser/app/bower_components/showdown/src/extensions/prettify.js +0 -29
  263. data/lib/api_browser/app/bower_components/showdown/src/extensions/table.js +0 -106
  264. data/lib/api_browser/app/bower_components/showdown/src/extensions/twitter.js +0 -42
  265. data/lib/api_browser/app/bower_components/showdown/src/ng-showdown.js +0 -150
  266. data/lib/api_browser/app/bower_components/showdown/src/showdown.js +0 -1454
@@ -1,126 +0,0 @@
1
- // Type definitions for Angular JS 1.1.5+ (ui.router module)
2
- // Project: https://github.com/angular-ui/ui-router
3
- // Definitions by: Michel Salib <https://github.com/michelsalib>
4
- // Definitions: https://github.com/borisyankov/DefinitelyTyped
5
-
6
- declare module ng.ui {
7
-
8
- interface IState {
9
- name?: string;
10
- template?: string;
11
- templateUrl?: any; // string || () => string
12
- templateProvider?: any; // () => string || IPromise<string>
13
- controller?: any;
14
- controllerAs?: string;
15
- controllerProvider?: any;
16
- resolve?: {};
17
- url?: string;
18
- params?: any;
19
- views?: {};
20
- abstract?: boolean;
21
- onEnter?: (...args: any[]) => void;
22
- onExit?: (...args: any[]) => void;
23
- data?: any;
24
- reloadOnSearch?: boolean;
25
- }
26
-
27
- interface ITypedState<T> extends IState {
28
- data?: T;
29
- }
30
-
31
- interface IStateProvider extends IServiceProvider {
32
- state(name: string, config: IState): IStateProvider;
33
- state(config: IState): IStateProvider;
34
- decorator(name?: string, decorator?: (state: IState, parent: Function) => any): any;
35
- }
36
-
37
- interface IUrlMatcher {
38
- concat(pattern: string): IUrlMatcher;
39
- exec(path: string, searchParams: {}): {};
40
- parameters(): string[];
41
- format(values: {}): string;
42
- }
43
-
44
- interface IUrlMatcherFactory {
45
- compile(pattern: string): IUrlMatcher;
46
- isMatcher(o: any): boolean;
47
- }
48
-
49
- interface IUrlRouterProvider extends IServiceProvider {
50
- when(whenPath: RegExp, handler: Function): IUrlRouterProvider;
51
- when(whenPath: RegExp, handler: any[]): IUrlRouterProvider;
52
- when(whenPath: RegExp, toPath: string): IUrlRouterProvider;
53
- when(whenPath: IUrlMatcher, hanlder: Function): IUrlRouterProvider;
54
- when(whenPath: IUrlMatcher, handler: any[]): IUrlRouterProvider;
55
- when(whenPath: IUrlMatcher, toPath: string): IUrlRouterProvider;
56
- when(whenPath: string, handler: Function): IUrlRouterProvider;
57
- when(whenPath: string, handler: any[]): IUrlRouterProvider;
58
- when(whenPath: string, toPath: string): IUrlRouterProvider;
59
- otherwise(handler: Function): IUrlRouterProvider;
60
- otherwise(handler: any[]): IUrlRouterProvider;
61
- otherwise(path: string): IUrlRouterProvider;
62
- rule(handler: Function): IUrlRouterProvider;
63
- rule(handler: any[]): IUrlRouterProvider;
64
- }
65
-
66
- interface IStateOptions {
67
- location?: any;
68
- inherit?: boolean;
69
- relative?: IState;
70
- notify?: boolean;
71
- reload?: boolean;
72
- }
73
-
74
- interface IHrefOptions {
75
- lossy?: boolean;
76
- inherit?: boolean;
77
- relative?: IState;
78
- absolute?: boolean;
79
- }
80
-
81
- interface IStateService {
82
- go(to: string, params?: {}, options?: IStateOptions): IPromise<any>;
83
- transitionTo(state: string, params?: {}, updateLocation?: boolean): void;
84
- transitionTo(state: string, params?: {}, options?: IStateOptions): void;
85
- includes(state: string, params?: {}): boolean;
86
- is(state:string, params?: {}): boolean;
87
- is(state: IState, params?: {}): boolean;
88
- href(state: IState, params?: {}, options?: IHrefOptions): string;
89
- href(state: string, params?: {}, options?: IHrefOptions): string;
90
- get(state: string): IState;
91
- get(): IState[];
92
- current: IState;
93
- params: any;
94
- reload(): void;
95
- }
96
-
97
- interface IStateParamsService {
98
- [key: string]: any;
99
- }
100
-
101
- interface IStateParams {
102
- [key: string]: any;
103
- }
104
-
105
- interface IUrlRouterService {
106
- /*
107
- * Triggers an update; the same update that happens when the address bar
108
- * url changes, aka $locationChangeSuccess.
109
- *
110
- * This method is useful when you need to use preventDefault() on the
111
- * $locationChangeSuccess event, perform some custom logic (route protection,
112
- * auth, config, redirection, etc) and then finally proceed with the transition
113
- * by calling $urlRouter.sync().
114
- *
115
- */
116
- sync(): void;
117
- }
118
-
119
- interface IUiViewScrollProvider {
120
- /*
121
- * Reverts back to using the core $anchorScroll service for scrolling
122
- * based on the url anchor.
123
- */
124
- useAnchorScroll(): void;
125
- }
126
- }
@@ -1,23 +0,0 @@
1
- {
2
- "name": "angular-ui-router",
3
- "version": "0.2.15",
4
- "main": "./release/angular-ui-router.js",
5
- "dependencies": {
6
- "angular": ">= 1.0.8"
7
- },
8
- "ignore": [
9
- "**/.*",
10
- "node_modules",
11
- "bower_components",
12
- "component.json",
13
- "package.json",
14
- "lib",
15
- "config",
16
- "sample",
17
- "test",
18
- "tests",
19
- "ngdoc_assets",
20
- "Gruntfile.js",
21
- "files.js"
22
- ]
23
- }
@@ -1,4370 +0,0 @@
1
- /**
2
- * State-based routing for AngularJS
3
- * @version v0.2.15
4
- * @link http://angular-ui.github.com/
5
- * @license MIT License, http://www.opensource.org/licenses/MIT
6
- */
7
-
8
- /* commonjs package manager support (eg componentjs) */
9
- if (typeof module !== "undefined" && typeof exports !== "undefined" && module.exports === exports){
10
- module.exports = 'ui.router';
11
- }
12
-
13
- (function (window, angular, undefined) {
14
- /*jshint globalstrict:true*/
15
- /*global angular:false*/
16
- 'use strict';
17
-
18
- var isDefined = angular.isDefined,
19
- isFunction = angular.isFunction,
20
- isString = angular.isString,
21
- isObject = angular.isObject,
22
- isArray = angular.isArray,
23
- forEach = angular.forEach,
24
- extend = angular.extend,
25
- copy = angular.copy;
26
-
27
- function inherit(parent, extra) {
28
- return extend(new (extend(function() {}, { prototype: parent }))(), extra);
29
- }
30
-
31
- function merge(dst) {
32
- forEach(arguments, function(obj) {
33
- if (obj !== dst) {
34
- forEach(obj, function(value, key) {
35
- if (!dst.hasOwnProperty(key)) dst[key] = value;
36
- });
37
- }
38
- });
39
- return dst;
40
- }
41
-
42
- /**
43
- * Finds the common ancestor path between two states.
44
- *
45
- * @param {Object} first The first state.
46
- * @param {Object} second The second state.
47
- * @return {Array} Returns an array of state names in descending order, not including the root.
48
- */
49
- function ancestors(first, second) {
50
- var path = [];
51
-
52
- for (var n in first.path) {
53
- if (first.path[n] !== second.path[n]) break;
54
- path.push(first.path[n]);
55
- }
56
- return path;
57
- }
58
-
59
- /**
60
- * IE8-safe wrapper for `Object.keys()`.
61
- *
62
- * @param {Object} object A JavaScript object.
63
- * @return {Array} Returns the keys of the object as an array.
64
- */
65
- function objectKeys(object) {
66
- if (Object.keys) {
67
- return Object.keys(object);
68
- }
69
- var result = [];
70
-
71
- forEach(object, function(val, key) {
72
- result.push(key);
73
- });
74
- return result;
75
- }
76
-
77
- /**
78
- * IE8-safe wrapper for `Array.prototype.indexOf()`.
79
- *
80
- * @param {Array} array A JavaScript array.
81
- * @param {*} value A value to search the array for.
82
- * @return {Number} Returns the array index value of `value`, or `-1` if not present.
83
- */
84
- function indexOf(array, value) {
85
- if (Array.prototype.indexOf) {
86
- return array.indexOf(value, Number(arguments[2]) || 0);
87
- }
88
- var len = array.length >>> 0, from = Number(arguments[2]) || 0;
89
- from = (from < 0) ? Math.ceil(from) : Math.floor(from);
90
-
91
- if (from < 0) from += len;
92
-
93
- for (; from < len; from++) {
94
- if (from in array && array[from] === value) return from;
95
- }
96
- return -1;
97
- }
98
-
99
- /**
100
- * Merges a set of parameters with all parameters inherited between the common parents of the
101
- * current state and a given destination state.
102
- *
103
- * @param {Object} currentParams The value of the current state parameters ($stateParams).
104
- * @param {Object} newParams The set of parameters which will be composited with inherited params.
105
- * @param {Object} $current Internal definition of object representing the current state.
106
- * @param {Object} $to Internal definition of object representing state to transition to.
107
- */
108
- function inheritParams(currentParams, newParams, $current, $to) {
109
- var parents = ancestors($current, $to), parentParams, inherited = {}, inheritList = [];
110
-
111
- for (var i in parents) {
112
- if (!parents[i].params) continue;
113
- parentParams = objectKeys(parents[i].params);
114
- if (!parentParams.length) continue;
115
-
116
- for (var j in parentParams) {
117
- if (indexOf(inheritList, parentParams[j]) >= 0) continue;
118
- inheritList.push(parentParams[j]);
119
- inherited[parentParams[j]] = currentParams[parentParams[j]];
120
- }
121
- }
122
- return extend({}, inherited, newParams);
123
- }
124
-
125
- /**
126
- * Performs a non-strict comparison of the subset of two objects, defined by a list of keys.
127
- *
128
- * @param {Object} a The first object.
129
- * @param {Object} b The second object.
130
- * @param {Array} keys The list of keys within each object to compare. If the list is empty or not specified,
131
- * it defaults to the list of keys in `a`.
132
- * @return {Boolean} Returns `true` if the keys match, otherwise `false`.
133
- */
134
- function equalForKeys(a, b, keys) {
135
- if (!keys) {
136
- keys = [];
137
- for (var n in a) keys.push(n); // Used instead of Object.keys() for IE8 compatibility
138
- }
139
-
140
- for (var i=0; i<keys.length; i++) {
141
- var k = keys[i];
142
- if (a[k] != b[k]) return false; // Not '===', values aren't necessarily normalized
143
- }
144
- return true;
145
- }
146
-
147
- /**
148
- * Returns the subset of an object, based on a list of keys.
149
- *
150
- * @param {Array} keys
151
- * @param {Object} values
152
- * @return {Boolean} Returns a subset of `values`.
153
- */
154
- function filterByKeys(keys, values) {
155
- var filtered = {};
156
-
157
- forEach(keys, function (name) {
158
- filtered[name] = values[name];
159
- });
160
- return filtered;
161
- }
162
-
163
- // like _.indexBy
164
- // when you know that your index values will be unique, or you want last-one-in to win
165
- function indexBy(array, propName) {
166
- var result = {};
167
- forEach(array, function(item) {
168
- result[item[propName]] = item;
169
- });
170
- return result;
171
- }
172
-
173
- // extracted from underscore.js
174
- // Return a copy of the object only containing the whitelisted properties.
175
- function pick(obj) {
176
- var copy = {};
177
- var keys = Array.prototype.concat.apply(Array.prototype, Array.prototype.slice.call(arguments, 1));
178
- forEach(keys, function(key) {
179
- if (key in obj) copy[key] = obj[key];
180
- });
181
- return copy;
182
- }
183
-
184
- // extracted from underscore.js
185
- // Return a copy of the object omitting the blacklisted properties.
186
- function omit(obj) {
187
- var copy = {};
188
- var keys = Array.prototype.concat.apply(Array.prototype, Array.prototype.slice.call(arguments, 1));
189
- for (var key in obj) {
190
- if (indexOf(keys, key) == -1) copy[key] = obj[key];
191
- }
192
- return copy;
193
- }
194
-
195
- function pluck(collection, key) {
196
- var result = isArray(collection) ? [] : {};
197
-
198
- forEach(collection, function(val, i) {
199
- result[i] = isFunction(key) ? key(val) : val[key];
200
- });
201
- return result;
202
- }
203
-
204
- function filter(collection, callback) {
205
- var array = isArray(collection);
206
- var result = array ? [] : {};
207
- forEach(collection, function(val, i) {
208
- if (callback(val, i)) {
209
- result[array ? result.length : i] = val;
210
- }
211
- });
212
- return result;
213
- }
214
-
215
- function map(collection, callback) {
216
- var result = isArray(collection) ? [] : {};
217
-
218
- forEach(collection, function(val, i) {
219
- result[i] = callback(val, i);
220
- });
221
- return result;
222
- }
223
-
224
- /**
225
- * @ngdoc overview
226
- * @name ui.router.util
227
- *
228
- * @description
229
- * # ui.router.util sub-module
230
- *
231
- * This module is a dependency of other sub-modules. Do not include this module as a dependency
232
- * in your angular app (use {@link ui.router} module instead).
233
- *
234
- */
235
- angular.module('ui.router.util', ['ng']);
236
-
237
- /**
238
- * @ngdoc overview
239
- * @name ui.router.router
240
- *
241
- * @requires ui.router.util
242
- *
243
- * @description
244
- * # ui.router.router sub-module
245
- *
246
- * This module is a dependency of other sub-modules. Do not include this module as a dependency
247
- * in your angular app (use {@link ui.router} module instead).
248
- */
249
- angular.module('ui.router.router', ['ui.router.util']);
250
-
251
- /**
252
- * @ngdoc overview
253
- * @name ui.router.state
254
- *
255
- * @requires ui.router.router
256
- * @requires ui.router.util
257
- *
258
- * @description
259
- * # ui.router.state sub-module
260
- *
261
- * This module is a dependency of the main ui.router module. Do not include this module as a dependency
262
- * in your angular app (use {@link ui.router} module instead).
263
- *
264
- */
265
- angular.module('ui.router.state', ['ui.router.router', 'ui.router.util']);
266
-
267
- /**
268
- * @ngdoc overview
269
- * @name ui.router
270
- *
271
- * @requires ui.router.state
272
- *
273
- * @description
274
- * # ui.router
275
- *
276
- * ## The main module for ui.router
277
- * There are several sub-modules included with the ui.router module, however only this module is needed
278
- * as a dependency within your angular app. The other modules are for organization purposes.
279
- *
280
- * The modules are:
281
- * * ui.router - the main "umbrella" module
282
- * * ui.router.router -
283
- *
284
- * *You'll need to include **only** this module as the dependency within your angular app.*
285
- *
286
- * <pre>
287
- * <!doctype html>
288
- * <html ng-app="myApp">
289
- * <head>
290
- * <script src="js/angular.js"></script>
291
- * <!-- Include the ui-router script -->
292
- * <script src="js/angular-ui-router.min.js"></script>
293
- * <script>
294
- * // ...and add 'ui.router' as a dependency
295
- * var myApp = angular.module('myApp', ['ui.router']);
296
- * </script>
297
- * </head>
298
- * <body>
299
- * </body>
300
- * </html>
301
- * </pre>
302
- */
303
- angular.module('ui.router', ['ui.router.state']);
304
-
305
- angular.module('ui.router.compat', ['ui.router']);
306
-
307
- /**
308
- * @ngdoc object
309
- * @name ui.router.util.$resolve
310
- *
311
- * @requires $q
312
- * @requires $injector
313
- *
314
- * @description
315
- * Manages resolution of (acyclic) graphs of promises.
316
- */
317
- $Resolve.$inject = ['$q', '$injector'];
318
- function $Resolve( $q, $injector) {
319
-
320
- var VISIT_IN_PROGRESS = 1,
321
- VISIT_DONE = 2,
322
- NOTHING = {},
323
- NO_DEPENDENCIES = [],
324
- NO_LOCALS = NOTHING,
325
- NO_PARENT = extend($q.when(NOTHING), { $$promises: NOTHING, $$values: NOTHING });
326
-
327
-
328
- /**
329
- * @ngdoc function
330
- * @name ui.router.util.$resolve#study
331
- * @methodOf ui.router.util.$resolve
332
- *
333
- * @description
334
- * Studies a set of invocables that are likely to be used multiple times.
335
- * <pre>
336
- * $resolve.study(invocables)(locals, parent, self)
337
- * </pre>
338
- * is equivalent to
339
- * <pre>
340
- * $resolve.resolve(invocables, locals, parent, self)
341
- * </pre>
342
- * but the former is more efficient (in fact `resolve` just calls `study`
343
- * internally).
344
- *
345
- * @param {object} invocables Invocable objects
346
- * @return {function} a function to pass in locals, parent and self
347
- */
348
- this.study = function (invocables) {
349
- if (!isObject(invocables)) throw new Error("'invocables' must be an object");
350
- var invocableKeys = objectKeys(invocables || {});
351
-
352
- // Perform a topological sort of invocables to build an ordered plan
353
- var plan = [], cycle = [], visited = {};
354
- function visit(value, key) {
355
- if (visited[key] === VISIT_DONE) return;
356
-
357
- cycle.push(key);
358
- if (visited[key] === VISIT_IN_PROGRESS) {
359
- cycle.splice(0, indexOf(cycle, key));
360
- throw new Error("Cyclic dependency: " + cycle.join(" -> "));
361
- }
362
- visited[key] = VISIT_IN_PROGRESS;
363
-
364
- if (isString(value)) {
365
- plan.push(key, [ function() { return $injector.get(value); }], NO_DEPENDENCIES);
366
- } else {
367
- var params = $injector.annotate(value);
368
- forEach(params, function (param) {
369
- if (param !== key && invocables.hasOwnProperty(param)) visit(invocables[param], param);
370
- });
371
- plan.push(key, value, params);
372
- }
373
-
374
- cycle.pop();
375
- visited[key] = VISIT_DONE;
376
- }
377
- forEach(invocables, visit);
378
- invocables = cycle = visited = null; // plan is all that's required
379
-
380
- function isResolve(value) {
381
- return isObject(value) && value.then && value.$$promises;
382
- }
383
-
384
- return function (locals, parent, self) {
385
- if (isResolve(locals) && self === undefined) {
386
- self = parent; parent = locals; locals = null;
387
- }
388
- if (!locals) locals = NO_LOCALS;
389
- else if (!isObject(locals)) {
390
- throw new Error("'locals' must be an object");
391
- }
392
- if (!parent) parent = NO_PARENT;
393
- else if (!isResolve(parent)) {
394
- throw new Error("'parent' must be a promise returned by $resolve.resolve()");
395
- }
396
-
397
- // To complete the overall resolution, we have to wait for the parent
398
- // promise and for the promise for each invokable in our plan.
399
- var resolution = $q.defer(),
400
- result = resolution.promise,
401
- promises = result.$$promises = {},
402
- values = extend({}, locals),
403
- wait = 1 + plan.length/3,
404
- merged = false;
405
-
406
- function done() {
407
- // Merge parent values we haven't got yet and publish our own $$values
408
- if (!--wait) {
409
- if (!merged) merge(values, parent.$$values);
410
- result.$$values = values;
411
- result.$$promises = result.$$promises || true; // keep for isResolve()
412
- delete result.$$inheritedValues;
413
- resolution.resolve(values);
414
- }
415
- }
416
-
417
- function fail(reason) {
418
- result.$$failure = reason;
419
- resolution.reject(reason);
420
- }
421
-
422
- // Short-circuit if parent has already failed
423
- if (isDefined(parent.$$failure)) {
424
- fail(parent.$$failure);
425
- return result;
426
- }
427
-
428
- if (parent.$$inheritedValues) {
429
- merge(values, omit(parent.$$inheritedValues, invocableKeys));
430
- }
431
-
432
- // Merge parent values if the parent has already resolved, or merge
433
- // parent promises and wait if the parent resolve is still in progress.
434
- extend(promises, parent.$$promises);
435
- if (parent.$$values) {
436
- merged = merge(values, omit(parent.$$values, invocableKeys));
437
- result.$$inheritedValues = omit(parent.$$values, invocableKeys);
438
- done();
439
- } else {
440
- if (parent.$$inheritedValues) {
441
- result.$$inheritedValues = omit(parent.$$inheritedValues, invocableKeys);
442
- }
443
- parent.then(done, fail);
444
- }
445
-
446
- // Process each invocable in the plan, but ignore any where a local of the same name exists.
447
- for (var i=0, ii=plan.length; i<ii; i+=3) {
448
- if (locals.hasOwnProperty(plan[i])) done();
449
- else invoke(plan[i], plan[i+1], plan[i+2]);
450
- }
451
-
452
- function invoke(key, invocable, params) {
453
- // Create a deferred for this invocation. Failures will propagate to the resolution as well.
454
- var invocation = $q.defer(), waitParams = 0;
455
- function onfailure(reason) {
456
- invocation.reject(reason);
457
- fail(reason);
458
- }
459
- // Wait for any parameter that we have a promise for (either from parent or from this
460
- // resolve; in that case study() will have made sure it's ordered before us in the plan).
461
- forEach(params, function (dep) {
462
- if (promises.hasOwnProperty(dep) && !locals.hasOwnProperty(dep)) {
463
- waitParams++;
464
- promises[dep].then(function (result) {
465
- values[dep] = result;
466
- if (!(--waitParams)) proceed();
467
- }, onfailure);
468
- }
469
- });
470
- if (!waitParams) proceed();
471
- function proceed() {
472
- if (isDefined(result.$$failure)) return;
473
- try {
474
- invocation.resolve($injector.invoke(invocable, self, values));
475
- invocation.promise.then(function (result) {
476
- values[key] = result;
477
- done();
478
- }, onfailure);
479
- } catch (e) {
480
- onfailure(e);
481
- }
482
- }
483
- // Publish promise synchronously; invocations further down in the plan may depend on it.
484
- promises[key] = invocation.promise;
485
- }
486
-
487
- return result;
488
- };
489
- };
490
-
491
- /**
492
- * @ngdoc function
493
- * @name ui.router.util.$resolve#resolve
494
- * @methodOf ui.router.util.$resolve
495
- *
496
- * @description
497
- * Resolves a set of invocables. An invocable is a function to be invoked via
498
- * `$injector.invoke()`, and can have an arbitrary number of dependencies.
499
- * An invocable can either return a value directly,
500
- * or a `$q` promise. If a promise is returned it will be resolved and the
501
- * resulting value will be used instead. Dependencies of invocables are resolved
502
- * (in this order of precedence)
503
- *
504
- * - from the specified `locals`
505
- * - from another invocable that is part of this `$resolve` call
506
- * - from an invocable that is inherited from a `parent` call to `$resolve`
507
- * (or recursively
508
- * - from any ancestor `$resolve` of that parent).
509
- *
510
- * The return value of `$resolve` is a promise for an object that contains
511
- * (in this order of precedence)
512
- *
513
- * - any `locals` (if specified)
514
- * - the resolved return values of all injectables
515
- * - any values inherited from a `parent` call to `$resolve` (if specified)
516
- *
517
- * The promise will resolve after the `parent` promise (if any) and all promises
518
- * returned by injectables have been resolved. If any invocable
519
- * (or `$injector.invoke`) throws an exception, or if a promise returned by an
520
- * invocable is rejected, the `$resolve` promise is immediately rejected with the
521
- * same error. A rejection of a `parent` promise (if specified) will likewise be
522
- * propagated immediately. Once the `$resolve` promise has been rejected, no
523
- * further invocables will be called.
524
- *
525
- * Cyclic dependencies between invocables are not permitted and will caues `$resolve`
526
- * to throw an error. As a special case, an injectable can depend on a parameter
527
- * with the same name as the injectable, which will be fulfilled from the `parent`
528
- * injectable of the same name. This allows inherited values to be decorated.
529
- * Note that in this case any other injectable in the same `$resolve` with the same
530
- * dependency would see the decorated value, not the inherited value.
531
- *
532
- * Note that missing dependencies -- unlike cyclic dependencies -- will cause an
533
- * (asynchronous) rejection of the `$resolve` promise rather than a (synchronous)
534
- * exception.
535
- *
536
- * Invocables are invoked eagerly as soon as all dependencies are available.
537
- * This is true even for dependencies inherited from a `parent` call to `$resolve`.
538
- *
539
- * As a special case, an invocable can be a string, in which case it is taken to
540
- * be a service name to be passed to `$injector.get()`. This is supported primarily
541
- * for backwards-compatibility with the `resolve` property of `$routeProvider`
542
- * routes.
543
- *
544
- * @param {object} invocables functions to invoke or
545
- * `$injector` services to fetch.
546
- * @param {object} locals values to make available to the injectables
547
- * @param {object} parent a promise returned by another call to `$resolve`.
548
- * @param {object} self the `this` for the invoked methods
549
- * @return {object} Promise for an object that contains the resolved return value
550
- * of all invocables, as well as any inherited and local values.
551
- */
552
- this.resolve = function (invocables, locals, parent, self) {
553
- return this.study(invocables)(locals, parent, self);
554
- };
555
- }
556
-
557
- angular.module('ui.router.util').service('$resolve', $Resolve);
558
-
559
-
560
- /**
561
- * @ngdoc object
562
- * @name ui.router.util.$templateFactory
563
- *
564
- * @requires $http
565
- * @requires $templateCache
566
- * @requires $injector
567
- *
568
- * @description
569
- * Service. Manages loading of templates.
570
- */
571
- $TemplateFactory.$inject = ['$http', '$templateCache', '$injector'];
572
- function $TemplateFactory( $http, $templateCache, $injector) {
573
-
574
- /**
575
- * @ngdoc function
576
- * @name ui.router.util.$templateFactory#fromConfig
577
- * @methodOf ui.router.util.$templateFactory
578
- *
579
- * @description
580
- * Creates a template from a configuration object.
581
- *
582
- * @param {object} config Configuration object for which to load a template.
583
- * The following properties are search in the specified order, and the first one
584
- * that is defined is used to create the template:
585
- *
586
- * @param {string|object} config.template html string template or function to
587
- * load via {@link ui.router.util.$templateFactory#fromString fromString}.
588
- * @param {string|object} config.templateUrl url to load or a function returning
589
- * the url to load via {@link ui.router.util.$templateFactory#fromUrl fromUrl}.
590
- * @param {Function} config.templateProvider function to invoke via
591
- * {@link ui.router.util.$templateFactory#fromProvider fromProvider}.
592
- * @param {object} params Parameters to pass to the template function.
593
- * @param {object} locals Locals to pass to `invoke` if the template is loaded
594
- * via a `templateProvider`. Defaults to `{ params: params }`.
595
- *
596
- * @return {string|object} The template html as a string, or a promise for
597
- * that string,or `null` if no template is configured.
598
- */
599
- this.fromConfig = function (config, params, locals) {
600
- return (
601
- isDefined(config.template) ? this.fromString(config.template, params) :
602
- isDefined(config.templateUrl) ? this.fromUrl(config.templateUrl, params) :
603
- isDefined(config.templateProvider) ? this.fromProvider(config.templateProvider, params, locals) :
604
- null
605
- );
606
- };
607
-
608
- /**
609
- * @ngdoc function
610
- * @name ui.router.util.$templateFactory#fromString
611
- * @methodOf ui.router.util.$templateFactory
612
- *
613
- * @description
614
- * Creates a template from a string or a function returning a string.
615
- *
616
- * @param {string|object} template html template as a string or function that
617
- * returns an html template as a string.
618
- * @param {object} params Parameters to pass to the template function.
619
- *
620
- * @return {string|object} The template html as a string, or a promise for that
621
- * string.
622
- */
623
- this.fromString = function (template, params) {
624
- return isFunction(template) ? template(params) : template;
625
- };
626
-
627
- /**
628
- * @ngdoc function
629
- * @name ui.router.util.$templateFactory#fromUrl
630
- * @methodOf ui.router.util.$templateFactory
631
- *
632
- * @description
633
- * Loads a template from the a URL via `$http` and `$templateCache`.
634
- *
635
- * @param {string|Function} url url of the template to load, or a function
636
- * that returns a url.
637
- * @param {Object} params Parameters to pass to the url function.
638
- * @return {string|Promise.<string>} The template html as a string, or a promise
639
- * for that string.
640
- */
641
- this.fromUrl = function (url, params) {
642
- if (isFunction(url)) url = url(params);
643
- if (url == null) return null;
644
- else return $http
645
- .get(url, { cache: $templateCache, headers: { Accept: 'text/html' }})
646
- .then(function(response) { return response.data; });
647
- };
648
-
649
- /**
650
- * @ngdoc function
651
- * @name ui.router.util.$templateFactory#fromProvider
652
- * @methodOf ui.router.util.$templateFactory
653
- *
654
- * @description
655
- * Creates a template by invoking an injectable provider function.
656
- *
657
- * @param {Function} provider Function to invoke via `$injector.invoke`
658
- * @param {Object} params Parameters for the template.
659
- * @param {Object} locals Locals to pass to `invoke`. Defaults to
660
- * `{ params: params }`.
661
- * @return {string|Promise.<string>} The template html as a string, or a promise
662
- * for that string.
663
- */
664
- this.fromProvider = function (provider, params, locals) {
665
- return $injector.invoke(provider, null, locals || { params: params });
666
- };
667
- }
668
-
669
- angular.module('ui.router.util').service('$templateFactory', $TemplateFactory);
670
-
671
- var $$UMFP; // reference to $UrlMatcherFactoryProvider
672
-
673
- /**
674
- * @ngdoc object
675
- * @name ui.router.util.type:UrlMatcher
676
- *
677
- * @description
678
- * Matches URLs against patterns and extracts named parameters from the path or the search
679
- * part of the URL. A URL pattern consists of a path pattern, optionally followed by '?' and a list
680
- * of search parameters. Multiple search parameter names are separated by '&'. Search parameters
681
- * do not influence whether or not a URL is matched, but their values are passed through into
682
- * the matched parameters returned by {@link ui.router.util.type:UrlMatcher#methods_exec exec}.
683
- *
684
- * Path parameter placeholders can be specified using simple colon/catch-all syntax or curly brace
685
- * syntax, which optionally allows a regular expression for the parameter to be specified:
686
- *
687
- * * `':'` name - colon placeholder
688
- * * `'*'` name - catch-all placeholder
689
- * * `'{' name '}'` - curly placeholder
690
- * * `'{' name ':' regexp|type '}'` - curly placeholder with regexp or type name. Should the
691
- * regexp itself contain curly braces, they must be in matched pairs or escaped with a backslash.
692
- *
693
- * Parameter names may contain only word characters (latin letters, digits, and underscore) and
694
- * must be unique within the pattern (across both path and search parameters). For colon
695
- * placeholders or curly placeholders without an explicit regexp, a path parameter matches any
696
- * number of characters other than '/'. For catch-all placeholders the path parameter matches
697
- * any number of characters.
698
- *
699
- * Examples:
700
- *
701
- * * `'/hello/'` - Matches only if the path is exactly '/hello/'. There is no special treatment for
702
- * trailing slashes, and patterns have to match the entire path, not just a prefix.
703
- * * `'/user/:id'` - Matches '/user/bob' or '/user/1234!!!' or even '/user/' but not '/user' or
704
- * '/user/bob/details'. The second path segment will be captured as the parameter 'id'.
705
- * * `'/user/{id}'` - Same as the previous example, but using curly brace syntax.
706
- * * `'/user/{id:[^/]*}'` - Same as the previous example.
707
- * * `'/user/{id:[0-9a-fA-F]{1,8}}'` - Similar to the previous example, but only matches if the id
708
- * parameter consists of 1 to 8 hex digits.
709
- * * `'/files/{path:.*}'` - Matches any URL starting with '/files/' and captures the rest of the
710
- * path into the parameter 'path'.
711
- * * `'/files/*path'` - ditto.
712
- * * `'/calendar/{start:date}'` - Matches "/calendar/2014-11-12" (because the pattern defined
713
- * in the built-in `date` Type matches `2014-11-12`) and provides a Date object in $stateParams.start
714
- *
715
- * @param {string} pattern The pattern to compile into a matcher.
716
- * @param {Object} config A configuration object hash:
717
- * @param {Object=} parentMatcher Used to concatenate the pattern/config onto
718
- * an existing UrlMatcher
719
- *
720
- * * `caseInsensitive` - `true` if URL matching should be case insensitive, otherwise `false`, the default value (for backward compatibility) is `false`.
721
- * * `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`.
722
- *
723
- * @property {string} prefix A static prefix of this pattern. The matcher guarantees that any
724
- * URL matching this matcher (i.e. any string for which {@link ui.router.util.type:UrlMatcher#methods_exec exec()} returns
725
- * non-null) will start with this prefix.
726
- *
727
- * @property {string} source The pattern that was passed into the constructor
728
- *
729
- * @property {string} sourcePath The path portion of the source property
730
- *
731
- * @property {string} sourceSearch The search portion of the source property
732
- *
733
- * @property {string} regex The constructed regex that will be used to match against the url when
734
- * it is time to determine which url will match.
735
- *
736
- * @returns {Object} New `UrlMatcher` object
737
- */
738
- function UrlMatcher(pattern, config, parentMatcher) {
739
- config = extend({ params: {} }, isObject(config) ? config : {});
740
-
741
- // Find all placeholders and create a compiled pattern, using either classic or curly syntax:
742
- // '*' name
743
- // ':' name
744
- // '{' name '}'
745
- // '{' name ':' regexp '}'
746
- // The regular expression is somewhat complicated due to the need to allow curly braces
747
- // inside the regular expression. The placeholder regexp breaks down as follows:
748
- // ([:*])([\w\[\]]+) - classic placeholder ($1 / $2) (search version has - for snake-case)
749
- // \{([\w\[\]]+)(?:\:( ... ))?\} - curly brace placeholder ($3) with optional regexp/type ... ($4) (search version has - for snake-case
750
- // (?: ... | ... | ... )+ - the regexp consists of any number of atoms, an atom being either
751
- // [^{}\\]+ - anything other than curly braces or backslash
752
- // \\. - a backslash escape
753
- // \{(?:[^{}\\]+|\\.)*\} - a matched set of curly braces containing other atoms
754
- var placeholder = /([:*])([\w\[\]]+)|\{([\w\[\]]+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,
755
- searchPlaceholder = /([:]?)([\w\[\]-]+)|\{([\w\[\]-]+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,
756
- compiled = '^', last = 0, m,
757
- segments = this.segments = [],
758
- parentParams = parentMatcher ? parentMatcher.params : {},
759
- params = this.params = parentMatcher ? parentMatcher.params.$$new() : new $$UMFP.ParamSet(),
760
- paramNames = [];
761
-
762
- function addParameter(id, type, config, location) {
763
- paramNames.push(id);
764
- if (parentParams[id]) return parentParams[id];
765
- if (!/^\w+(-+\w+)*(?:\[\])?$/.test(id)) throw new Error("Invalid parameter name '" + id + "' in pattern '" + pattern + "'");
766
- if (params[id]) throw new Error("Duplicate parameter name '" + id + "' in pattern '" + pattern + "'");
767
- params[id] = new $$UMFP.Param(id, type, config, location);
768
- return params[id];
769
- }
770
-
771
- function quoteRegExp(string, pattern, squash, optional) {
772
- var surroundPattern = ['',''], result = string.replace(/[\\\[\]\^$*+?.()|{}]/g, "\\$&");
773
- if (!pattern) return result;
774
- switch(squash) {
775
- case false: surroundPattern = ['(', ')' + (optional ? "?" : "")]; break;
776
- case true: surroundPattern = ['?(', ')?']; break;
777
- default: surroundPattern = ['(' + squash + "|", ')?']; break;
778
- }
779
- return result + surroundPattern[0] + pattern + surroundPattern[1];
780
- }
781
-
782
- this.source = pattern;
783
-
784
- // Split into static segments separated by path parameter placeholders.
785
- // The number of segments is always 1 more than the number of parameters.
786
- function matchDetails(m, isSearch) {
787
- var id, regexp, segment, type, cfg, arrayMode;
788
- id = m[2] || m[3]; // IE[78] returns '' for unmatched groups instead of null
789
- cfg = config.params[id];
790
- segment = pattern.substring(last, m.index);
791
- regexp = isSearch ? m[4] : m[4] || (m[1] == '*' ? '.*' : null);
792
- type = $$UMFP.type(regexp || "string") || inherit($$UMFP.type("string"), { pattern: new RegExp(regexp, config.caseInsensitive ? 'i' : undefined) });
793
- return {
794
- id: id, regexp: regexp, segment: segment, type: type, cfg: cfg
795
- };
796
- }
797
-
798
- var p, param, segment;
799
- while ((m = placeholder.exec(pattern))) {
800
- p = matchDetails(m, false);
801
- if (p.segment.indexOf('?') >= 0) break; // we're into the search part
802
-
803
- param = addParameter(p.id, p.type, p.cfg, "path");
804
- compiled += quoteRegExp(p.segment, param.type.pattern.source, param.squash, param.isOptional);
805
- segments.push(p.segment);
806
- last = placeholder.lastIndex;
807
- }
808
- segment = pattern.substring(last);
809
-
810
- // Find any search parameter names and remove them from the last segment
811
- var i = segment.indexOf('?');
812
-
813
- if (i >= 0) {
814
- var search = this.sourceSearch = segment.substring(i);
815
- segment = segment.substring(0, i);
816
- this.sourcePath = pattern.substring(0, last + i);
817
-
818
- if (search.length > 0) {
819
- last = 0;
820
- while ((m = searchPlaceholder.exec(search))) {
821
- p = matchDetails(m, true);
822
- param = addParameter(p.id, p.type, p.cfg, "search");
823
- last = placeholder.lastIndex;
824
- // check if ?&
825
- }
826
- }
827
- } else {
828
- this.sourcePath = pattern;
829
- this.sourceSearch = '';
830
- }
831
-
832
- compiled += quoteRegExp(segment) + (config.strict === false ? '\/?' : '') + '$';
833
- segments.push(segment);
834
-
835
- this.regexp = new RegExp(compiled, config.caseInsensitive ? 'i' : undefined);
836
- this.prefix = segments[0];
837
- this.$$paramNames = paramNames;
838
- }
839
-
840
- /**
841
- * @ngdoc function
842
- * @name ui.router.util.type:UrlMatcher#concat
843
- * @methodOf ui.router.util.type:UrlMatcher
844
- *
845
- * @description
846
- * Returns a new matcher for a pattern constructed by appending the path part and adding the
847
- * search parameters of the specified pattern to this pattern. The current pattern is not
848
- * modified. This can be understood as creating a pattern for URLs that are relative to (or
849
- * suffixes of) the current pattern.
850
- *
851
- * @example
852
- * The following two matchers are equivalent:
853
- * <pre>
854
- * new UrlMatcher('/user/{id}?q').concat('/details?date');
855
- * new UrlMatcher('/user/{id}/details?q&date');
856
- * </pre>
857
- *
858
- * @param {string} pattern The pattern to append.
859
- * @param {Object} config An object hash of the configuration for the matcher.
860
- * @returns {UrlMatcher} A matcher for the concatenated pattern.
861
- */
862
- UrlMatcher.prototype.concat = function (pattern, config) {
863
- // Because order of search parameters is irrelevant, we can add our own search
864
- // parameters to the end of the new pattern. Parse the new pattern by itself
865
- // and then join the bits together, but it's much easier to do this on a string level.
866
- var defaultConfig = {
867
- caseInsensitive: $$UMFP.caseInsensitive(),
868
- strict: $$UMFP.strictMode(),
869
- squash: $$UMFP.defaultSquashPolicy()
870
- };
871
- return new UrlMatcher(this.sourcePath + pattern + this.sourceSearch, extend(defaultConfig, config), this);
872
- };
873
-
874
- UrlMatcher.prototype.toString = function () {
875
- return this.source;
876
- };
877
-
878
- /**
879
- * @ngdoc function
880
- * @name ui.router.util.type:UrlMatcher#exec
881
- * @methodOf ui.router.util.type:UrlMatcher
882
- *
883
- * @description
884
- * Tests the specified path against this matcher, and returns an object containing the captured
885
- * parameter values, or null if the path does not match. The returned object contains the values
886
- * of any search parameters that are mentioned in the pattern, but their value may be null if
887
- * they are not present in `searchParams`. This means that search parameters are always treated
888
- * as optional.
889
- *
890
- * @example
891
- * <pre>
892
- * new UrlMatcher('/user/{id}?q&r').exec('/user/bob', {
893
- * x: '1', q: 'hello'
894
- * });
895
- * // returns { id: 'bob', q: 'hello', r: null }
896
- * </pre>
897
- *
898
- * @param {string} path The URL path to match, e.g. `$location.path()`.
899
- * @param {Object} searchParams URL search parameters, e.g. `$location.search()`.
900
- * @returns {Object} The captured parameter values.
901
- */
902
- UrlMatcher.prototype.exec = function (path, searchParams) {
903
- var m = this.regexp.exec(path);
904
- if (!m) return null;
905
- searchParams = searchParams || {};
906
-
907
- var paramNames = this.parameters(), nTotal = paramNames.length,
908
- nPath = this.segments.length - 1,
909
- values = {}, i, j, cfg, paramName;
910
-
911
- if (nPath !== m.length - 1) throw new Error("Unbalanced capture group in route '" + this.source + "'");
912
-
913
- function decodePathArray(string) {
914
- function reverseString(str) { return str.split("").reverse().join(""); }
915
- function unquoteDashes(str) { return str.replace(/\\-/g, "-"); }
916
-
917
- var split = reverseString(string).split(/-(?!\\)/);
918
- var allReversed = map(split, reverseString);
919
- return map(allReversed, unquoteDashes).reverse();
920
- }
921
-
922
- for (i = 0; i < nPath; i++) {
923
- paramName = paramNames[i];
924
- var param = this.params[paramName];
925
- var paramVal = m[i+1];
926
- // if the param value matches a pre-replace pair, replace the value before decoding.
927
- for (j = 0; j < param.replace; j++) {
928
- if (param.replace[j].from === paramVal) paramVal = param.replace[j].to;
929
- }
930
- if (paramVal && param.array === true) paramVal = decodePathArray(paramVal);
931
- values[paramName] = param.value(paramVal);
932
- }
933
- for (/**/; i < nTotal; i++) {
934
- paramName = paramNames[i];
935
- values[paramName] = this.params[paramName].value(searchParams[paramName]);
936
- }
937
-
938
- return values;
939
- };
940
-
941
- /**
942
- * @ngdoc function
943
- * @name ui.router.util.type:UrlMatcher#parameters
944
- * @methodOf ui.router.util.type:UrlMatcher
945
- *
946
- * @description
947
- * Returns the names of all path and search parameters of this pattern in an unspecified order.
948
- *
949
- * @returns {Array.<string>} An array of parameter names. Must be treated as read-only. If the
950
- * pattern has no parameters, an empty array is returned.
951
- */
952
- UrlMatcher.prototype.parameters = function (param) {
953
- if (!isDefined(param)) return this.$$paramNames;
954
- return this.params[param] || null;
955
- };
956
-
957
- /**
958
- * @ngdoc function
959
- * @name ui.router.util.type:UrlMatcher#validate
960
- * @methodOf ui.router.util.type:UrlMatcher
961
- *
962
- * @description
963
- * Checks an object hash of parameters to validate their correctness according to the parameter
964
- * types of this `UrlMatcher`.
965
- *
966
- * @param {Object} params The object hash of parameters to validate.
967
- * @returns {boolean} Returns `true` if `params` validates, otherwise `false`.
968
- */
969
- UrlMatcher.prototype.validates = function (params) {
970
- return this.params.$$validates(params);
971
- };
972
-
973
- /**
974
- * @ngdoc function
975
- * @name ui.router.util.type:UrlMatcher#format
976
- * @methodOf ui.router.util.type:UrlMatcher
977
- *
978
- * @description
979
- * Creates a URL that matches this pattern by substituting the specified values
980
- * for the path and search parameters. Null values for path parameters are
981
- * treated as empty strings.
982
- *
983
- * @example
984
- * <pre>
985
- * new UrlMatcher('/user/{id}?q').format({ id:'bob', q:'yes' });
986
- * // returns '/user/bob?q=yes'
987
- * </pre>
988
- *
989
- * @param {Object} values the values to substitute for the parameters in this pattern.
990
- * @returns {string} the formatted URL (path and optionally search part).
991
- */
992
- UrlMatcher.prototype.format = function (values) {
993
- values = values || {};
994
- var segments = this.segments, params = this.parameters(), paramset = this.params;
995
- if (!this.validates(values)) return null;
996
-
997
- var i, search = false, nPath = segments.length - 1, nTotal = params.length, result = segments[0];
998
-
999
- function encodeDashes(str) { // Replace dashes with encoded "\-"
1000
- return encodeURIComponent(str).replace(/-/g, function(c) { return '%5C%' + c.charCodeAt(0).toString(16).toUpperCase(); });
1001
- }
1002
-
1003
- for (i = 0; i < nTotal; i++) {
1004
- var isPathParam = i < nPath;
1005
- var name = params[i], param = paramset[name], value = param.value(values[name]);
1006
- var isDefaultValue = param.isOptional && param.type.equals(param.value(), value);
1007
- var squash = isDefaultValue ? param.squash : false;
1008
- var encoded = param.type.encode(value);
1009
-
1010
- if (isPathParam) {
1011
- var nextSegment = segments[i + 1];
1012
- if (squash === false) {
1013
- if (encoded != null) {
1014
- if (isArray(encoded)) {
1015
- result += map(encoded, encodeDashes).join("-");
1016
- } else {
1017
- result += encodeURIComponent(encoded);
1018
- }
1019
- }
1020
- result += nextSegment;
1021
- } else if (squash === true) {
1022
- var capture = result.match(/\/$/) ? /\/?(.*)/ : /(.*)/;
1023
- result += nextSegment.match(capture)[1];
1024
- } else if (isString(squash)) {
1025
- result += squash + nextSegment;
1026
- }
1027
- } else {
1028
- if (encoded == null || (isDefaultValue && squash !== false)) continue;
1029
- if (!isArray(encoded)) encoded = [ encoded ];
1030
- encoded = map(encoded, encodeURIComponent).join('&' + name + '=');
1031
- result += (search ? '&' : '?') + (name + '=' + encoded);
1032
- search = true;
1033
- }
1034
- }
1035
-
1036
- return result;
1037
- };
1038
-
1039
- /**
1040
- * @ngdoc object
1041
- * @name ui.router.util.type:Type
1042
- *
1043
- * @description
1044
- * Implements an interface to define custom parameter types that can be decoded from and encoded to
1045
- * string parameters matched in a URL. Used by {@link ui.router.util.type:UrlMatcher `UrlMatcher`}
1046
- * objects when matching or formatting URLs, or comparing or validating parameter values.
1047
- *
1048
- * See {@link ui.router.util.$urlMatcherFactory#methods_type `$urlMatcherFactory#type()`} for more
1049
- * information on registering custom types.
1050
- *
1051
- * @param {Object} config A configuration object which contains the custom type definition. The object's
1052
- * properties will override the default methods and/or pattern in `Type`'s public interface.
1053
- * @example
1054
- * <pre>
1055
- * {
1056
- * decode: function(val) { return parseInt(val, 10); },
1057
- * encode: function(val) { return val && val.toString(); },
1058
- * equals: function(a, b) { return this.is(a) && a === b; },
1059
- * is: function(val) { return angular.isNumber(val) isFinite(val) && val % 1 === 0; },
1060
- * pattern: /\d+/
1061
- * }
1062
- * </pre>
1063
- *
1064
- * @property {RegExp} pattern The regular expression pattern used to match values of this type when
1065
- * coming from a substring of a URL.
1066
- *
1067
- * @returns {Object} Returns a new `Type` object.
1068
- */
1069
- function Type(config) {
1070
- extend(this, config);
1071
- }
1072
-
1073
- /**
1074
- * @ngdoc function
1075
- * @name ui.router.util.type:Type#is
1076
- * @methodOf ui.router.util.type:Type
1077
- *
1078
- * @description
1079
- * Detects whether a value is of a particular type. Accepts a native (decoded) value
1080
- * and determines whether it matches the current `Type` object.
1081
- *
1082
- * @param {*} val The value to check.
1083
- * @param {string} key Optional. If the type check is happening in the context of a specific
1084
- * {@link ui.router.util.type:UrlMatcher `UrlMatcher`} object, this is the name of the
1085
- * parameter in which `val` is stored. Can be used for meta-programming of `Type` objects.
1086
- * @returns {Boolean} Returns `true` if the value matches the type, otherwise `false`.
1087
- */
1088
- Type.prototype.is = function(val, key) {
1089
- return true;
1090
- };
1091
-
1092
- /**
1093
- * @ngdoc function
1094
- * @name ui.router.util.type:Type#encode
1095
- * @methodOf ui.router.util.type:Type
1096
- *
1097
- * @description
1098
- * Encodes a custom/native type value to a string that can be embedded in a URL. Note that the
1099
- * return value does *not* need to be URL-safe (i.e. passed through `encodeURIComponent()`), it
1100
- * only needs to be a representation of `val` that has been coerced to a string.
1101
- *
1102
- * @param {*} val The value to encode.
1103
- * @param {string} key The name of the parameter in which `val` is stored. Can be used for
1104
- * meta-programming of `Type` objects.
1105
- * @returns {string} Returns a string representation of `val` that can be encoded in a URL.
1106
- */
1107
- Type.prototype.encode = function(val, key) {
1108
- return val;
1109
- };
1110
-
1111
- /**
1112
- * @ngdoc function
1113
- * @name ui.router.util.type:Type#decode
1114
- * @methodOf ui.router.util.type:Type
1115
- *
1116
- * @description
1117
- * Converts a parameter value (from URL string or transition param) to a custom/native value.
1118
- *
1119
- * @param {string} val The URL parameter value to decode.
1120
- * @param {string} key The name of the parameter in which `val` is stored. Can be used for
1121
- * meta-programming of `Type` objects.
1122
- * @returns {*} Returns a custom representation of the URL parameter value.
1123
- */
1124
- Type.prototype.decode = function(val, key) {
1125
- return val;
1126
- };
1127
-
1128
- /**
1129
- * @ngdoc function
1130
- * @name ui.router.util.type:Type#equals
1131
- * @methodOf ui.router.util.type:Type
1132
- *
1133
- * @description
1134
- * Determines whether two decoded values are equivalent.
1135
- *
1136
- * @param {*} a A value to compare against.
1137
- * @param {*} b A value to compare against.
1138
- * @returns {Boolean} Returns `true` if the values are equivalent/equal, otherwise `false`.
1139
- */
1140
- Type.prototype.equals = function(a, b) {
1141
- return a == b;
1142
- };
1143
-
1144
- Type.prototype.$subPattern = function() {
1145
- var sub = this.pattern.toString();
1146
- return sub.substr(1, sub.length - 2);
1147
- };
1148
-
1149
- Type.prototype.pattern = /.*/;
1150
-
1151
- Type.prototype.toString = function() { return "{Type:" + this.name + "}"; };
1152
-
1153
- /** Given an encoded string, or a decoded object, returns a decoded object */
1154
- Type.prototype.$normalize = function(val) {
1155
- return this.is(val) ? val : this.decode(val);
1156
- };
1157
-
1158
- /*
1159
- * Wraps an existing custom Type as an array of Type, depending on 'mode'.
1160
- * e.g.:
1161
- * - urlmatcher pattern "/path?{queryParam[]:int}"
1162
- * - url: "/path?queryParam=1&queryParam=2
1163
- * - $stateParams.queryParam will be [1, 2]
1164
- * if `mode` is "auto", then
1165
- * - url: "/path?queryParam=1 will create $stateParams.queryParam: 1
1166
- * - url: "/path?queryParam=1&queryParam=2 will create $stateParams.queryParam: [1, 2]
1167
- */
1168
- Type.prototype.$asArray = function(mode, isSearch) {
1169
- if (!mode) return this;
1170
- if (mode === "auto" && !isSearch) throw new Error("'auto' array mode is for query parameters only");
1171
-
1172
- function ArrayType(type, mode) {
1173
- function bindTo(type, callbackName) {
1174
- return function() {
1175
- return type[callbackName].apply(type, arguments);
1176
- };
1177
- }
1178
-
1179
- // Wrap non-array value as array
1180
- function arrayWrap(val) { return isArray(val) ? val : (isDefined(val) ? [ val ] : []); }
1181
- // Unwrap array value for "auto" mode. Return undefined for empty array.
1182
- function arrayUnwrap(val) {
1183
- switch(val.length) {
1184
- case 0: return undefined;
1185
- case 1: return mode === "auto" ? val[0] : val;
1186
- default: return val;
1187
- }
1188
- }
1189
- function falsey(val) { return !val; }
1190
-
1191
- // Wraps type (.is/.encode/.decode) functions to operate on each value of an array
1192
- function arrayHandler(callback, allTruthyMode) {
1193
- return function handleArray(val) {
1194
- val = arrayWrap(val);
1195
- var result = map(val, callback);
1196
- if (allTruthyMode === true)
1197
- return filter(result, falsey).length === 0;
1198
- return arrayUnwrap(result);
1199
- };
1200
- }
1201
-
1202
- // Wraps type (.equals) functions to operate on each value of an array
1203
- function arrayEqualsHandler(callback) {
1204
- return function handleArray(val1, val2) {
1205
- var left = arrayWrap(val1), right = arrayWrap(val2);
1206
- if (left.length !== right.length) return false;
1207
- for (var i = 0; i < left.length; i++) {
1208
- if (!callback(left[i], right[i])) return false;
1209
- }
1210
- return true;
1211
- };
1212
- }
1213
-
1214
- this.encode = arrayHandler(bindTo(type, 'encode'));
1215
- this.decode = arrayHandler(bindTo(type, 'decode'));
1216
- this.is = arrayHandler(bindTo(type, 'is'), true);
1217
- this.equals = arrayEqualsHandler(bindTo(type, 'equals'));
1218
- this.pattern = type.pattern;
1219
- this.$normalize = arrayHandler(bindTo(type, '$normalize'));
1220
- this.name = type.name;
1221
- this.$arrayMode = mode;
1222
- }
1223
-
1224
- return new ArrayType(this, mode);
1225
- };
1226
-
1227
-
1228
-
1229
- /**
1230
- * @ngdoc object
1231
- * @name ui.router.util.$urlMatcherFactory
1232
- *
1233
- * @description
1234
- * Factory for {@link ui.router.util.type:UrlMatcher `UrlMatcher`} instances. The factory
1235
- * is also available to providers under the name `$urlMatcherFactoryProvider`.
1236
- */
1237
- function $UrlMatcherFactory() {
1238
- $$UMFP = this;
1239
-
1240
- var isCaseInsensitive = false, isStrictMode = true, defaultSquashPolicy = false;
1241
-
1242
- function valToString(val) { return val != null ? val.toString().replace(/\//g, "%2F") : val; }
1243
- function valFromString(val) { return val != null ? val.toString().replace(/%2F/g, "/") : val; }
1244
-
1245
- var $types = {}, enqueue = true, typeQueue = [], injector, defaultTypes = {
1246
- string: {
1247
- encode: valToString,
1248
- decode: valFromString,
1249
- // TODO: in 1.0, make string .is() return false if value is undefined/null by default.
1250
- // In 0.2.x, string params are optional by default for backwards compat
1251
- is: function(val) { return val == null || !isDefined(val) || typeof val === "string"; },
1252
- pattern: /[^/]*/
1253
- },
1254
- int: {
1255
- encode: valToString,
1256
- decode: function(val) { return parseInt(val, 10); },
1257
- is: function(val) { return isDefined(val) && this.decode(val.toString()) === val; },
1258
- pattern: /\d+/
1259
- },
1260
- bool: {
1261
- encode: function(val) { return val ? 1 : 0; },
1262
- decode: function(val) { return parseInt(val, 10) !== 0; },
1263
- is: function(val) { return val === true || val === false; },
1264
- pattern: /0|1/
1265
- },
1266
- date: {
1267
- encode: function (val) {
1268
- if (!this.is(val))
1269
- return undefined;
1270
- return [ val.getFullYear(),
1271
- ('0' + (val.getMonth() + 1)).slice(-2),
1272
- ('0' + val.getDate()).slice(-2)
1273
- ].join("-");
1274
- },
1275
- decode: function (val) {
1276
- if (this.is(val)) return val;
1277
- var match = this.capture.exec(val);
1278
- return match ? new Date(match[1], match[2] - 1, match[3]) : undefined;
1279
- },
1280
- is: function(val) { return val instanceof Date && !isNaN(val.valueOf()); },
1281
- equals: function (a, b) { return this.is(a) && this.is(b) && a.toISOString() === b.toISOString(); },
1282
- pattern: /[0-9]{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[1-2][0-9]|3[0-1])/,
1283
- capture: /([0-9]{4})-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])/
1284
- },
1285
- json: {
1286
- encode: angular.toJson,
1287
- decode: angular.fromJson,
1288
- is: angular.isObject,
1289
- equals: angular.equals,
1290
- pattern: /[^/]*/
1291
- },
1292
- any: { // does not encode/decode
1293
- encode: angular.identity,
1294
- decode: angular.identity,
1295
- equals: angular.equals,
1296
- pattern: /.*/
1297
- }
1298
- };
1299
-
1300
- function getDefaultConfig() {
1301
- return {
1302
- strict: isStrictMode,
1303
- caseInsensitive: isCaseInsensitive
1304
- };
1305
- }
1306
-
1307
- function isInjectable(value) {
1308
- return (isFunction(value) || (isArray(value) && isFunction(value[value.length - 1])));
1309
- }
1310
-
1311
- /**
1312
- * [Internal] Get the default value of a parameter, which may be an injectable function.
1313
- */
1314
- $UrlMatcherFactory.$$getDefaultValue = function(config) {
1315
- if (!isInjectable(config.value)) return config.value;
1316
- if (!injector) throw new Error("Injectable functions cannot be called at configuration time");
1317
- return injector.invoke(config.value);
1318
- };
1319
-
1320
- /**
1321
- * @ngdoc function
1322
- * @name ui.router.util.$urlMatcherFactory#caseInsensitive
1323
- * @methodOf ui.router.util.$urlMatcherFactory
1324
- *
1325
- * @description
1326
- * Defines whether URL matching should be case sensitive (the default behavior), or not.
1327
- *
1328
- * @param {boolean} value `false` to match URL in a case sensitive manner; otherwise `true`;
1329
- * @returns {boolean} the current value of caseInsensitive
1330
- */
1331
- this.caseInsensitive = function(value) {
1332
- if (isDefined(value))
1333
- isCaseInsensitive = value;
1334
- return isCaseInsensitive;
1335
- };
1336
-
1337
- /**
1338
- * @ngdoc function
1339
- * @name ui.router.util.$urlMatcherFactory#strictMode
1340
- * @methodOf ui.router.util.$urlMatcherFactory
1341
- *
1342
- * @description
1343
- * Defines whether URLs should match trailing slashes, or not (the default behavior).
1344
- *
1345
- * @param {boolean=} value `false` to match trailing slashes in URLs, otherwise `true`.
1346
- * @returns {boolean} the current value of strictMode
1347
- */
1348
- this.strictMode = function(value) {
1349
- if (isDefined(value))
1350
- isStrictMode = value;
1351
- return isStrictMode;
1352
- };
1353
-
1354
- /**
1355
- * @ngdoc function
1356
- * @name ui.router.util.$urlMatcherFactory#defaultSquashPolicy
1357
- * @methodOf ui.router.util.$urlMatcherFactory
1358
- *
1359
- * @description
1360
- * Sets the default behavior when generating or matching URLs with default parameter values.
1361
- *
1362
- * @param {string} value A string that defines the default parameter URL squashing behavior.
1363
- * `nosquash`: When generating an href with a default parameter value, do not squash the parameter value from the URL
1364
- * `slash`: When generating an href with a default parameter value, squash (remove) the parameter value, and, if the
1365
- * parameter is surrounded by slashes, squash (remove) one slash from the URL
1366
- * any other string, e.g. "~": When generating an href with a default parameter value, squash (remove)
1367
- * the parameter value from the URL and replace it with this string.
1368
- */
1369
- this.defaultSquashPolicy = function(value) {
1370
- if (!isDefined(value)) return defaultSquashPolicy;
1371
- if (value !== true && value !== false && !isString(value))
1372
- throw new Error("Invalid squash policy: " + value + ". Valid policies: false, true, arbitrary-string");
1373
- defaultSquashPolicy = value;
1374
- return value;
1375
- };
1376
-
1377
- /**
1378
- * @ngdoc function
1379
- * @name ui.router.util.$urlMatcherFactory#compile
1380
- * @methodOf ui.router.util.$urlMatcherFactory
1381
- *
1382
- * @description
1383
- * Creates a {@link ui.router.util.type:UrlMatcher `UrlMatcher`} for the specified pattern.
1384
- *
1385
- * @param {string} pattern The URL pattern.
1386
- * @param {Object} config The config object hash.
1387
- * @returns {UrlMatcher} The UrlMatcher.
1388
- */
1389
- this.compile = function (pattern, config) {
1390
- return new UrlMatcher(pattern, extend(getDefaultConfig(), config));
1391
- };
1392
-
1393
- /**
1394
- * @ngdoc function
1395
- * @name ui.router.util.$urlMatcherFactory#isMatcher
1396
- * @methodOf ui.router.util.$urlMatcherFactory
1397
- *
1398
- * @description
1399
- * Returns true if the specified object is a `UrlMatcher`, or false otherwise.
1400
- *
1401
- * @param {Object} object The object to perform the type check against.
1402
- * @returns {Boolean} Returns `true` if the object matches the `UrlMatcher` interface, by
1403
- * implementing all the same methods.
1404
- */
1405
- this.isMatcher = function (o) {
1406
- if (!isObject(o)) return false;
1407
- var result = true;
1408
-
1409
- forEach(UrlMatcher.prototype, function(val, name) {
1410
- if (isFunction(val)) {
1411
- result = result && (isDefined(o[name]) && isFunction(o[name]));
1412
- }
1413
- });
1414
- return result;
1415
- };
1416
-
1417
- /**
1418
- * @ngdoc function
1419
- * @name ui.router.util.$urlMatcherFactory#type
1420
- * @methodOf ui.router.util.$urlMatcherFactory
1421
- *
1422
- * @description
1423
- * Registers a custom {@link ui.router.util.type:Type `Type`} object that can be used to
1424
- * generate URLs with typed parameters.
1425
- *
1426
- * @param {string} name The type name.
1427
- * @param {Object|Function} definition The type definition. See
1428
- * {@link ui.router.util.type:Type `Type`} for information on the values accepted.
1429
- * @param {Object|Function} definitionFn (optional) A function that is injected before the app
1430
- * runtime starts. The result of this function is merged into the existing `definition`.
1431
- * See {@link ui.router.util.type:Type `Type`} for information on the values accepted.
1432
- *
1433
- * @returns {Object} Returns `$urlMatcherFactoryProvider`.
1434
- *
1435
- * @example
1436
- * This is a simple example of a custom type that encodes and decodes items from an
1437
- * array, using the array index as the URL-encoded value:
1438
- *
1439
- * <pre>
1440
- * var list = ['John', 'Paul', 'George', 'Ringo'];
1441
- *
1442
- * $urlMatcherFactoryProvider.type('listItem', {
1443
- * encode: function(item) {
1444
- * // Represent the list item in the URL using its corresponding index
1445
- * return list.indexOf(item);
1446
- * },
1447
- * decode: function(item) {
1448
- * // Look up the list item by index
1449
- * return list[parseInt(item, 10)];
1450
- * },
1451
- * is: function(item) {
1452
- * // Ensure the item is valid by checking to see that it appears
1453
- * // in the list
1454
- * return list.indexOf(item) > -1;
1455
- * }
1456
- * });
1457
- *
1458
- * $stateProvider.state('list', {
1459
- * url: "/list/{item:listItem}",
1460
- * controller: function($scope, $stateParams) {
1461
- * console.log($stateParams.item);
1462
- * }
1463
- * });
1464
- *
1465
- * // ...
1466
- *
1467
- * // Changes URL to '/list/3', logs "Ringo" to the console
1468
- * $state.go('list', { item: "Ringo" });
1469
- * </pre>
1470
- *
1471
- * This is a more complex example of a type that relies on dependency injection to
1472
- * interact with services, and uses the parameter name from the URL to infer how to
1473
- * handle encoding and decoding parameter values:
1474
- *
1475
- * <pre>
1476
- * // Defines a custom type that gets a value from a service,
1477
- * // where each service gets different types of values from
1478
- * // a backend API:
1479
- * $urlMatcherFactoryProvider.type('dbObject', {}, function(Users, Posts) {
1480
- *
1481
- * // Matches up services to URL parameter names
1482
- * var services = {
1483
- * user: Users,
1484
- * post: Posts
1485
- * };
1486
- *
1487
- * return {
1488
- * encode: function(object) {
1489
- * // Represent the object in the URL using its unique ID
1490
- * return object.id;
1491
- * },
1492
- * decode: function(value, key) {
1493
- * // Look up the object by ID, using the parameter
1494
- * // name (key) to call the correct service
1495
- * return services[key].findById(value);
1496
- * },
1497
- * is: function(object, key) {
1498
- * // Check that object is a valid dbObject
1499
- * return angular.isObject(object) && object.id && services[key];
1500
- * }
1501
- * equals: function(a, b) {
1502
- * // Check the equality of decoded objects by comparing
1503
- * // their unique IDs
1504
- * return a.id === b.id;
1505
- * }
1506
- * };
1507
- * });
1508
- *
1509
- * // In a config() block, you can then attach URLs with
1510
- * // type-annotated parameters:
1511
- * $stateProvider.state('users', {
1512
- * url: "/users",
1513
- * // ...
1514
- * }).state('users.item', {
1515
- * url: "/{user:dbObject}",
1516
- * controller: function($scope, $stateParams) {
1517
- * // $stateParams.user will now be an object returned from
1518
- * // the Users service
1519
- * },
1520
- * // ...
1521
- * });
1522
- * </pre>
1523
- */
1524
- this.type = function (name, definition, definitionFn) {
1525
- if (!isDefined(definition)) return $types[name];
1526
- if ($types.hasOwnProperty(name)) throw new Error("A type named '" + name + "' has already been defined.");
1527
-
1528
- $types[name] = new Type(extend({ name: name }, definition));
1529
- if (definitionFn) {
1530
- typeQueue.push({ name: name, def: definitionFn });
1531
- if (!enqueue) flushTypeQueue();
1532
- }
1533
- return this;
1534
- };
1535
-
1536
- // `flushTypeQueue()` waits until `$urlMatcherFactory` is injected before invoking the queued `definitionFn`s
1537
- function flushTypeQueue() {
1538
- while(typeQueue.length) {
1539
- var type = typeQueue.shift();
1540
- if (type.pattern) throw new Error("You cannot override a type's .pattern at runtime.");
1541
- angular.extend($types[type.name], injector.invoke(type.def));
1542
- }
1543
- }
1544
-
1545
- // Register default types. Store them in the prototype of $types.
1546
- forEach(defaultTypes, function(type, name) { $types[name] = new Type(extend({name: name}, type)); });
1547
- $types = inherit($types, {});
1548
-
1549
- /* No need to document $get, since it returns this */
1550
- this.$get = ['$injector', function ($injector) {
1551
- injector = $injector;
1552
- enqueue = false;
1553
- flushTypeQueue();
1554
-
1555
- forEach(defaultTypes, function(type, name) {
1556
- if (!$types[name]) $types[name] = new Type(type);
1557
- });
1558
- return this;
1559
- }];
1560
-
1561
- this.Param = function Param(id, type, config, location) {
1562
- var self = this;
1563
- config = unwrapShorthand(config);
1564
- type = getType(config, type, location);
1565
- var arrayMode = getArrayMode();
1566
- type = arrayMode ? type.$asArray(arrayMode, location === "search") : type;
1567
- if (type.name === "string" && !arrayMode && location === "path" && config.value === undefined)
1568
- config.value = ""; // for 0.2.x; in 0.3.0+ do not automatically default to ""
1569
- var isOptional = config.value !== undefined;
1570
- var squash = getSquashPolicy(config, isOptional);
1571
- var replace = getReplace(config, arrayMode, isOptional, squash);
1572
-
1573
- function unwrapShorthand(config) {
1574
- var keys = isObject(config) ? objectKeys(config) : [];
1575
- var isShorthand = indexOf(keys, "value") === -1 && indexOf(keys, "type") === -1 &&
1576
- indexOf(keys, "squash") === -1 && indexOf(keys, "array") === -1;
1577
- if (isShorthand) config = { value: config };
1578
- config.$$fn = isInjectable(config.value) ? config.value : function () { return config.value; };
1579
- return config;
1580
- }
1581
-
1582
- function getType(config, urlType, location) {
1583
- if (config.type && urlType) throw new Error("Param '"+id+"' has two type configurations.");
1584
- if (urlType) return urlType;
1585
- if (!config.type) return (location === "config" ? $types.any : $types.string);
1586
- return config.type instanceof Type ? config.type : new Type(config.type);
1587
- }
1588
-
1589
- // array config: param name (param[]) overrides default settings. explicit config overrides param name.
1590
- function getArrayMode() {
1591
- var arrayDefaults = { array: (location === "search" ? "auto" : false) };
1592
- var arrayParamNomenclature = id.match(/\[\]$/) ? { array: true } : {};
1593
- return extend(arrayDefaults, arrayParamNomenclature, config).array;
1594
- }
1595
-
1596
- /**
1597
- * returns false, true, or the squash value to indicate the "default parameter url squash policy".
1598
- */
1599
- function getSquashPolicy(config, isOptional) {
1600
- var squash = config.squash;
1601
- if (!isOptional || squash === false) return false;
1602
- if (!isDefined(squash) || squash == null) return defaultSquashPolicy;
1603
- if (squash === true || isString(squash)) return squash;
1604
- throw new Error("Invalid squash policy: '" + squash + "'. Valid policies: false, true, or arbitrary string");
1605
- }
1606
-
1607
- function getReplace(config, arrayMode, isOptional, squash) {
1608
- var replace, configuredKeys, defaultPolicy = [
1609
- { from: "", to: (isOptional || arrayMode ? undefined : "") },
1610
- { from: null, to: (isOptional || arrayMode ? undefined : "") }
1611
- ];
1612
- replace = isArray(config.replace) ? config.replace : [];
1613
- if (isString(squash))
1614
- replace.push({ from: squash, to: undefined });
1615
- configuredKeys = map(replace, function(item) { return item.from; } );
1616
- return filter(defaultPolicy, function(item) { return indexOf(configuredKeys, item.from) === -1; }).concat(replace);
1617
- }
1618
-
1619
- /**
1620
- * [Internal] Get the default value of a parameter, which may be an injectable function.
1621
- */
1622
- function $$getDefaultValue() {
1623
- if (!injector) throw new Error("Injectable functions cannot be called at configuration time");
1624
- var defaultValue = injector.invoke(config.$$fn);
1625
- if (defaultValue !== null && defaultValue !== undefined && !self.type.is(defaultValue))
1626
- throw new Error("Default value (" + defaultValue + ") for parameter '" + self.id + "' is not an instance of Type (" + self.type.name + ")");
1627
- return defaultValue;
1628
- }
1629
-
1630
- /**
1631
- * [Internal] Gets the decoded representation of a value if the value is defined, otherwise, returns the
1632
- * default value, which may be the result of an injectable function.
1633
- */
1634
- function $value(value) {
1635
- function hasReplaceVal(val) { return function(obj) { return obj.from === val; }; }
1636
- function $replace(value) {
1637
- var replacement = map(filter(self.replace, hasReplaceVal(value)), function(obj) { return obj.to; });
1638
- return replacement.length ? replacement[0] : value;
1639
- }
1640
- value = $replace(value);
1641
- return !isDefined(value) ? $$getDefaultValue() : self.type.$normalize(value);
1642
- }
1643
-
1644
- function toString() { return "{Param:" + id + " " + type + " squash: '" + squash + "' optional: " + isOptional + "}"; }
1645
-
1646
- extend(this, {
1647
- id: id,
1648
- type: type,
1649
- location: location,
1650
- array: arrayMode,
1651
- squash: squash,
1652
- replace: replace,
1653
- isOptional: isOptional,
1654
- value: $value,
1655
- dynamic: undefined,
1656
- config: config,
1657
- toString: toString
1658
- });
1659
- };
1660
-
1661
- function ParamSet(params) {
1662
- extend(this, params || {});
1663
- }
1664
-
1665
- ParamSet.prototype = {
1666
- $$new: function() {
1667
- return inherit(this, extend(new ParamSet(), { $$parent: this}));
1668
- },
1669
- $$keys: function () {
1670
- var keys = [], chain = [], parent = this,
1671
- ignore = objectKeys(ParamSet.prototype);
1672
- while (parent) { chain.push(parent); parent = parent.$$parent; }
1673
- chain.reverse();
1674
- forEach(chain, function(paramset) {
1675
- forEach(objectKeys(paramset), function(key) {
1676
- if (indexOf(keys, key) === -1 && indexOf(ignore, key) === -1) keys.push(key);
1677
- });
1678
- });
1679
- return keys;
1680
- },
1681
- $$values: function(paramValues) {
1682
- var values = {}, self = this;
1683
- forEach(self.$$keys(), function(key) {
1684
- values[key] = self[key].value(paramValues && paramValues[key]);
1685
- });
1686
- return values;
1687
- },
1688
- $$equals: function(paramValues1, paramValues2) {
1689
- var equal = true, self = this;
1690
- forEach(self.$$keys(), function(key) {
1691
- var left = paramValues1 && paramValues1[key], right = paramValues2 && paramValues2[key];
1692
- if (!self[key].type.equals(left, right)) equal = false;
1693
- });
1694
- return equal;
1695
- },
1696
- $$validates: function $$validate(paramValues) {
1697
- var keys = this.$$keys(), i, param, rawVal, normalized, encoded;
1698
- for (i = 0; i < keys.length; i++) {
1699
- param = this[keys[i]];
1700
- rawVal = paramValues[keys[i]];
1701
- if ((rawVal === undefined || rawVal === null) && param.isOptional)
1702
- break; // There was no parameter value, but the param is optional
1703
- normalized = param.type.$normalize(rawVal);
1704
- if (!param.type.is(normalized))
1705
- return false; // The value was not of the correct Type, and could not be decoded to the correct Type
1706
- encoded = param.type.encode(normalized);
1707
- if (angular.isString(encoded) && !param.type.pattern.exec(encoded))
1708
- return false; // The value was of the correct type, but when encoded, did not match the Type's regexp
1709
- }
1710
- return true;
1711
- },
1712
- $$parent: undefined
1713
- };
1714
-
1715
- this.ParamSet = ParamSet;
1716
- }
1717
-
1718
- // Register as a provider so it's available to other providers
1719
- angular.module('ui.router.util').provider('$urlMatcherFactory', $UrlMatcherFactory);
1720
- angular.module('ui.router.util').run(['$urlMatcherFactory', function($urlMatcherFactory) { }]);
1721
-
1722
- /**
1723
- * @ngdoc object
1724
- * @name ui.router.router.$urlRouterProvider
1725
- *
1726
- * @requires ui.router.util.$urlMatcherFactoryProvider
1727
- * @requires $locationProvider
1728
- *
1729
- * @description
1730
- * `$urlRouterProvider` has the responsibility of watching `$location`.
1731
- * When `$location` changes it runs through a list of rules one by one until a
1732
- * match is found. `$urlRouterProvider` is used behind the scenes anytime you specify
1733
- * a url in a state configuration. All urls are compiled into a UrlMatcher object.
1734
- *
1735
- * There are several methods on `$urlRouterProvider` that make it useful to use directly
1736
- * in your module config.
1737
- */
1738
- $UrlRouterProvider.$inject = ['$locationProvider', '$urlMatcherFactoryProvider'];
1739
- function $UrlRouterProvider( $locationProvider, $urlMatcherFactory) {
1740
- var rules = [], otherwise = null, interceptDeferred = false, listener;
1741
-
1742
- // Returns a string that is a prefix of all strings matching the RegExp
1743
- function regExpPrefix(re) {
1744
- var prefix = /^\^((?:\\[^a-zA-Z0-9]|[^\\\[\]\^$*+?.()|{}]+)*)/.exec(re.source);
1745
- return (prefix != null) ? prefix[1].replace(/\\(.)/g, "$1") : '';
1746
- }
1747
-
1748
- // Interpolates matched values into a String.replace()-style pattern
1749
- function interpolate(pattern, match) {
1750
- return pattern.replace(/\$(\$|\d{1,2})/, function (m, what) {
1751
- return match[what === '$' ? 0 : Number(what)];
1752
- });
1753
- }
1754
-
1755
- /**
1756
- * @ngdoc function
1757
- * @name ui.router.router.$urlRouterProvider#rule
1758
- * @methodOf ui.router.router.$urlRouterProvider
1759
- *
1760
- * @description
1761
- * Defines rules that are used by `$urlRouterProvider` to find matches for
1762
- * specific URLs.
1763
- *
1764
- * @example
1765
- * <pre>
1766
- * var app = angular.module('app', ['ui.router.router']);
1767
- *
1768
- * app.config(function ($urlRouterProvider) {
1769
- * // Here's an example of how you might allow case insensitive urls
1770
- * $urlRouterProvider.rule(function ($injector, $location) {
1771
- * var path = $location.path(),
1772
- * normalized = path.toLowerCase();
1773
- *
1774
- * if (path !== normalized) {
1775
- * return normalized;
1776
- * }
1777
- * });
1778
- * });
1779
- * </pre>
1780
- *
1781
- * @param {object} rule Handler function that takes `$injector` and `$location`
1782
- * services as arguments. You can use them to return a valid path as a string.
1783
- *
1784
- * @return {object} `$urlRouterProvider` - `$urlRouterProvider` instance
1785
- */
1786
- this.rule = function (rule) {
1787
- if (!isFunction(rule)) throw new Error("'rule' must be a function");
1788
- rules.push(rule);
1789
- return this;
1790
- };
1791
-
1792
- /**
1793
- * @ngdoc object
1794
- * @name ui.router.router.$urlRouterProvider#otherwise
1795
- * @methodOf ui.router.router.$urlRouterProvider
1796
- *
1797
- * @description
1798
- * Defines a path that is used when an invalid route is requested.
1799
- *
1800
- * @example
1801
- * <pre>
1802
- * var app = angular.module('app', ['ui.router.router']);
1803
- *
1804
- * app.config(function ($urlRouterProvider) {
1805
- * // if the path doesn't match any of the urls you configured
1806
- * // otherwise will take care of routing the user to the
1807
- * // specified url
1808
- * $urlRouterProvider.otherwise('/index');
1809
- *
1810
- * // Example of using function rule as param
1811
- * $urlRouterProvider.otherwise(function ($injector, $location) {
1812
- * return '/a/valid/url';
1813
- * });
1814
- * });
1815
- * </pre>
1816
- *
1817
- * @param {string|object} rule The url path you want to redirect to or a function
1818
- * rule that returns the url path. The function version is passed two params:
1819
- * `$injector` and `$location` services, and must return a url string.
1820
- *
1821
- * @return {object} `$urlRouterProvider` - `$urlRouterProvider` instance
1822
- */
1823
- this.otherwise = function (rule) {
1824
- if (isString(rule)) {
1825
- var redirect = rule;
1826
- rule = function () { return redirect; };
1827
- }
1828
- else if (!isFunction(rule)) throw new Error("'rule' must be a function");
1829
- otherwise = rule;
1830
- return this;
1831
- };
1832
-
1833
-
1834
- function handleIfMatch($injector, handler, match) {
1835
- if (!match) return false;
1836
- var result = $injector.invoke(handler, handler, { $match: match });
1837
- return isDefined(result) ? result : true;
1838
- }
1839
-
1840
- /**
1841
- * @ngdoc function
1842
- * @name ui.router.router.$urlRouterProvider#when
1843
- * @methodOf ui.router.router.$urlRouterProvider
1844
- *
1845
- * @description
1846
- * Registers a handler for a given url matching. if handle is a string, it is
1847
- * treated as a redirect, and is interpolated according to the syntax of match
1848
- * (i.e. like `String.replace()` for `RegExp`, or like a `UrlMatcher` pattern otherwise).
1849
- *
1850
- * If the handler is a function, it is injectable. It gets invoked if `$location`
1851
- * matches. You have the option of inject the match object as `$match`.
1852
- *
1853
- * The handler can return
1854
- *
1855
- * - **falsy** to indicate that the rule didn't match after all, then `$urlRouter`
1856
- * will continue trying to find another one that matches.
1857
- * - **string** which is treated as a redirect and passed to `$location.url()`
1858
- * - **void** or any **truthy** value tells `$urlRouter` that the url was handled.
1859
- *
1860
- * @example
1861
- * <pre>
1862
- * var app = angular.module('app', ['ui.router.router']);
1863
- *
1864
- * app.config(function ($urlRouterProvider) {
1865
- * $urlRouterProvider.when($state.url, function ($match, $stateParams) {
1866
- * if ($state.$current.navigable !== state ||
1867
- * !equalForKeys($match, $stateParams) {
1868
- * $state.transitionTo(state, $match, false);
1869
- * }
1870
- * });
1871
- * });
1872
- * </pre>
1873
- *
1874
- * @param {string|object} what The incoming path that you want to redirect.
1875
- * @param {string|object} handler The path you want to redirect your user to.
1876
- */
1877
- this.when = function (what, handler) {
1878
- var redirect, handlerIsString = isString(handler);
1879
- if (isString(what)) what = $urlMatcherFactory.compile(what);
1880
-
1881
- if (!handlerIsString && !isFunction(handler) && !isArray(handler))
1882
- throw new Error("invalid 'handler' in when()");
1883
-
1884
- var strategies = {
1885
- matcher: function (what, handler) {
1886
- if (handlerIsString) {
1887
- redirect = $urlMatcherFactory.compile(handler);
1888
- handler = ['$match', function ($match) { return redirect.format($match); }];
1889
- }
1890
- return extend(function ($injector, $location) {
1891
- return handleIfMatch($injector, handler, what.exec($location.path(), $location.search()));
1892
- }, {
1893
- prefix: isString(what.prefix) ? what.prefix : ''
1894
- });
1895
- },
1896
- regex: function (what, handler) {
1897
- if (what.global || what.sticky) throw new Error("when() RegExp must not be global or sticky");
1898
-
1899
- if (handlerIsString) {
1900
- redirect = handler;
1901
- handler = ['$match', function ($match) { return interpolate(redirect, $match); }];
1902
- }
1903
- return extend(function ($injector, $location) {
1904
- return handleIfMatch($injector, handler, what.exec($location.path()));
1905
- }, {
1906
- prefix: regExpPrefix(what)
1907
- });
1908
- }
1909
- };
1910
-
1911
- var check = { matcher: $urlMatcherFactory.isMatcher(what), regex: what instanceof RegExp };
1912
-
1913
- for (var n in check) {
1914
- if (check[n]) return this.rule(strategies[n](what, handler));
1915
- }
1916
-
1917
- throw new Error("invalid 'what' in when()");
1918
- };
1919
-
1920
- /**
1921
- * @ngdoc function
1922
- * @name ui.router.router.$urlRouterProvider#deferIntercept
1923
- * @methodOf ui.router.router.$urlRouterProvider
1924
- *
1925
- * @description
1926
- * Disables (or enables) deferring location change interception.
1927
- *
1928
- * If you wish to customize the behavior of syncing the URL (for example, if you wish to
1929
- * defer a transition but maintain the current URL), call this method at configuration time.
1930
- * Then, at run time, call `$urlRouter.listen()` after you have configured your own
1931
- * `$locationChangeSuccess` event handler.
1932
- *
1933
- * @example
1934
- * <pre>
1935
- * var app = angular.module('app', ['ui.router.router']);
1936
- *
1937
- * app.config(function ($urlRouterProvider) {
1938
- *
1939
- * // Prevent $urlRouter from automatically intercepting URL changes;
1940
- * // this allows you to configure custom behavior in between
1941
- * // location changes and route synchronization:
1942
- * $urlRouterProvider.deferIntercept();
1943
- *
1944
- * }).run(function ($rootScope, $urlRouter, UserService) {
1945
- *
1946
- * $rootScope.$on('$locationChangeSuccess', function(e) {
1947
- * // UserService is an example service for managing user state
1948
- * if (UserService.isLoggedIn()) return;
1949
- *
1950
- * // Prevent $urlRouter's default handler from firing
1951
- * e.preventDefault();
1952
- *
1953
- * UserService.handleLogin().then(function() {
1954
- * // Once the user has logged in, sync the current URL
1955
- * // to the router:
1956
- * $urlRouter.sync();
1957
- * });
1958
- * });
1959
- *
1960
- * // Configures $urlRouter's listener *after* your custom listener
1961
- * $urlRouter.listen();
1962
- * });
1963
- * </pre>
1964
- *
1965
- * @param {boolean} defer Indicates whether to defer location change interception. Passing
1966
- no parameter is equivalent to `true`.
1967
- */
1968
- this.deferIntercept = function (defer) {
1969
- if (defer === undefined) defer = true;
1970
- interceptDeferred = defer;
1971
- };
1972
-
1973
- /**
1974
- * @ngdoc object
1975
- * @name ui.router.router.$urlRouter
1976
- *
1977
- * @requires $location
1978
- * @requires $rootScope
1979
- * @requires $injector
1980
- * @requires $browser
1981
- *
1982
- * @description
1983
- *
1984
- */
1985
- this.$get = $get;
1986
- $get.$inject = ['$location', '$rootScope', '$injector', '$browser'];
1987
- function $get( $location, $rootScope, $injector, $browser) {
1988
-
1989
- var baseHref = $browser.baseHref(), location = $location.url(), lastPushedUrl;
1990
-
1991
- function appendBasePath(url, isHtml5, absolute) {
1992
- if (baseHref === '/') return url;
1993
- if (isHtml5) return baseHref.slice(0, -1) + url;
1994
- if (absolute) return baseHref.slice(1) + url;
1995
- return url;
1996
- }
1997
-
1998
- // TODO: Optimize groups of rules with non-empty prefix into some sort of decision tree
1999
- function update(evt) {
2000
- if (evt && evt.defaultPrevented) return;
2001
- var ignoreUpdate = lastPushedUrl && $location.url() === lastPushedUrl;
2002
- lastPushedUrl = undefined;
2003
- // TODO: Re-implement this in 1.0 for https://github.com/angular-ui/ui-router/issues/1573
2004
- //if (ignoreUpdate) return true;
2005
-
2006
- function check(rule) {
2007
- var handled = rule($injector, $location);
2008
-
2009
- if (!handled) return false;
2010
- if (isString(handled)) $location.replace().url(handled);
2011
- return true;
2012
- }
2013
- var n = rules.length, i;
2014
-
2015
- for (i = 0; i < n; i++) {
2016
- if (check(rules[i])) return;
2017
- }
2018
- // always check otherwise last to allow dynamic updates to the set of rules
2019
- if (otherwise) check(otherwise);
2020
- }
2021
-
2022
- function listen() {
2023
- listener = listener || $rootScope.$on('$locationChangeSuccess', update);
2024
- return listener;
2025
- }
2026
-
2027
- if (!interceptDeferred) listen();
2028
-
2029
- return {
2030
- /**
2031
- * @ngdoc function
2032
- * @name ui.router.router.$urlRouter#sync
2033
- * @methodOf ui.router.router.$urlRouter
2034
- *
2035
- * @description
2036
- * Triggers an update; the same update that happens when the address bar url changes, aka `$locationChangeSuccess`.
2037
- * This method is useful when you need to use `preventDefault()` on the `$locationChangeSuccess` event,
2038
- * perform some custom logic (route protection, auth, config, redirection, etc) and then finally proceed
2039
- * with the transition by calling `$urlRouter.sync()`.
2040
- *
2041
- * @example
2042
- * <pre>
2043
- * angular.module('app', ['ui.router'])
2044
- * .run(function($rootScope, $urlRouter) {
2045
- * $rootScope.$on('$locationChangeSuccess', function(evt) {
2046
- * // Halt state change from even starting
2047
- * evt.preventDefault();
2048
- * // Perform custom logic
2049
- * var meetsRequirement = ...
2050
- * // Continue with the update and state transition if logic allows
2051
- * if (meetsRequirement) $urlRouter.sync();
2052
- * });
2053
- * });
2054
- * </pre>
2055
- */
2056
- sync: function() {
2057
- update();
2058
- },
2059
-
2060
- listen: function() {
2061
- return listen();
2062
- },
2063
-
2064
- update: function(read) {
2065
- if (read) {
2066
- location = $location.url();
2067
- return;
2068
- }
2069
- if ($location.url() === location) return;
2070
-
2071
- $location.url(location);
2072
- $location.replace();
2073
- },
2074
-
2075
- push: function(urlMatcher, params, options) {
2076
- var url = urlMatcher.format(params || {});
2077
-
2078
- // Handle the special hash param, if needed
2079
- if (url !== null && params && params['#']) {
2080
- url += '#' + params['#'];
2081
- }
2082
-
2083
- $location.url(url);
2084
- lastPushedUrl = options && options.$$avoidResync ? $location.url() : undefined;
2085
- if (options && options.replace) $location.replace();
2086
- },
2087
-
2088
- /**
2089
- * @ngdoc function
2090
- * @name ui.router.router.$urlRouter#href
2091
- * @methodOf ui.router.router.$urlRouter
2092
- *
2093
- * @description
2094
- * A URL generation method that returns the compiled URL for a given
2095
- * {@link ui.router.util.type:UrlMatcher `UrlMatcher`}, populated with the provided parameters.
2096
- *
2097
- * @example
2098
- * <pre>
2099
- * $bob = $urlRouter.href(new UrlMatcher("/about/:person"), {
2100
- * person: "bob"
2101
- * });
2102
- * // $bob == "/about/bob";
2103
- * </pre>
2104
- *
2105
- * @param {UrlMatcher} urlMatcher The `UrlMatcher` object which is used as the template of the URL to generate.
2106
- * @param {object=} params An object of parameter values to fill the matcher's required parameters.
2107
- * @param {object=} options Options object. The options are:
2108
- *
2109
- * - **`absolute`** - {boolean=false}, If true will generate an absolute url, e.g. "http://www.example.com/fullurl".
2110
- *
2111
- * @returns {string} Returns the fully compiled URL, or `null` if `params` fail validation against `urlMatcher`
2112
- */
2113
- href: function(urlMatcher, params, options) {
2114
- if (!urlMatcher.validates(params)) return null;
2115
-
2116
- var isHtml5 = $locationProvider.html5Mode();
2117
- if (angular.isObject(isHtml5)) {
2118
- isHtml5 = isHtml5.enabled;
2119
- }
2120
-
2121
- var url = urlMatcher.format(params);
2122
- options = options || {};
2123
-
2124
- if (!isHtml5 && url !== null) {
2125
- url = "#" + $locationProvider.hashPrefix() + url;
2126
- }
2127
-
2128
- // Handle special hash param, if needed
2129
- if (url !== null && params && params['#']) {
2130
- url += '#' + params['#'];
2131
- }
2132
-
2133
- url = appendBasePath(url, isHtml5, options.absolute);
2134
-
2135
- if (!options.absolute || !url) {
2136
- return url;
2137
- }
2138
-
2139
- var slash = (!isHtml5 && url ? '/' : ''), port = $location.port();
2140
- port = (port === 80 || port === 443 ? '' : ':' + port);
2141
-
2142
- return [$location.protocol(), '://', $location.host(), port, slash, url].join('');
2143
- }
2144
- };
2145
- }
2146
- }
2147
-
2148
- angular.module('ui.router.router').provider('$urlRouter', $UrlRouterProvider);
2149
-
2150
- /**
2151
- * @ngdoc object
2152
- * @name ui.router.state.$stateProvider
2153
- *
2154
- * @requires ui.router.router.$urlRouterProvider
2155
- * @requires ui.router.util.$urlMatcherFactoryProvider
2156
- *
2157
- * @description
2158
- * The new `$stateProvider` works similar to Angular's v1 router, but it focuses purely
2159
- * on state.
2160
- *
2161
- * A state corresponds to a "place" in the application in terms of the overall UI and
2162
- * navigation. A state describes (via the controller / template / view properties) what
2163
- * the UI looks like and does at that place.
2164
- *
2165
- * States often have things in common, and the primary way of factoring out these
2166
- * commonalities in this model is via the state hierarchy, i.e. parent/child states aka
2167
- * nested states.
2168
- *
2169
- * The `$stateProvider` provides interfaces to declare these states for your app.
2170
- */
2171
- $StateProvider.$inject = ['$urlRouterProvider', '$urlMatcherFactoryProvider'];
2172
- function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
2173
-
2174
- var root, states = {}, $state, queue = {}, abstractKey = 'abstract';
2175
-
2176
- // Builds state properties from definition passed to registerState()
2177
- var stateBuilder = {
2178
-
2179
- // Derive parent state from a hierarchical name only if 'parent' is not explicitly defined.
2180
- // state.children = [];
2181
- // if (parent) parent.children.push(state);
2182
- parent: function(state) {
2183
- if (isDefined(state.parent) && state.parent) return findState(state.parent);
2184
- // regex matches any valid composite state name
2185
- // would match "contact.list" but not "contacts"
2186
- var compositeName = /^(.+)\.[^.]+$/.exec(state.name);
2187
- return compositeName ? findState(compositeName[1]) : root;
2188
- },
2189
-
2190
- // inherit 'data' from parent and override by own values (if any)
2191
- data: function(state) {
2192
- if (state.parent && state.parent.data) {
2193
- state.data = state.self.data = extend({}, state.parent.data, state.data);
2194
- }
2195
- return state.data;
2196
- },
2197
-
2198
- // Build a URLMatcher if necessary, either via a relative or absolute URL
2199
- url: function(state) {
2200
- var url = state.url, config = { params: state.params || {} };
2201
-
2202
- if (isString(url)) {
2203
- if (url.charAt(0) == '^') return $urlMatcherFactory.compile(url.substring(1), config);
2204
- return (state.parent.navigable || root).url.concat(url, config);
2205
- }
2206
-
2207
- if (!url || $urlMatcherFactory.isMatcher(url)) return url;
2208
- throw new Error("Invalid url '" + url + "' in state '" + state + "'");
2209
- },
2210
-
2211
- // Keep track of the closest ancestor state that has a URL (i.e. is navigable)
2212
- navigable: function(state) {
2213
- return state.url ? state : (state.parent ? state.parent.navigable : null);
2214
- },
2215
-
2216
- // Own parameters for this state. state.url.params is already built at this point. Create and add non-url params
2217
- ownParams: function(state) {
2218
- var params = state.url && state.url.params || new $$UMFP.ParamSet();
2219
- forEach(state.params || {}, function(config, id) {
2220
- if (!params[id]) params[id] = new $$UMFP.Param(id, null, config, "config");
2221
- });
2222
- return params;
2223
- },
2224
-
2225
- // Derive parameters for this state and ensure they're a super-set of parent's parameters
2226
- params: function(state) {
2227
- return state.parent && state.parent.params ? extend(state.parent.params.$$new(), state.ownParams) : new $$UMFP.ParamSet();
2228
- },
2229
-
2230
- // If there is no explicit multi-view configuration, make one up so we don't have
2231
- // to handle both cases in the view directive later. Note that having an explicit
2232
- // 'views' property will mean the default unnamed view properties are ignored. This
2233
- // is also a good time to resolve view names to absolute names, so everything is a
2234
- // straight lookup at link time.
2235
- views: function(state) {
2236
- var views = {};
2237
-
2238
- forEach(isDefined(state.views) ? state.views : { '': state }, function (view, name) {
2239
- if (name.indexOf('@') < 0) name += '@' + state.parent.name;
2240
- views[name] = view;
2241
- });
2242
- return views;
2243
- },
2244
-
2245
- // Keep a full path from the root down to this state as this is needed for state activation.
2246
- path: function(state) {
2247
- return state.parent ? state.parent.path.concat(state) : []; // exclude root from path
2248
- },
2249
-
2250
- // Speed up $state.contains() as it's used a lot
2251
- includes: function(state) {
2252
- var includes = state.parent ? extend({}, state.parent.includes) : {};
2253
- includes[state.name] = true;
2254
- return includes;
2255
- },
2256
-
2257
- $delegates: {}
2258
- };
2259
-
2260
- function isRelative(stateName) {
2261
- return stateName.indexOf(".") === 0 || stateName.indexOf("^") === 0;
2262
- }
2263
-
2264
- function findState(stateOrName, base) {
2265
- if (!stateOrName) return undefined;
2266
-
2267
- var isStr = isString(stateOrName),
2268
- name = isStr ? stateOrName : stateOrName.name,
2269
- path = isRelative(name);
2270
-
2271
- if (path) {
2272
- if (!base) throw new Error("No reference point given for path '" + name + "'");
2273
- base = findState(base);
2274
-
2275
- var rel = name.split("."), i = 0, pathLength = rel.length, current = base;
2276
-
2277
- for (; i < pathLength; i++) {
2278
- if (rel[i] === "" && i === 0) {
2279
- current = base;
2280
- continue;
2281
- }
2282
- if (rel[i] === "^") {
2283
- if (!current.parent) throw new Error("Path '" + name + "' not valid for state '" + base.name + "'");
2284
- current = current.parent;
2285
- continue;
2286
- }
2287
- break;
2288
- }
2289
- rel = rel.slice(i).join(".");
2290
- name = current.name + (current.name && rel ? "." : "") + rel;
2291
- }
2292
- var state = states[name];
2293
-
2294
- if (state && (isStr || (!isStr && (state === stateOrName || state.self === stateOrName)))) {
2295
- return state;
2296
- }
2297
- return undefined;
2298
- }
2299
-
2300
- function queueState(parentName, state) {
2301
- if (!queue[parentName]) {
2302
- queue[parentName] = [];
2303
- }
2304
- queue[parentName].push(state);
2305
- }
2306
-
2307
- function flushQueuedChildren(parentName) {
2308
- var queued = queue[parentName] || [];
2309
- while(queued.length) {
2310
- registerState(queued.shift());
2311
- }
2312
- }
2313
-
2314
- function registerState(state) {
2315
- // Wrap a new object around the state so we can store our private details easily.
2316
- state = inherit(state, {
2317
- self: state,
2318
- resolve: state.resolve || {},
2319
- toString: function() { return this.name; }
2320
- });
2321
-
2322
- var name = state.name;
2323
- if (!isString(name) || name.indexOf('@') >= 0) throw new Error("State must have a valid name");
2324
- if (states.hasOwnProperty(name)) throw new Error("State '" + name + "'' is already defined");
2325
-
2326
- // Get parent name
2327
- var parentName = (name.indexOf('.') !== -1) ? name.substring(0, name.lastIndexOf('.'))
2328
- : (isString(state.parent)) ? state.parent
2329
- : (isObject(state.parent) && isString(state.parent.name)) ? state.parent.name
2330
- : '';
2331
-
2332
- // If parent is not registered yet, add state to queue and register later
2333
- if (parentName && !states[parentName]) {
2334
- return queueState(parentName, state.self);
2335
- }
2336
-
2337
- for (var key in stateBuilder) {
2338
- if (isFunction(stateBuilder[key])) state[key] = stateBuilder[key](state, stateBuilder.$delegates[key]);
2339
- }
2340
- states[name] = state;
2341
-
2342
- // Register the state in the global state list and with $urlRouter if necessary.
2343
- if (!state[abstractKey] && state.url) {
2344
- $urlRouterProvider.when(state.url, ['$match', '$stateParams', function ($match, $stateParams) {
2345
- if ($state.$current.navigable != state || !equalForKeys($match, $stateParams)) {
2346
- $state.transitionTo(state, $match, { inherit: true, location: false });
2347
- }
2348
- }]);
2349
- }
2350
-
2351
- // Register any queued children
2352
- flushQueuedChildren(name);
2353
-
2354
- return state;
2355
- }
2356
-
2357
- // Checks text to see if it looks like a glob.
2358
- function isGlob (text) {
2359
- return text.indexOf('*') > -1;
2360
- }
2361
-
2362
- // Returns true if glob matches current $state name.
2363
- function doesStateMatchGlob (glob) {
2364
- var globSegments = glob.split('.'),
2365
- segments = $state.$current.name.split('.');
2366
-
2367
- //match single stars
2368
- for (var i = 0, l = globSegments.length; i < l; i++) {
2369
- if (globSegments[i] === '*') {
2370
- segments[i] = '*';
2371
- }
2372
- }
2373
-
2374
- //match greedy starts
2375
- if (globSegments[0] === '**') {
2376
- segments = segments.slice(indexOf(segments, globSegments[1]));
2377
- segments.unshift('**');
2378
- }
2379
- //match greedy ends
2380
- if (globSegments[globSegments.length - 1] === '**') {
2381
- segments.splice(indexOf(segments, globSegments[globSegments.length - 2]) + 1, Number.MAX_VALUE);
2382
- segments.push('**');
2383
- }
2384
-
2385
- if (globSegments.length != segments.length) {
2386
- return false;
2387
- }
2388
-
2389
- return segments.join('') === globSegments.join('');
2390
- }
2391
-
2392
-
2393
- // Implicit root state that is always active
2394
- root = registerState({
2395
- name: '',
2396
- url: '^',
2397
- views: null,
2398
- 'abstract': true
2399
- });
2400
- root.navigable = null;
2401
-
2402
-
2403
- /**
2404
- * @ngdoc function
2405
- * @name ui.router.state.$stateProvider#decorator
2406
- * @methodOf ui.router.state.$stateProvider
2407
- *
2408
- * @description
2409
- * Allows you to extend (carefully) or override (at your own peril) the
2410
- * `stateBuilder` object used internally by `$stateProvider`. This can be used
2411
- * to add custom functionality to ui-router, for example inferring templateUrl
2412
- * based on the state name.
2413
- *
2414
- * When passing only a name, it returns the current (original or decorated) builder
2415
- * function that matches `name`.
2416
- *
2417
- * The builder functions that can be decorated are listed below. Though not all
2418
- * necessarily have a good use case for decoration, that is up to you to decide.
2419
- *
2420
- * In addition, users can attach custom decorators, which will generate new
2421
- * properties within the state's internal definition. There is currently no clear
2422
- * use-case for this beyond accessing internal states (i.e. $state.$current),
2423
- * however, expect this to become increasingly relevant as we introduce additional
2424
- * meta-programming features.
2425
- *
2426
- * **Warning**: Decorators should not be interdependent because the order of
2427
- * execution of the builder functions in non-deterministic. Builder functions
2428
- * should only be dependent on the state definition object and super function.
2429
- *
2430
- *
2431
- * Existing builder functions and current return values:
2432
- *
2433
- * - **parent** `{object}` - returns the parent state object.
2434
- * - **data** `{object}` - returns state data, including any inherited data that is not
2435
- * overridden by own values (if any).
2436
- * - **url** `{object}` - returns a {@link ui.router.util.type:UrlMatcher UrlMatcher}
2437
- * or `null`.
2438
- * - **navigable** `{object}` - returns closest ancestor state that has a URL (aka is
2439
- * navigable).
2440
- * - **params** `{object}` - returns an array of state params that are ensured to
2441
- * be a super-set of parent's params.
2442
- * - **views** `{object}` - returns a views object where each key is an absolute view
2443
- * name (i.e. "viewName@stateName") and each value is the config object
2444
- * (template, controller) for the view. Even when you don't use the views object
2445
- * explicitly on a state config, one is still created for you internally.
2446
- * So by decorating this builder function you have access to decorating template
2447
- * and controller properties.
2448
- * - **ownParams** `{object}` - returns an array of params that belong to the state,
2449
- * not including any params defined by ancestor states.
2450
- * - **path** `{string}` - returns the full path from the root down to this state.
2451
- * Needed for state activation.
2452
- * - **includes** `{object}` - returns an object that includes every state that
2453
- * would pass a `$state.includes()` test.
2454
- *
2455
- * @example
2456
- * <pre>
2457
- * // Override the internal 'views' builder with a function that takes the state
2458
- * // definition, and a reference to the internal function being overridden:
2459
- * $stateProvider.decorator('views', function (state, parent) {
2460
- * var result = {},
2461
- * views = parent(state);
2462
- *
2463
- * angular.forEach(views, function (config, name) {
2464
- * var autoName = (state.name + '.' + name).replace('.', '/');
2465
- * config.templateUrl = config.templateUrl || '/partials/' + autoName + '.html';
2466
- * result[name] = config;
2467
- * });
2468
- * return result;
2469
- * });
2470
- *
2471
- * $stateProvider.state('home', {
2472
- * views: {
2473
- * 'contact.list': { controller: 'ListController' },
2474
- * 'contact.item': { controller: 'ItemController' }
2475
- * }
2476
- * });
2477
- *
2478
- * // ...
2479
- *
2480
- * $state.go('home');
2481
- * // Auto-populates list and item views with /partials/home/contact/list.html,
2482
- * // and /partials/home/contact/item.html, respectively.
2483
- * </pre>
2484
- *
2485
- * @param {string} name The name of the builder function to decorate.
2486
- * @param {object} func A function that is responsible for decorating the original
2487
- * builder function. The function receives two parameters:
2488
- *
2489
- * - `{object}` - state - The state config object.
2490
- * - `{object}` - super - The original builder function.
2491
- *
2492
- * @return {object} $stateProvider - $stateProvider instance
2493
- */
2494
- this.decorator = decorator;
2495
- function decorator(name, func) {
2496
- /*jshint validthis: true */
2497
- if (isString(name) && !isDefined(func)) {
2498
- return stateBuilder[name];
2499
- }
2500
- if (!isFunction(func) || !isString(name)) {
2501
- return this;
2502
- }
2503
- if (stateBuilder[name] && !stateBuilder.$delegates[name]) {
2504
- stateBuilder.$delegates[name] = stateBuilder[name];
2505
- }
2506
- stateBuilder[name] = func;
2507
- return this;
2508
- }
2509
-
2510
- /**
2511
- * @ngdoc function
2512
- * @name ui.router.state.$stateProvider#state
2513
- * @methodOf ui.router.state.$stateProvider
2514
- *
2515
- * @description
2516
- * Registers a state configuration under a given state name. The stateConfig object
2517
- * has the following acceptable properties.
2518
- *
2519
- * @param {string} name A unique state name, e.g. "home", "about", "contacts".
2520
- * To create a parent/child state use a dot, e.g. "about.sales", "home.newest".
2521
- * @param {object} stateConfig State configuration object.
2522
- * @param {string|function=} stateConfig.template
2523
- * <a id='template'></a>
2524
- * html template as a string or a function that returns
2525
- * an html template as a string which should be used by the uiView directives. This property
2526
- * takes precedence over templateUrl.
2527
- *
2528
- * If `template` is a function, it will be called with the following parameters:
2529
- *
2530
- * - {array.&lt;object&gt;} - state parameters extracted from the current $location.path() by
2531
- * applying the current state
2532
- *
2533
- * <pre>template:
2534
- * "<h1>inline template definition</h1>" +
2535
- * "<div ui-view></div>"</pre>
2536
- * <pre>template: function(params) {
2537
- * return "<h1>generated template</h1>"; }</pre>
2538
- * </div>
2539
- *
2540
- * @param {string|function=} stateConfig.templateUrl
2541
- * <a id='templateUrl'></a>
2542
- *
2543
- * path or function that returns a path to an html
2544
- * template that should be used by uiView.
2545
- *
2546
- * If `templateUrl` is a function, it will be called with the following parameters:
2547
- *
2548
- * - {array.&lt;object&gt;} - state parameters extracted from the current $location.path() by
2549
- * applying the current state
2550
- *
2551
- * <pre>templateUrl: "home.html"</pre>
2552
- * <pre>templateUrl: function(params) {
2553
- * return myTemplates[params.pageId]; }</pre>
2554
- *
2555
- * @param {function=} stateConfig.templateProvider
2556
- * <a id='templateProvider'></a>
2557
- * Provider function that returns HTML content string.
2558
- * <pre> templateProvider:
2559
- * function(MyTemplateService, params) {
2560
- * return MyTemplateService.getTemplate(params.pageId);
2561
- * }</pre>
2562
- *
2563
- * @param {string|function=} stateConfig.controller
2564
- * <a id='controller'></a>
2565
- *
2566
- * Controller fn that should be associated with newly
2567
- * related scope or the name of a registered controller if passed as a string.
2568
- * Optionally, the ControllerAs may be declared here.
2569
- * <pre>controller: "MyRegisteredController"</pre>
2570
- * <pre>controller:
2571
- * "MyRegisteredController as fooCtrl"}</pre>
2572
- * <pre>controller: function($scope, MyService) {
2573
- * $scope.data = MyService.getData(); }</pre>
2574
- *
2575
- * @param {function=} stateConfig.controllerProvider
2576
- * <a id='controllerProvider'></a>
2577
- *
2578
- * Injectable provider function that returns the actual controller or string.
2579
- * <pre>controllerProvider:
2580
- * function(MyResolveData) {
2581
- * if (MyResolveData.foo)
2582
- * return "FooCtrl"
2583
- * else if (MyResolveData.bar)
2584
- * return "BarCtrl";
2585
- * else return function($scope) {
2586
- * $scope.baz = "Qux";
2587
- * }
2588
- * }</pre>
2589
- *
2590
- * @param {string=} stateConfig.controllerAs
2591
- * <a id='controllerAs'></a>
2592
- *
2593
- * A controller alias name. If present the controller will be
2594
- * published to scope under the controllerAs name.
2595
- * <pre>controllerAs: "myCtrl"</pre>
2596
- *
2597
- * @param {string|object=} stateConfig.parent
2598
- * <a id='parent'></a>
2599
- * Optionally specifies the parent state of this state.
2600
- *
2601
- * <pre>parent: 'parentState'</pre>
2602
- * <pre>parent: parentState // JS variable</pre>
2603
- *
2604
- * @param {object=} stateConfig.resolve
2605
- * <a id='resolve'></a>
2606
- *
2607
- * An optional map&lt;string, function&gt; of dependencies which
2608
- * should be injected into the controller. If any of these dependencies are promises,
2609
- * the router will wait for them all to be resolved before the controller is instantiated.
2610
- * If all the promises are resolved successfully, the $stateChangeSuccess event is fired
2611
- * and the values of the resolved promises are injected into any controllers that reference them.
2612
- * If any of the promises are rejected the $stateChangeError event is fired.
2613
- *
2614
- * The map object is:
2615
- *
2616
- * - key - {string}: name of dependency to be injected into controller
2617
- * - factory - {string|function}: If string then it is alias for service. Otherwise if function,
2618
- * it is injected and return value it treated as dependency. If result is a promise, it is
2619
- * resolved before its value is injected into controller.
2620
- *
2621
- * <pre>resolve: {
2622
- * myResolve1:
2623
- * function($http, $stateParams) {
2624
- * return $http.get("/api/foos/"+stateParams.fooID);
2625
- * }
2626
- * }</pre>
2627
- *
2628
- * @param {string=} stateConfig.url
2629
- * <a id='url'></a>
2630
- *
2631
- * A url fragment with optional parameters. When a state is navigated or
2632
- * transitioned to, the `$stateParams` service will be populated with any
2633
- * parameters that were passed.
2634
- *
2635
- * (See {@link ui.router.util.type:UrlMatcher UrlMatcher} `UrlMatcher`} for
2636
- * more details on acceptable patterns )
2637
- *
2638
- * examples:
2639
- * <pre>url: "/home"
2640
- * url: "/users/:userid"
2641
- * url: "/books/{bookid:[a-zA-Z_-]}"
2642
- * url: "/books/{categoryid:int}"
2643
- * url: "/books/{publishername:string}/{categoryid:int}"
2644
- * url: "/messages?before&after"
2645
- * url: "/messages?{before:date}&{after:date}"
2646
- * url: "/messages/:mailboxid?{before:date}&{after:date}"
2647
- * </pre>
2648
- *
2649
- * @param {object=} stateConfig.views
2650
- * <a id='views'></a>
2651
- * an optional map&lt;string, object&gt; which defined multiple views, or targets views
2652
- * manually/explicitly.
2653
- *
2654
- * Examples:
2655
- *
2656
- * Targets three named `ui-view`s in the parent state's template
2657
- * <pre>views: {
2658
- * header: {
2659
- * controller: "headerCtrl",
2660
- * templateUrl: "header.html"
2661
- * }, body: {
2662
- * controller: "bodyCtrl",
2663
- * templateUrl: "body.html"
2664
- * }, footer: {
2665
- * controller: "footCtrl",
2666
- * templateUrl: "footer.html"
2667
- * }
2668
- * }</pre>
2669
- *
2670
- * Targets named `ui-view="header"` from grandparent state 'top''s template, and named `ui-view="body" from parent state's template.
2671
- * <pre>views: {
2672
- * 'header@top': {
2673
- * controller: "msgHeaderCtrl",
2674
- * templateUrl: "msgHeader.html"
2675
- * }, 'body': {
2676
- * controller: "messagesCtrl",
2677
- * templateUrl: "messages.html"
2678
- * }
2679
- * }</pre>
2680
- *
2681
- * @param {boolean=} [stateConfig.abstract=false]
2682
- * <a id='abstract'></a>
2683
- * An abstract state will never be directly activated,
2684
- * but can provide inherited properties to its common children states.
2685
- * <pre>abstract: true</pre>
2686
- *
2687
- * @param {function=} stateConfig.onEnter
2688
- * <a id='onEnter'></a>
2689
- *
2690
- * Callback function for when a state is entered. Good way
2691
- * to trigger an action or dispatch an event, such as opening a dialog.
2692
- * If minifying your scripts, make sure to explictly annotate this function,
2693
- * because it won't be automatically annotated by your build tools.
2694
- *
2695
- * <pre>onEnter: function(MyService, $stateParams) {
2696
- * MyService.foo($stateParams.myParam);
2697
- * }</pre>
2698
- *
2699
- * @param {function=} stateConfig.onExit
2700
- * <a id='onExit'></a>
2701
- *
2702
- * Callback function for when a state is exited. Good way to
2703
- * trigger an action or dispatch an event, such as opening a dialog.
2704
- * If minifying your scripts, make sure to explictly annotate this function,
2705
- * because it won't be automatically annotated by your build tools.
2706
- *
2707
- * <pre>onExit: function(MyService, $stateParams) {
2708
- * MyService.cleanup($stateParams.myParam);
2709
- * }</pre>
2710
- *
2711
- * @param {boolean=} [stateConfig.reloadOnSearch=true]
2712
- * <a id='reloadOnSearch'></a>
2713
- *
2714
- * If `false`, will not retrigger the same state
2715
- * just because a search/query parameter has changed (via $location.search() or $location.hash()).
2716
- * Useful for when you'd like to modify $location.search() without triggering a reload.
2717
- * <pre>reloadOnSearch: false</pre>
2718
- *
2719
- * @param {object=} stateConfig.data
2720
- * <a id='data'></a>
2721
- *
2722
- * Arbitrary data object, useful for custom configuration. The parent state's `data` is
2723
- * prototypally inherited. In other words, adding a data property to a state adds it to
2724
- * the entire subtree via prototypal inheritance.
2725
- *
2726
- * <pre>data: {
2727
- * requiredRole: 'foo'
2728
- * } </pre>
2729
- *
2730
- * @param {object=} stateConfig.params
2731
- * <a id='params'></a>
2732
- *
2733
- * A map which optionally configures parameters declared in the `url`, or
2734
- * defines additional non-url parameters. For each parameter being
2735
- * configured, add a configuration object keyed to the name of the parameter.
2736
- *
2737
- * Each parameter configuration object may contain the following properties:
2738
- *
2739
- * - ** value ** - {object|function=}: specifies the default value for this
2740
- * parameter. This implicitly sets this parameter as optional.
2741
- *
2742
- * When UI-Router routes to a state and no value is
2743
- * specified for this parameter in the URL or transition, the
2744
- * default value will be used instead. If `value` is a function,
2745
- * it will be injected and invoked, and the return value used.
2746
- *
2747
- * *Note*: `undefined` is treated as "no default value" while `null`
2748
- * is treated as "the default value is `null`".
2749
- *
2750
- * *Shorthand*: If you only need to configure the default value of the
2751
- * parameter, you may use a shorthand syntax. In the **`params`**
2752
- * map, instead mapping the param name to a full parameter configuration
2753
- * object, simply set map it to the default parameter value, e.g.:
2754
- *
2755
- * <pre>// define a parameter's default value
2756
- * params: {
2757
- * param1: { value: "defaultValue" }
2758
- * }
2759
- * // shorthand default values
2760
- * params: {
2761
- * param1: "defaultValue",
2762
- * param2: "param2Default"
2763
- * }</pre>
2764
- *
2765
- * - ** array ** - {boolean=}: *(default: false)* If true, the param value will be
2766
- * treated as an array of values. If you specified a Type, the value will be
2767
- * treated as an array of the specified Type. Note: query parameter values
2768
- * default to a special `"auto"` mode.
2769
- *
2770
- * For query parameters in `"auto"` mode, if multiple values for a single parameter
2771
- * are present in the URL (e.g.: `/foo?bar=1&bar=2&bar=3`) then the values
2772
- * are mapped to an array (e.g.: `{ foo: [ '1', '2', '3' ] }`). However, if
2773
- * only one value is present (e.g.: `/foo?bar=1`) then the value is treated as single
2774
- * value (e.g.: `{ foo: '1' }`).
2775
- *
2776
- * <pre>params: {
2777
- * param1: { array: true }
2778
- * }</pre>
2779
- *
2780
- * - ** squash ** - {bool|string=}: `squash` configures how a default parameter value is represented in the URL when
2781
- * the current parameter value is the same as the default value. If `squash` is not set, it uses the
2782
- * configured default squash policy.
2783
- * (See {@link ui.router.util.$urlMatcherFactory#methods_defaultSquashPolicy `defaultSquashPolicy()`})
2784
- *
2785
- * There are three squash settings:
2786
- *
2787
- * - false: The parameter's default value is not squashed. It is encoded and included in the URL
2788
- * - true: The parameter's default value is omitted from the URL. If the parameter is preceeded and followed
2789
- * by slashes in the state's `url` declaration, then one of those slashes are omitted.
2790
- * This can allow for cleaner looking URLs.
2791
- * - `"<arbitrary string>"`: The parameter's default value is replaced with an arbitrary placeholder of your choice.
2792
- *
2793
- * <pre>params: {
2794
- * param1: {
2795
- * value: "defaultId",
2796
- * squash: true
2797
- * } }
2798
- * // squash "defaultValue" to "~"
2799
- * params: {
2800
- * param1: {
2801
- * value: "defaultValue",
2802
- * squash: "~"
2803
- * } }
2804
- * </pre>
2805
- *
2806
- *
2807
- * @example
2808
- * <pre>
2809
- * // Some state name examples
2810
- *
2811
- * // stateName can be a single top-level name (must be unique).
2812
- * $stateProvider.state("home", {});
2813
- *
2814
- * // Or it can be a nested state name. This state is a child of the
2815
- * // above "home" state.
2816
- * $stateProvider.state("home.newest", {});
2817
- *
2818
- * // Nest states as deeply as needed.
2819
- * $stateProvider.state("home.newest.abc.xyz.inception", {});
2820
- *
2821
- * // state() returns $stateProvider, so you can chain state declarations.
2822
- * $stateProvider
2823
- * .state("home", {})
2824
- * .state("about", {})
2825
- * .state("contacts", {});
2826
- * </pre>
2827
- *
2828
- */
2829
- this.state = state;
2830
- function state(name, definition) {
2831
- /*jshint validthis: true */
2832
- if (isObject(name)) definition = name;
2833
- else definition.name = name;
2834
- registerState(definition);
2835
- return this;
2836
- }
2837
-
2838
- /**
2839
- * @ngdoc object
2840
- * @name ui.router.state.$state
2841
- *
2842
- * @requires $rootScope
2843
- * @requires $q
2844
- * @requires ui.router.state.$view
2845
- * @requires $injector
2846
- * @requires ui.router.util.$resolve
2847
- * @requires ui.router.state.$stateParams
2848
- * @requires ui.router.router.$urlRouter
2849
- *
2850
- * @property {object} params A param object, e.g. {sectionId: section.id)}, that
2851
- * you'd like to test against the current active state.
2852
- * @property {object} current A reference to the state's config object. However
2853
- * you passed it in. Useful for accessing custom data.
2854
- * @property {object} transition Currently pending transition. A promise that'll
2855
- * resolve or reject.
2856
- *
2857
- * @description
2858
- * `$state` service is responsible for representing states as well as transitioning
2859
- * between them. It also provides interfaces to ask for current state or even states
2860
- * you're coming from.
2861
- */
2862
- this.$get = $get;
2863
- $get.$inject = ['$rootScope', '$q', '$view', '$injector', '$resolve', '$stateParams', '$urlRouter', '$location', '$urlMatcherFactory'];
2864
- function $get( $rootScope, $q, $view, $injector, $resolve, $stateParams, $urlRouter, $location, $urlMatcherFactory) {
2865
-
2866
- var TransitionSuperseded = $q.reject(new Error('transition superseded'));
2867
- var TransitionPrevented = $q.reject(new Error('transition prevented'));
2868
- var TransitionAborted = $q.reject(new Error('transition aborted'));
2869
- var TransitionFailed = $q.reject(new Error('transition failed'));
2870
-
2871
- // Handles the case where a state which is the target of a transition is not found, and the user
2872
- // can optionally retry or defer the transition
2873
- function handleRedirect(redirect, state, params, options) {
2874
- /**
2875
- * @ngdoc event
2876
- * @name ui.router.state.$state#$stateNotFound
2877
- * @eventOf ui.router.state.$state
2878
- * @eventType broadcast on root scope
2879
- * @description
2880
- * Fired when a requested state **cannot be found** using the provided state name during transition.
2881
- * The event is broadcast allowing any handlers a single chance to deal with the error (usually by
2882
- * lazy-loading the unfound state). A special `unfoundState` object is passed to the listener handler,
2883
- * you can see its three properties in the example. You can use `event.preventDefault()` to abort the
2884
- * transition and the promise returned from `go` will be rejected with a `'transition aborted'` value.
2885
- *
2886
- * @param {Object} event Event object.
2887
- * @param {Object} unfoundState Unfound State information. Contains: `to, toParams, options` properties.
2888
- * @param {State} fromState Current state object.
2889
- * @param {Object} fromParams Current state params.
2890
- *
2891
- * @example
2892
- *
2893
- * <pre>
2894
- * // somewhere, assume lazy.state has not been defined
2895
- * $state.go("lazy.state", {a:1, b:2}, {inherit:false});
2896
- *
2897
- * // somewhere else
2898
- * $scope.$on('$stateNotFound',
2899
- * function(event, unfoundState, fromState, fromParams){
2900
- * console.log(unfoundState.to); // "lazy.state"
2901
- * console.log(unfoundState.toParams); // {a:1, b:2}
2902
- * console.log(unfoundState.options); // {inherit:false} + default options
2903
- * })
2904
- * </pre>
2905
- */
2906
- var evt = $rootScope.$broadcast('$stateNotFound', redirect, state, params);
2907
-
2908
- if (evt.defaultPrevented) {
2909
- $urlRouter.update();
2910
- return TransitionAborted;
2911
- }
2912
-
2913
- if (!evt.retry) {
2914
- return null;
2915
- }
2916
-
2917
- // Allow the handler to return a promise to defer state lookup retry
2918
- if (options.$retry) {
2919
- $urlRouter.update();
2920
- return TransitionFailed;
2921
- }
2922
- var retryTransition = $state.transition = $q.when(evt.retry);
2923
-
2924
- retryTransition.then(function() {
2925
- if (retryTransition !== $state.transition) return TransitionSuperseded;
2926
- redirect.options.$retry = true;
2927
- return $state.transitionTo(redirect.to, redirect.toParams, redirect.options);
2928
- }, function() {
2929
- return TransitionAborted;
2930
- });
2931
- $urlRouter.update();
2932
-
2933
- return retryTransition;
2934
- }
2935
-
2936
- root.locals = { resolve: null, globals: { $stateParams: {} } };
2937
-
2938
- $state = {
2939
- params: {},
2940
- current: root.self,
2941
- $current: root,
2942
- transition: null
2943
- };
2944
-
2945
- /**
2946
- * @ngdoc function
2947
- * @name ui.router.state.$state#reload
2948
- * @methodOf ui.router.state.$state
2949
- *
2950
- * @description
2951
- * A method that force reloads the current state. All resolves are re-resolved,
2952
- * controllers reinstantiated, and events re-fired.
2953
- *
2954
- * @example
2955
- * <pre>
2956
- * var app angular.module('app', ['ui.router']);
2957
- *
2958
- * app.controller('ctrl', function ($scope, $state) {
2959
- * $scope.reload = function(){
2960
- * $state.reload();
2961
- * }
2962
- * });
2963
- * </pre>
2964
- *
2965
- * `reload()` is just an alias for:
2966
- * <pre>
2967
- * $state.transitionTo($state.current, $stateParams, {
2968
- * reload: true, inherit: false, notify: true
2969
- * });
2970
- * </pre>
2971
- *
2972
- * @param {string=|object=} state - A state name or a state object, which is the root of the resolves to be re-resolved.
2973
- * @example
2974
- * <pre>
2975
- * //assuming app application consists of 3 states: 'contacts', 'contacts.detail', 'contacts.detail.item'
2976
- * //and current state is 'contacts.detail.item'
2977
- * var app angular.module('app', ['ui.router']);
2978
- *
2979
- * app.controller('ctrl', function ($scope, $state) {
2980
- * $scope.reload = function(){
2981
- * //will reload 'contact.detail' and 'contact.detail.item' states
2982
- * $state.reload('contact.detail');
2983
- * }
2984
- * });
2985
- * </pre>
2986
- *
2987
- * `reload()` is just an alias for:
2988
- * <pre>
2989
- * $state.transitionTo($state.current, $stateParams, {
2990
- * reload: true, inherit: false, notify: true
2991
- * });
2992
- * </pre>
2993
-
2994
- * @returns {promise} A promise representing the state of the new transition. See
2995
- * {@link ui.router.state.$state#methods_go $state.go}.
2996
- */
2997
- $state.reload = function reload(state) {
2998
- return $state.transitionTo($state.current, $stateParams, { reload: state || true, inherit: false, notify: true});
2999
- };
3000
-
3001
- /**
3002
- * @ngdoc function
3003
- * @name ui.router.state.$state#go
3004
- * @methodOf ui.router.state.$state
3005
- *
3006
- * @description
3007
- * Convenience method for transitioning to a new state. `$state.go` calls
3008
- * `$state.transitionTo` internally but automatically sets options to
3009
- * `{ location: true, inherit: true, relative: $state.$current, notify: true }`.
3010
- * This allows you to easily use an absolute or relative to path and specify
3011
- * only the parameters you'd like to update (while letting unspecified parameters
3012
- * inherit from the currently active ancestor states).
3013
- *
3014
- * @example
3015
- * <pre>
3016
- * var app = angular.module('app', ['ui.router']);
3017
- *
3018
- * app.controller('ctrl', function ($scope, $state) {
3019
- * $scope.changeState = function () {
3020
- * $state.go('contact.detail');
3021
- * };
3022
- * });
3023
- * </pre>
3024
- * <img src='../ngdoc_assets/StateGoExamples.png'/>
3025
- *
3026
- * @param {string} to Absolute state name or relative state path. Some examples:
3027
- *
3028
- * - `$state.go('contact.detail')` - will go to the `contact.detail` state
3029
- * - `$state.go('^')` - will go to a parent state
3030
- * - `$state.go('^.sibling')` - will go to a sibling state
3031
- * - `$state.go('.child.grandchild')` - will go to grandchild state
3032
- *
3033
- * @param {object=} params A map of the parameters that will be sent to the state,
3034
- * will populate $stateParams. Any parameters that are not specified will be inherited from currently
3035
- * defined parameters. This allows, for example, going to a sibling state that shares parameters
3036
- * specified in a parent state. Parameter inheritance only works between common ancestor states, I.e.
3037
- * transitioning to a sibling will get you the parameters for all parents, transitioning to a child
3038
- * will get you all current parameters, etc.
3039
- * @param {object=} options Options object. The options are:
3040
- *
3041
- * - **`location`** - {boolean=true|string=} - If `true` will update the url in the location bar, if `false`
3042
- * will not. If string, must be `"replace"`, which will update url and also replace last history record.
3043
- * - **`inherit`** - {boolean=true}, If `true` will inherit url parameters from current url.
3044
- * - **`relative`** - {object=$state.$current}, When transitioning with relative path (e.g '^'),
3045
- * defines which state to be relative from.
3046
- * - **`notify`** - {boolean=true}, If `true` will broadcast $stateChangeStart and $stateChangeSuccess events.
3047
- * - **`reload`** (v0.2.5) - {boolean=false}, If `true` will force transition even if the state or params
3048
- * have not changed, aka a reload of the same state. It differs from reloadOnSearch because you'd
3049
- * use this when you want to force a reload when *everything* is the same, including search params.
3050
- *
3051
- * @returns {promise} A promise representing the state of the new transition.
3052
- *
3053
- * Possible success values:
3054
- *
3055
- * - $state.current
3056
- *
3057
- * <br/>Possible rejection values:
3058
- *
3059
- * - 'transition superseded' - when a newer transition has been started after this one
3060
- * - 'transition prevented' - when `event.preventDefault()` has been called in a `$stateChangeStart` listener
3061
- * - 'transition aborted' - when `event.preventDefault()` has been called in a `$stateNotFound` listener or
3062
- * when a `$stateNotFound` `event.retry` promise errors.
3063
- * - 'transition failed' - when a state has been unsuccessfully found after 2 tries.
3064
- * - *resolve error* - when an error has occurred with a `resolve`
3065
- *
3066
- */
3067
- $state.go = function go(to, params, options) {
3068
- return $state.transitionTo(to, params, extend({ inherit: true, relative: $state.$current }, options));
3069
- };
3070
-
3071
- /**
3072
- * @ngdoc function
3073
- * @name ui.router.state.$state#transitionTo
3074
- * @methodOf ui.router.state.$state
3075
- *
3076
- * @description
3077
- * Low-level method for transitioning to a new state. {@link ui.router.state.$state#methods_go $state.go}
3078
- * uses `transitionTo` internally. `$state.go` is recommended in most situations.
3079
- *
3080
- * @example
3081
- * <pre>
3082
- * var app = angular.module('app', ['ui.router']);
3083
- *
3084
- * app.controller('ctrl', function ($scope, $state) {
3085
- * $scope.changeState = function () {
3086
- * $state.transitionTo('contact.detail');
3087
- * };
3088
- * });
3089
- * </pre>
3090
- *
3091
- * @param {string} to State name.
3092
- * @param {object=} toParams A map of the parameters that will be sent to the state,
3093
- * will populate $stateParams.
3094
- * @param {object=} options Options object. The options are:
3095
- *
3096
- * - **`location`** - {boolean=true|string=} - If `true` will update the url in the location bar, if `false`
3097
- * will not. If string, must be `"replace"`, which will update url and also replace last history record.
3098
- * - **`inherit`** - {boolean=false}, If `true` will inherit url parameters from current url.
3099
- * - **`relative`** - {object=}, When transitioning with relative path (e.g '^'),
3100
- * defines which state to be relative from.
3101
- * - **`notify`** - {boolean=true}, If `true` will broadcast $stateChangeStart and $stateChangeSuccess events.
3102
- * - **`reload`** (v0.2.5) - {boolean=false|string=|object=}, If `true` will force transition even if the state or params
3103
- * have not changed, aka a reload of the same state. It differs from reloadOnSearch because you'd
3104
- * use this when you want to force a reload when *everything* is the same, including search params.
3105
- * if String, then will reload the state with the name given in reload, and any children.
3106
- * if Object, then a stateObj is expected, will reload the state found in stateObj, and any children.
3107
- *
3108
- * @returns {promise} A promise representing the state of the new transition. See
3109
- * {@link ui.router.state.$state#methods_go $state.go}.
3110
- */
3111
- $state.transitionTo = function transitionTo(to, toParams, options) {
3112
- toParams = toParams || {};
3113
- options = extend({
3114
- location: true, inherit: false, relative: null, notify: true, reload: false, $retry: false
3115
- }, options || {});
3116
-
3117
- var from = $state.$current, fromParams = $state.params, fromPath = from.path;
3118
- var evt, toState = findState(to, options.relative);
3119
-
3120
- // Store the hash param for later (since it will be stripped out by various methods)
3121
- var hash = toParams['#'];
3122
-
3123
- if (!isDefined(toState)) {
3124
- var redirect = { to: to, toParams: toParams, options: options };
3125
- var redirectResult = handleRedirect(redirect, from.self, fromParams, options);
3126
-
3127
- if (redirectResult) {
3128
- return redirectResult;
3129
- }
3130
-
3131
- // Always retry once if the $stateNotFound was not prevented
3132
- // (handles either redirect changed or state lazy-definition)
3133
- to = redirect.to;
3134
- toParams = redirect.toParams;
3135
- options = redirect.options;
3136
- toState = findState(to, options.relative);
3137
-
3138
- if (!isDefined(toState)) {
3139
- if (!options.relative) throw new Error("No such state '" + to + "'");
3140
- throw new Error("Could not resolve '" + to + "' from state '" + options.relative + "'");
3141
- }
3142
- }
3143
- if (toState[abstractKey]) throw new Error("Cannot transition to abstract state '" + to + "'");
3144
- if (options.inherit) toParams = inheritParams($stateParams, toParams || {}, $state.$current, toState);
3145
- if (!toState.params.$$validates(toParams)) return TransitionFailed;
3146
-
3147
- toParams = toState.params.$$values(toParams);
3148
- to = toState;
3149
-
3150
- var toPath = to.path;
3151
-
3152
- // Starting from the root of the path, keep all levels that haven't changed
3153
- var keep = 0, state = toPath[keep], locals = root.locals, toLocals = [];
3154
-
3155
- if (!options.reload) {
3156
- while (state && state === fromPath[keep] && state.ownParams.$$equals(toParams, fromParams)) {
3157
- locals = toLocals[keep] = state.locals;
3158
- keep++;
3159
- state = toPath[keep];
3160
- }
3161
- } else if (isString(options.reload) || isObject(options.reload)) {
3162
- if (isObject(options.reload) && !options.reload.name) {
3163
- throw new Error('Invalid reload state object');
3164
- }
3165
-
3166
- var reloadState = options.reload === true ? fromPath[0] : findState(options.reload);
3167
- if (options.reload && !reloadState) {
3168
- throw new Error("No such reload state '" + (isString(options.reload) ? options.reload : options.reload.name) + "'");
3169
- }
3170
-
3171
- while (state && state === fromPath[keep] && state !== reloadState) {
3172
- locals = toLocals[keep] = state.locals;
3173
- keep++;
3174
- state = toPath[keep];
3175
- }
3176
- }
3177
-
3178
- // If we're going to the same state and all locals are kept, we've got nothing to do.
3179
- // But clear 'transition', as we still want to cancel any other pending transitions.
3180
- // TODO: We may not want to bump 'transition' if we're called from a location change
3181
- // that we've initiated ourselves, because we might accidentally abort a legitimate
3182
- // transition initiated from code?
3183
- if (shouldSkipReload(to, toParams, from, fromParams, locals, options)) {
3184
- if (hash) toParams['#'] = hash;
3185
- $state.params = toParams;
3186
- copy($state.params, $stateParams);
3187
- if (options.location && to.navigable && to.navigable.url) {
3188
- $urlRouter.push(to.navigable.url, toParams, {
3189
- $$avoidResync: true, replace: options.location === 'replace'
3190
- });
3191
- $urlRouter.update(true);
3192
- }
3193
- $state.transition = null;
3194
- return $q.when($state.current);
3195
- }
3196
-
3197
- // Filter parameters before we pass them to event handlers etc.
3198
- toParams = filterByKeys(to.params.$$keys(), toParams || {});
3199
-
3200
- // Broadcast start event and cancel the transition if requested
3201
- if (options.notify) {
3202
- /**
3203
- * @ngdoc event
3204
- * @name ui.router.state.$state#$stateChangeStart
3205
- * @eventOf ui.router.state.$state
3206
- * @eventType broadcast on root scope
3207
- * @description
3208
- * Fired when the state transition **begins**. You can use `event.preventDefault()`
3209
- * to prevent the transition from happening and then the transition promise will be
3210
- * rejected with a `'transition prevented'` value.
3211
- *
3212
- * @param {Object} event Event object.
3213
- * @param {State} toState The state being transitioned to.
3214
- * @param {Object} toParams The params supplied to the `toState`.
3215
- * @param {State} fromState The current state, pre-transition.
3216
- * @param {Object} fromParams The params supplied to the `fromState`.
3217
- *
3218
- * @example
3219
- *
3220
- * <pre>
3221
- * $rootScope.$on('$stateChangeStart',
3222
- * function(event, toState, toParams, fromState, fromParams){
3223
- * event.preventDefault();
3224
- * // transitionTo() promise will be rejected with
3225
- * // a 'transition prevented' error
3226
- * })
3227
- * </pre>
3228
- */
3229
- if ($rootScope.$broadcast('$stateChangeStart', to.self, toParams, from.self, fromParams).defaultPrevented) {
3230
- $rootScope.$broadcast('$stateChangeCancel', to.self, toParams, from.self, fromParams);
3231
- $urlRouter.update();
3232
- return TransitionPrevented;
3233
- }
3234
- }
3235
-
3236
- // Resolve locals for the remaining states, but don't update any global state just
3237
- // yet -- if anything fails to resolve the current state needs to remain untouched.
3238
- // We also set up an inheritance chain for the locals here. This allows the view directive
3239
- // to quickly look up the correct definition for each view in the current state. Even
3240
- // though we create the locals object itself outside resolveState(), it is initially
3241
- // empty and gets filled asynchronously. We need to keep track of the promise for the
3242
- // (fully resolved) current locals, and pass this down the chain.
3243
- var resolved = $q.when(locals);
3244
-
3245
- for (var l = keep; l < toPath.length; l++, state = toPath[l]) {
3246
- locals = toLocals[l] = inherit(locals);
3247
- resolved = resolveState(state, toParams, state === to, resolved, locals, options);
3248
- }
3249
-
3250
- // Once everything is resolved, we are ready to perform the actual transition
3251
- // and return a promise for the new state. We also keep track of what the
3252
- // current promise is, so that we can detect overlapping transitions and
3253
- // keep only the outcome of the last transition.
3254
- var transition = $state.transition = resolved.then(function () {
3255
- var l, entering, exiting;
3256
-
3257
- if ($state.transition !== transition) return TransitionSuperseded;
3258
-
3259
- // Exit 'from' states not kept
3260
- for (l = fromPath.length - 1; l >= keep; l--) {
3261
- exiting = fromPath[l];
3262
- if (exiting.self.onExit) {
3263
- $injector.invoke(exiting.self.onExit, exiting.self, exiting.locals.globals);
3264
- }
3265
- exiting.locals = null;
3266
- }
3267
-
3268
- // Enter 'to' states not kept
3269
- for (l = keep; l < toPath.length; l++) {
3270
- entering = toPath[l];
3271
- entering.locals = toLocals[l];
3272
- if (entering.self.onEnter) {
3273
- $injector.invoke(entering.self.onEnter, entering.self, entering.locals.globals);
3274
- }
3275
- }
3276
-
3277
- // Re-add the saved hash before we start returning things
3278
- if (hash) toParams['#'] = hash;
3279
-
3280
- // Run it again, to catch any transitions in callbacks
3281
- if ($state.transition !== transition) return TransitionSuperseded;
3282
-
3283
- // Update globals in $state
3284
- $state.$current = to;
3285
- $state.current = to.self;
3286
- $state.params = toParams;
3287
- copy($state.params, $stateParams);
3288
- $state.transition = null;
3289
-
3290
- if (options.location && to.navigable) {
3291
- $urlRouter.push(to.navigable.url, to.navigable.locals.globals.$stateParams, {
3292
- $$avoidResync: true, replace: options.location === 'replace'
3293
- });
3294
- }
3295
-
3296
- if (options.notify) {
3297
- /**
3298
- * @ngdoc event
3299
- * @name ui.router.state.$state#$stateChangeSuccess
3300
- * @eventOf ui.router.state.$state
3301
- * @eventType broadcast on root scope
3302
- * @description
3303
- * Fired once the state transition is **complete**.
3304
- *
3305
- * @param {Object} event Event object.
3306
- * @param {State} toState The state being transitioned to.
3307
- * @param {Object} toParams The params supplied to the `toState`.
3308
- * @param {State} fromState The current state, pre-transition.
3309
- * @param {Object} fromParams The params supplied to the `fromState`.
3310
- */
3311
- $rootScope.$broadcast('$stateChangeSuccess', to.self, toParams, from.self, fromParams);
3312
- }
3313
- $urlRouter.update(true);
3314
-
3315
- return $state.current;
3316
- }, function (error) {
3317
- if ($state.transition !== transition) return TransitionSuperseded;
3318
-
3319
- $state.transition = null;
3320
- /**
3321
- * @ngdoc event
3322
- * @name ui.router.state.$state#$stateChangeError
3323
- * @eventOf ui.router.state.$state
3324
- * @eventType broadcast on root scope
3325
- * @description
3326
- * Fired when an **error occurs** during transition. It's important to note that if you
3327
- * have any errors in your resolve functions (javascript errors, non-existent services, etc)
3328
- * they will not throw traditionally. You must listen for this $stateChangeError event to
3329
- * catch **ALL** errors.
3330
- *
3331
- * @param {Object} event Event object.
3332
- * @param {State} toState The state being transitioned to.
3333
- * @param {Object} toParams The params supplied to the `toState`.
3334
- * @param {State} fromState The current state, pre-transition.
3335
- * @param {Object} fromParams The params supplied to the `fromState`.
3336
- * @param {Error} error The resolve error object.
3337
- */
3338
- evt = $rootScope.$broadcast('$stateChangeError', to.self, toParams, from.self, fromParams, error);
3339
-
3340
- if (!evt.defaultPrevented) {
3341
- $urlRouter.update();
3342
- }
3343
-
3344
- return $q.reject(error);
3345
- });
3346
-
3347
- return transition;
3348
- };
3349
-
3350
- /**
3351
- * @ngdoc function
3352
- * @name ui.router.state.$state#is
3353
- * @methodOf ui.router.state.$state
3354
- *
3355
- * @description
3356
- * Similar to {@link ui.router.state.$state#methods_includes $state.includes},
3357
- * but only checks for the full state name. If params is supplied then it will be
3358
- * tested for strict equality against the current active params object, so all params
3359
- * must match with none missing and no extras.
3360
- *
3361
- * @example
3362
- * <pre>
3363
- * $state.$current.name = 'contacts.details.item';
3364
- *
3365
- * // absolute name
3366
- * $state.is('contact.details.item'); // returns true
3367
- * $state.is(contactDetailItemStateObject); // returns true
3368
- *
3369
- * // relative name (. and ^), typically from a template
3370
- * // E.g. from the 'contacts.details' template
3371
- * <div ng-class="{highlighted: $state.is('.item')}">Item</div>
3372
- * </pre>
3373
- *
3374
- * @param {string|object} stateOrName The state name (absolute or relative) or state object you'd like to check.
3375
- * @param {object=} params A param object, e.g. `{sectionId: section.id}`, that you'd like
3376
- * to test against the current active state.
3377
- * @param {object=} options An options object. The options are:
3378
- *
3379
- * - **`relative`** - {string|object} - If `stateOrName` is a relative state name and `options.relative` is set, .is will
3380
- * test relative to `options.relative` state (or name).
3381
- *
3382
- * @returns {boolean} Returns true if it is the state.
3383
- */
3384
- $state.is = function is(stateOrName, params, options) {
3385
- options = extend({ relative: $state.$current }, options || {});
3386
- var state = findState(stateOrName, options.relative);
3387
-
3388
- if (!isDefined(state)) { return undefined; }
3389
- if ($state.$current !== state) { return false; }
3390
- return params ? equalForKeys(state.params.$$values(params), $stateParams) : true;
3391
- };
3392
-
3393
- /**
3394
- * @ngdoc function
3395
- * @name ui.router.state.$state#includes
3396
- * @methodOf ui.router.state.$state
3397
- *
3398
- * @description
3399
- * A method to determine if the current active state is equal to or is the child of the
3400
- * state stateName. If any params are passed then they will be tested for a match as well.
3401
- * Not all the parameters need to be passed, just the ones you'd like to test for equality.
3402
- *
3403
- * @example
3404
- * Partial and relative names
3405
- * <pre>
3406
- * $state.$current.name = 'contacts.details.item';
3407
- *
3408
- * // Using partial names
3409
- * $state.includes("contacts"); // returns true
3410
- * $state.includes("contacts.details"); // returns true
3411
- * $state.includes("contacts.details.item"); // returns true
3412
- * $state.includes("contacts.list"); // returns false
3413
- * $state.includes("about"); // returns false
3414
- *
3415
- * // Using relative names (. and ^), typically from a template
3416
- * // E.g. from the 'contacts.details' template
3417
- * <div ng-class="{highlighted: $state.includes('.item')}">Item</div>
3418
- * </pre>
3419
- *
3420
- * Basic globbing patterns
3421
- * <pre>
3422
- * $state.$current.name = 'contacts.details.item.url';
3423
- *
3424
- * $state.includes("*.details.*.*"); // returns true
3425
- * $state.includes("*.details.**"); // returns true
3426
- * $state.includes("**.item.**"); // returns true
3427
- * $state.includes("*.details.item.url"); // returns true
3428
- * $state.includes("*.details.*.url"); // returns true
3429
- * $state.includes("*.details.*"); // returns false
3430
- * $state.includes("item.**"); // returns false
3431
- * </pre>
3432
- *
3433
- * @param {string} stateOrName A partial name, relative name, or glob pattern
3434
- * to be searched for within the current state name.
3435
- * @param {object=} params A param object, e.g. `{sectionId: section.id}`,
3436
- * that you'd like to test against the current active state.
3437
- * @param {object=} options An options object. The options are:
3438
- *
3439
- * - **`relative`** - {string|object=} - If `stateOrName` is a relative state reference and `options.relative` is set,
3440
- * .includes will test relative to `options.relative` state (or name).
3441
- *
3442
- * @returns {boolean} Returns true if it does include the state
3443
- */
3444
- $state.includes = function includes(stateOrName, params, options) {
3445
- options = extend({ relative: $state.$current }, options || {});
3446
- if (isString(stateOrName) && isGlob(stateOrName)) {
3447
- if (!doesStateMatchGlob(stateOrName)) {
3448
- return false;
3449
- }
3450
- stateOrName = $state.$current.name;
3451
- }
3452
-
3453
- var state = findState(stateOrName, options.relative);
3454
- if (!isDefined(state)) { return undefined; }
3455
- if (!isDefined($state.$current.includes[state.name])) { return false; }
3456
- return params ? equalForKeys(state.params.$$values(params), $stateParams, objectKeys(params)) : true;
3457
- };
3458
-
3459
-
3460
- /**
3461
- * @ngdoc function
3462
- * @name ui.router.state.$state#href
3463
- * @methodOf ui.router.state.$state
3464
- *
3465
- * @description
3466
- * A url generation method that returns the compiled url for the given state populated with the given params.
3467
- *
3468
- * @example
3469
- * <pre>
3470
- * expect($state.href("about.person", { person: "bob" })).toEqual("/about/bob");
3471
- * </pre>
3472
- *
3473
- * @param {string|object} stateOrName The state name or state object you'd like to generate a url from.
3474
- * @param {object=} params An object of parameter values to fill the state's required parameters.
3475
- * @param {object=} options Options object. The options are:
3476
- *
3477
- * - **`lossy`** - {boolean=true} - If true, and if there is no url associated with the state provided in the
3478
- * first parameter, then the constructed href url will be built from the first navigable ancestor (aka
3479
- * ancestor with a valid url).
3480
- * - **`inherit`** - {boolean=true}, If `true` will inherit url parameters from current url.
3481
- * - **`relative`** - {object=$state.$current}, When transitioning with relative path (e.g '^'),
3482
- * defines which state to be relative from.
3483
- * - **`absolute`** - {boolean=false}, If true will generate an absolute url, e.g. "http://www.example.com/fullurl".
3484
- *
3485
- * @returns {string} compiled state url
3486
- */
3487
- $state.href = function href(stateOrName, params, options) {
3488
- options = extend({
3489
- lossy: true,
3490
- inherit: true,
3491
- absolute: false,
3492
- relative: $state.$current
3493
- }, options || {});
3494
-
3495
- var state = findState(stateOrName, options.relative);
3496
-
3497
- if (!isDefined(state)) return null;
3498
- if (options.inherit) params = inheritParams($stateParams, params || {}, $state.$current, state);
3499
-
3500
- var nav = (state && options.lossy) ? state.navigable : state;
3501
-
3502
- if (!nav || nav.url === undefined || nav.url === null) {
3503
- return null;
3504
- }
3505
- return $urlRouter.href(nav.url, filterByKeys(state.params.$$keys().concat('#'), params || {}), {
3506
- absolute: options.absolute
3507
- });
3508
- };
3509
-
3510
- /**
3511
- * @ngdoc function
3512
- * @name ui.router.state.$state#get
3513
- * @methodOf ui.router.state.$state
3514
- *
3515
- * @description
3516
- * Returns the state configuration object for any specific state or all states.
3517
- *
3518
- * @param {string|object=} stateOrName (absolute or relative) If provided, will only get the config for
3519
- * the requested state. If not provided, returns an array of ALL state configs.
3520
- * @param {string|object=} context When stateOrName is a relative state reference, the state will be retrieved relative to context.
3521
- * @returns {Object|Array} State configuration object or array of all objects.
3522
- */
3523
- $state.get = function (stateOrName, context) {
3524
- if (arguments.length === 0) return map(objectKeys(states), function(name) { return states[name].self; });
3525
- var state = findState(stateOrName, context || $state.$current);
3526
- return (state && state.self) ? state.self : null;
3527
- };
3528
-
3529
- function resolveState(state, params, paramsAreFiltered, inherited, dst, options) {
3530
- // Make a restricted $stateParams with only the parameters that apply to this state if
3531
- // necessary. In addition to being available to the controller and onEnter/onExit callbacks,
3532
- // we also need $stateParams to be available for any $injector calls we make during the
3533
- // dependency resolution process.
3534
- var $stateParams = (paramsAreFiltered) ? params : filterByKeys(state.params.$$keys(), params);
3535
- var locals = { $stateParams: $stateParams };
3536
-
3537
- // Resolve 'global' dependencies for the state, i.e. those not specific to a view.
3538
- // We're also including $stateParams in this; that way the parameters are restricted
3539
- // to the set that should be visible to the state, and are independent of when we update
3540
- // the global $state and $stateParams values.
3541
- dst.resolve = $resolve.resolve(state.resolve, locals, dst.resolve, state);
3542
- var promises = [dst.resolve.then(function (globals) {
3543
- dst.globals = globals;
3544
- })];
3545
- if (inherited) promises.push(inherited);
3546
-
3547
- function resolveViews() {
3548
- var viewsPromises = [];
3549
-
3550
- // Resolve template and dependencies for all views.
3551
- forEach(state.views, function (view, name) {
3552
- var injectables = (view.resolve && view.resolve !== state.resolve ? view.resolve : {});
3553
- injectables.$template = [ function () {
3554
- return $view.load(name, { view: view, locals: dst.globals, params: $stateParams, notify: options.notify }) || '';
3555
- }];
3556
-
3557
- viewsPromises.push($resolve.resolve(injectables, dst.globals, dst.resolve, state).then(function (result) {
3558
- // References to the controller (only instantiated at link time)
3559
- if (isFunction(view.controllerProvider) || isArray(view.controllerProvider)) {
3560
- var injectLocals = angular.extend({}, injectables, dst.globals);
3561
- result.$$controller = $injector.invoke(view.controllerProvider, null, injectLocals);
3562
- } else {
3563
- result.$$controller = view.controller;
3564
- }
3565
- // Provide access to the state itself for internal use
3566
- result.$$state = state;
3567
- result.$$controllerAs = view.controllerAs;
3568
- dst[name] = result;
3569
- }));
3570
- });
3571
-
3572
- return $q.all(viewsPromises).then(function(){
3573
- return dst.globals;
3574
- });
3575
- }
3576
-
3577
- // Wait for all the promises and then return the activation object
3578
- return $q.all(promises).then(resolveViews).then(function (values) {
3579
- return dst;
3580
- });
3581
- }
3582
-
3583
- return $state;
3584
- }
3585
-
3586
- function shouldSkipReload(to, toParams, from, fromParams, locals, options) {
3587
- // Return true if there are no differences in non-search (path/object) params, false if there are differences
3588
- function nonSearchParamsEqual(fromAndToState, fromParams, toParams) {
3589
- // Identify whether all the parameters that differ between `fromParams` and `toParams` were search params.
3590
- function notSearchParam(key) {
3591
- return fromAndToState.params[key].location != "search";
3592
- }
3593
- var nonQueryParamKeys = fromAndToState.params.$$keys().filter(notSearchParam);
3594
- var nonQueryParams = pick.apply({}, [fromAndToState.params].concat(nonQueryParamKeys));
3595
- var nonQueryParamSet = new $$UMFP.ParamSet(nonQueryParams);
3596
- return nonQueryParamSet.$$equals(fromParams, toParams);
3597
- }
3598
-
3599
- // If reload was not explicitly requested
3600
- // and we're transitioning to the same state we're already in
3601
- // and the locals didn't change
3602
- // or they changed in a way that doesn't merit reloading
3603
- // (reloadOnParams:false, or reloadOnSearch.false and only search params changed)
3604
- // Then return true.
3605
- if (!options.reload && to === from &&
3606
- (locals === from.locals || (to.self.reloadOnSearch === false && nonSearchParamsEqual(from, fromParams, toParams)))) {
3607
- return true;
3608
- }
3609
- }
3610
- }
3611
-
3612
- angular.module('ui.router.state')
3613
- .value('$stateParams', {})
3614
- .provider('$state', $StateProvider);
3615
-
3616
-
3617
- $ViewProvider.$inject = [];
3618
- function $ViewProvider() {
3619
-
3620
- this.$get = $get;
3621
- /**
3622
- * @ngdoc object
3623
- * @name ui.router.state.$view
3624
- *
3625
- * @requires ui.router.util.$templateFactory
3626
- * @requires $rootScope
3627
- *
3628
- * @description
3629
- *
3630
- */
3631
- $get.$inject = ['$rootScope', '$templateFactory'];
3632
- function $get( $rootScope, $templateFactory) {
3633
- return {
3634
- // $view.load('full.viewName', { template: ..., controller: ..., resolve: ..., async: false, params: ... })
3635
- /**
3636
- * @ngdoc function
3637
- * @name ui.router.state.$view#load
3638
- * @methodOf ui.router.state.$view
3639
- *
3640
- * @description
3641
- *
3642
- * @param {string} name name
3643
- * @param {object} options option object.
3644
- */
3645
- load: function load(name, options) {
3646
- var result, defaults = {
3647
- template: null, controller: null, view: null, locals: null, notify: true, async: true, params: {}
3648
- };
3649
- options = extend(defaults, options);
3650
-
3651
- if (options.view) {
3652
- result = $templateFactory.fromConfig(options.view, options.params, options.locals);
3653
- }
3654
- if (result && options.notify) {
3655
- /**
3656
- * @ngdoc event
3657
- * @name ui.router.state.$state#$viewContentLoading
3658
- * @eventOf ui.router.state.$view
3659
- * @eventType broadcast on root scope
3660
- * @description
3661
- *
3662
- * Fired once the view **begins loading**, *before* the DOM is rendered.
3663
- *
3664
- * @param {Object} event Event object.
3665
- * @param {Object} viewConfig The view config properties (template, controller, etc).
3666
- *
3667
- * @example
3668
- *
3669
- * <pre>
3670
- * $scope.$on('$viewContentLoading',
3671
- * function(event, viewConfig){
3672
- * // Access to all the view config properties.
3673
- * // and one special property 'targetView'
3674
- * // viewConfig.targetView
3675
- * });
3676
- * </pre>
3677
- */
3678
- $rootScope.$broadcast('$viewContentLoading', options);
3679
- }
3680
- return result;
3681
- }
3682
- };
3683
- }
3684
- }
3685
-
3686
- angular.module('ui.router.state').provider('$view', $ViewProvider);
3687
-
3688
- /**
3689
- * @ngdoc object
3690
- * @name ui.router.state.$uiViewScrollProvider
3691
- *
3692
- * @description
3693
- * Provider that returns the {@link ui.router.state.$uiViewScroll} service function.
3694
- */
3695
- function $ViewScrollProvider() {
3696
-
3697
- var useAnchorScroll = false;
3698
-
3699
- /**
3700
- * @ngdoc function
3701
- * @name ui.router.state.$uiViewScrollProvider#useAnchorScroll
3702
- * @methodOf ui.router.state.$uiViewScrollProvider
3703
- *
3704
- * @description
3705
- * Reverts back to using the core [`$anchorScroll`](http://docs.angularjs.org/api/ng.$anchorScroll) service for
3706
- * scrolling based on the url anchor.
3707
- */
3708
- this.useAnchorScroll = function () {
3709
- useAnchorScroll = true;
3710
- };
3711
-
3712
- /**
3713
- * @ngdoc object
3714
- * @name ui.router.state.$uiViewScroll
3715
- *
3716
- * @requires $anchorScroll
3717
- * @requires $timeout
3718
- *
3719
- * @description
3720
- * When called with a jqLite element, it scrolls the element into view (after a
3721
- * `$timeout` so the DOM has time to refresh).
3722
- *
3723
- * If you prefer to rely on `$anchorScroll` to scroll the view to the anchor,
3724
- * this can be enabled by calling {@link ui.router.state.$uiViewScrollProvider#methods_useAnchorScroll `$uiViewScrollProvider.useAnchorScroll()`}.
3725
- */
3726
- this.$get = ['$anchorScroll', '$timeout', function ($anchorScroll, $timeout) {
3727
- if (useAnchorScroll) {
3728
- return $anchorScroll;
3729
- }
3730
-
3731
- return function ($element) {
3732
- return $timeout(function () {
3733
- $element[0].scrollIntoView();
3734
- }, 0, false);
3735
- };
3736
- }];
3737
- }
3738
-
3739
- angular.module('ui.router.state').provider('$uiViewScroll', $ViewScrollProvider);
3740
-
3741
- /**
3742
- * @ngdoc directive
3743
- * @name ui.router.state.directive:ui-view
3744
- *
3745
- * @requires ui.router.state.$state
3746
- * @requires $compile
3747
- * @requires $controller
3748
- * @requires $injector
3749
- * @requires ui.router.state.$uiViewScroll
3750
- * @requires $document
3751
- *
3752
- * @restrict ECA
3753
- *
3754
- * @description
3755
- * The ui-view directive tells $state where to place your templates.
3756
- *
3757
- * @param {string=} name A view name. The name should be unique amongst the other views in the
3758
- * same state. You can have views of the same name that live in different states.
3759
- *
3760
- * @param {string=} autoscroll It allows you to set the scroll behavior of the browser window
3761
- * when a view is populated. By default, $anchorScroll is overridden by ui-router's custom scroll
3762
- * service, {@link ui.router.state.$uiViewScroll}. This custom service let's you
3763
- * scroll ui-view elements into view when they are populated during a state activation.
3764
- *
3765
- * *Note: To revert back to old [`$anchorScroll`](http://docs.angularjs.org/api/ng.$anchorScroll)
3766
- * functionality, call `$uiViewScrollProvider.useAnchorScroll()`.*
3767
- *
3768
- * @param {string=} onload Expression to evaluate whenever the view updates.
3769
- *
3770
- * @example
3771
- * A view can be unnamed or named.
3772
- * <pre>
3773
- * <!-- Unnamed -->
3774
- * <div ui-view></div>
3775
- *
3776
- * <!-- Named -->
3777
- * <div ui-view="viewName"></div>
3778
- * </pre>
3779
- *
3780
- * You can only have one unnamed view within any template (or root html). If you are only using a
3781
- * single view and it is unnamed then you can populate it like so:
3782
- * <pre>
3783
- * <div ui-view></div>
3784
- * $stateProvider.state("home", {
3785
- * template: "<h1>HELLO!</h1>"
3786
- * })
3787
- * </pre>
3788
- *
3789
- * The above is a convenient shortcut equivalent to specifying your view explicitly with the {@link ui.router.state.$stateProvider#views `views`}
3790
- * config property, by name, in this case an empty name:
3791
- * <pre>
3792
- * $stateProvider.state("home", {
3793
- * views: {
3794
- * "": {
3795
- * template: "<h1>HELLO!</h1>"
3796
- * }
3797
- * }
3798
- * })
3799
- * </pre>
3800
- *
3801
- * But typically you'll only use the views property if you name your view or have more than one view
3802
- * in the same template. There's not really a compelling reason to name a view if its the only one,
3803
- * but you could if you wanted, like so:
3804
- * <pre>
3805
- * <div ui-view="main"></div>
3806
- * </pre>
3807
- * <pre>
3808
- * $stateProvider.state("home", {
3809
- * views: {
3810
- * "main": {
3811
- * template: "<h1>HELLO!</h1>"
3812
- * }
3813
- * }
3814
- * })
3815
- * </pre>
3816
- *
3817
- * Really though, you'll use views to set up multiple views:
3818
- * <pre>
3819
- * <div ui-view></div>
3820
- * <div ui-view="chart"></div>
3821
- * <div ui-view="data"></div>
3822
- * </pre>
3823
- *
3824
- * <pre>
3825
- * $stateProvider.state("home", {
3826
- * views: {
3827
- * "": {
3828
- * template: "<h1>HELLO!</h1>"
3829
- * },
3830
- * "chart": {
3831
- * template: "<chart_thing/>"
3832
- * },
3833
- * "data": {
3834
- * template: "<data_thing/>"
3835
- * }
3836
- * }
3837
- * })
3838
- * </pre>
3839
- *
3840
- * Examples for `autoscroll`:
3841
- *
3842
- * <pre>
3843
- * <!-- If autoscroll present with no expression,
3844
- * then scroll ui-view into view -->
3845
- * <ui-view autoscroll/>
3846
- *
3847
- * <!-- If autoscroll present with valid expression,
3848
- * then scroll ui-view into view if expression evaluates to true -->
3849
- * <ui-view autoscroll='true'/>
3850
- * <ui-view autoscroll='false'/>
3851
- * <ui-view autoscroll='scopeVariable'/>
3852
- * </pre>
3853
- */
3854
- $ViewDirective.$inject = ['$state', '$injector', '$uiViewScroll', '$interpolate'];
3855
- function $ViewDirective( $state, $injector, $uiViewScroll, $interpolate) {
3856
-
3857
- function getService() {
3858
- return ($injector.has) ? function(service) {
3859
- return $injector.has(service) ? $injector.get(service) : null;
3860
- } : function(service) {
3861
- try {
3862
- return $injector.get(service);
3863
- } catch (e) {
3864
- return null;
3865
- }
3866
- };
3867
- }
3868
-
3869
- var service = getService(),
3870
- $animator = service('$animator'),
3871
- $animate = service('$animate');
3872
-
3873
- // Returns a set of DOM manipulation functions based on which Angular version
3874
- // it should use
3875
- function getRenderer(attrs, scope) {
3876
- var statics = function() {
3877
- return {
3878
- enter: function (element, target, cb) { target.after(element); cb(); },
3879
- leave: function (element, cb) { element.remove(); cb(); }
3880
- };
3881
- };
3882
-
3883
- if ($animate) {
3884
- return {
3885
- enter: function(element, target, cb) {
3886
- var promise = $animate.enter(element, null, target, cb);
3887
- if (promise && promise.then) promise.then(cb);
3888
- },
3889
- leave: function(element, cb) {
3890
- var promise = $animate.leave(element, cb);
3891
- if (promise && promise.then) promise.then(cb);
3892
- }
3893
- };
3894
- }
3895
-
3896
- if ($animator) {
3897
- var animate = $animator && $animator(scope, attrs);
3898
-
3899
- return {
3900
- enter: function(element, target, cb) {animate.enter(element, null, target); cb(); },
3901
- leave: function(element, cb) { animate.leave(element); cb(); }
3902
- };
3903
- }
3904
-
3905
- return statics();
3906
- }
3907
-
3908
- var directive = {
3909
- restrict: 'ECA',
3910
- terminal: true,
3911
- priority: 400,
3912
- transclude: 'element',
3913
- compile: function (tElement, tAttrs, $transclude) {
3914
- return function (scope, $element, attrs) {
3915
- var previousEl, currentEl, currentScope, latestLocals,
3916
- onloadExp = attrs.onload || '',
3917
- autoScrollExp = attrs.autoscroll,
3918
- renderer = getRenderer(attrs, scope);
3919
-
3920
- scope.$on('$stateChangeSuccess', function() {
3921
- updateView(false);
3922
- });
3923
- scope.$on('$viewContentLoading', function() {
3924
- updateView(false);
3925
- });
3926
-
3927
- updateView(true);
3928
-
3929
- function cleanupLastView() {
3930
- if (previousEl) {
3931
- previousEl.remove();
3932
- previousEl = null;
3933
- }
3934
-
3935
- if (currentScope) {
3936
- currentScope.$destroy();
3937
- currentScope = null;
3938
- }
3939
-
3940
- if (currentEl) {
3941
- renderer.leave(currentEl, function() {
3942
- previousEl = null;
3943
- });
3944
-
3945
- previousEl = currentEl;
3946
- currentEl = null;
3947
- }
3948
- }
3949
-
3950
- function updateView(firstTime) {
3951
- var newScope,
3952
- name = getUiViewName(scope, attrs, $element, $interpolate),
3953
- previousLocals = name && $state.$current && $state.$current.locals[name];
3954
-
3955
- if (!firstTime && previousLocals === latestLocals) return; // nothing to do
3956
- newScope = scope.$new();
3957
- latestLocals = $state.$current.locals[name];
3958
-
3959
- var clone = $transclude(newScope, function(clone) {
3960
- renderer.enter(clone, $element, function onUiViewEnter() {
3961
- if(currentScope) {
3962
- currentScope.$emit('$viewContentAnimationEnded');
3963
- }
3964
-
3965
- if (angular.isDefined(autoScrollExp) && !autoScrollExp || scope.$eval(autoScrollExp)) {
3966
- $uiViewScroll(clone);
3967
- }
3968
- });
3969
- cleanupLastView();
3970
- });
3971
-
3972
- currentEl = clone;
3973
- currentScope = newScope;
3974
- /**
3975
- * @ngdoc event
3976
- * @name ui.router.state.directive:ui-view#$viewContentLoaded
3977
- * @eventOf ui.router.state.directive:ui-view
3978
- * @eventType emits on ui-view directive scope
3979
- * @description *
3980
- * Fired once the view is **loaded**, *after* the DOM is rendered.
3981
- *
3982
- * @param {Object} event Event object.
3983
- */
3984
- currentScope.$emit('$viewContentLoaded');
3985
- currentScope.$eval(onloadExp);
3986
- }
3987
- };
3988
- }
3989
- };
3990
-
3991
- return directive;
3992
- }
3993
-
3994
- $ViewDirectiveFill.$inject = ['$compile', '$controller', '$state', '$interpolate'];
3995
- function $ViewDirectiveFill ( $compile, $controller, $state, $interpolate) {
3996
- return {
3997
- restrict: 'ECA',
3998
- priority: -400,
3999
- compile: function (tElement) {
4000
- var initial = tElement.html();
4001
- return function (scope, $element, attrs) {
4002
- var current = $state.$current,
4003
- name = getUiViewName(scope, attrs, $element, $interpolate),
4004
- locals = current && current.locals[name];
4005
-
4006
- if (! locals) {
4007
- return;
4008
- }
4009
-
4010
- $element.data('$uiView', { name: name, state: locals.$$state });
4011
- $element.html(locals.$template ? locals.$template : initial);
4012
-
4013
- var link = $compile($element.contents());
4014
-
4015
- if (locals.$$controller) {
4016
- locals.$scope = scope;
4017
- locals.$element = $element;
4018
- var controller = $controller(locals.$$controller, locals);
4019
- if (locals.$$controllerAs) {
4020
- scope[locals.$$controllerAs] = controller;
4021
- }
4022
- $element.data('$ngControllerController', controller);
4023
- $element.children().data('$ngControllerController', controller);
4024
- }
4025
-
4026
- link(scope);
4027
- };
4028
- }
4029
- };
4030
- }
4031
-
4032
- /**
4033
- * Shared ui-view code for both directives:
4034
- * Given scope, element, and its attributes, return the view's name
4035
- */
4036
- function getUiViewName(scope, attrs, element, $interpolate) {
4037
- var name = $interpolate(attrs.uiView || attrs.name || '')(scope);
4038
- var inherited = element.inheritedData('$uiView');
4039
- return name.indexOf('@') >= 0 ? name : (name + '@' + (inherited ? inherited.state.name : ''));
4040
- }
4041
-
4042
- angular.module('ui.router.state').directive('uiView', $ViewDirective);
4043
- angular.module('ui.router.state').directive('uiView', $ViewDirectiveFill);
4044
-
4045
- function parseStateRef(ref, current) {
4046
- var preparsed = ref.match(/^\s*({[^}]*})\s*$/), parsed;
4047
- if (preparsed) ref = current + '(' + preparsed[1] + ')';
4048
- parsed = ref.replace(/\n/g, " ").match(/^([^(]+?)\s*(\((.*)\))?$/);
4049
- if (!parsed || parsed.length !== 4) throw new Error("Invalid state ref '" + ref + "'");
4050
- return { state: parsed[1], paramExpr: parsed[3] || null };
4051
- }
4052
-
4053
- function stateContext(el) {
4054
- var stateData = el.parent().inheritedData('$uiView');
4055
-
4056
- if (stateData && stateData.state && stateData.state.name) {
4057
- return stateData.state;
4058
- }
4059
- }
4060
-
4061
- /**
4062
- * @ngdoc directive
4063
- * @name ui.router.state.directive:ui-sref
4064
- *
4065
- * @requires ui.router.state.$state
4066
- * @requires $timeout
4067
- *
4068
- * @restrict A
4069
- *
4070
- * @description
4071
- * A directive that binds a link (`<a>` tag) to a state. If the state has an associated
4072
- * URL, the directive will automatically generate & update the `href` attribute via
4073
- * the {@link ui.router.state.$state#methods_href $state.href()} method. Clicking
4074
- * the link will trigger a state transition with optional parameters.
4075
- *
4076
- * Also middle-clicking, right-clicking, and ctrl-clicking on the link will be
4077
- * handled natively by the browser.
4078
- *
4079
- * You can also use relative state paths within ui-sref, just like the relative
4080
- * paths passed to `$state.go()`. You just need to be aware that the path is relative
4081
- * to the state that the link lives in, in other words the state that loaded the
4082
- * template containing the link.
4083
- *
4084
- * You can specify options to pass to {@link ui.router.state.$state#go $state.go()}
4085
- * using the `ui-sref-opts` attribute. Options are restricted to `location`, `inherit`,
4086
- * and `reload`.
4087
- *
4088
- * @example
4089
- * Here's an example of how you'd use ui-sref and how it would compile. If you have the
4090
- * following template:
4091
- * <pre>
4092
- * <a ui-sref="home">Home</a> | <a ui-sref="about">About</a> | <a ui-sref="{page: 2}">Next page</a>
4093
- *
4094
- * <ul>
4095
- * <li ng-repeat="contact in contacts">
4096
- * <a ui-sref="contacts.detail({ id: contact.id })">{{ contact.name }}</a>
4097
- * </li>
4098
- * </ul>
4099
- * </pre>
4100
- *
4101
- * Then the compiled html would be (assuming Html5Mode is off and current state is contacts):
4102
- * <pre>
4103
- * <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>
4104
- *
4105
- * <ul>
4106
- * <li ng-repeat="contact in contacts">
4107
- * <a href="#/contacts/1" ui-sref="contacts.detail({ id: contact.id })">Joe</a>
4108
- * </li>
4109
- * <li ng-repeat="contact in contacts">
4110
- * <a href="#/contacts/2" ui-sref="contacts.detail({ id: contact.id })">Alice</a>
4111
- * </li>
4112
- * <li ng-repeat="contact in contacts">
4113
- * <a href="#/contacts/3" ui-sref="contacts.detail({ id: contact.id })">Bob</a>
4114
- * </li>
4115
- * </ul>
4116
- *
4117
- * <a ui-sref="home" ui-sref-opts="{reload: true}">Home</a>
4118
- * </pre>
4119
- *
4120
- * @param {string} ui-sref 'stateName' can be any valid absolute or relative state
4121
- * @param {Object} ui-sref-opts options to pass to {@link ui.router.state.$state#go $state.go()}
4122
- */
4123
- $StateRefDirective.$inject = ['$state', '$timeout'];
4124
- function $StateRefDirective($state, $timeout) {
4125
- var allowedOptions = ['location', 'inherit', 'reload', 'absolute'];
4126
-
4127
- return {
4128
- restrict: 'A',
4129
- require: ['?^uiSrefActive', '?^uiSrefActiveEq'],
4130
- link: function(scope, element, attrs, uiSrefActive) {
4131
- var ref = parseStateRef(attrs.uiSref, $state.current.name);
4132
- var params = null, url = null, base = stateContext(element) || $state.$current;
4133
- // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
4134
- var hrefKind = Object.prototype.toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
4135
- 'xlink:href' : 'href';
4136
- var newHref = null, isAnchor = element.prop("tagName").toUpperCase() === "A";
4137
- var isForm = element[0].nodeName === "FORM";
4138
- var attr = isForm ? "action" : hrefKind, nav = true;
4139
-
4140
- var options = { relative: base, inherit: true };
4141
- var optionsOverride = scope.$eval(attrs.uiSrefOpts) || {};
4142
-
4143
- angular.forEach(allowedOptions, function(option) {
4144
- if (option in optionsOverride) {
4145
- options[option] = optionsOverride[option];
4146
- }
4147
- });
4148
-
4149
- var update = function(newVal) {
4150
- if (newVal) params = angular.copy(newVal);
4151
- if (!nav) return;
4152
-
4153
- newHref = $state.href(ref.state, params, options);
4154
-
4155
- var activeDirective = uiSrefActive[1] || uiSrefActive[0];
4156
- if (activeDirective) {
4157
- activeDirective.$$addStateInfo(ref.state, params);
4158
- }
4159
- if (newHref === null) {
4160
- nav = false;
4161
- return false;
4162
- }
4163
- attrs.$set(attr, newHref);
4164
- };
4165
-
4166
- if (ref.paramExpr) {
4167
- scope.$watch(ref.paramExpr, function(newVal, oldVal) {
4168
- if (newVal !== params) update(newVal);
4169
- }, true);
4170
- params = angular.copy(scope.$eval(ref.paramExpr));
4171
- }
4172
- update();
4173
-
4174
- if (isForm) return;
4175
-
4176
- element.bind("click", function(e) {
4177
- var button = e.which || e.button;
4178
- if ( !(button > 1 || e.ctrlKey || e.metaKey || e.shiftKey || element.attr('target')) ) {
4179
- // HACK: This is to allow ng-clicks to be processed before the transition is initiated:
4180
- var transition = $timeout(function() {
4181
- $state.go(ref.state, params, options);
4182
- });
4183
- e.preventDefault();
4184
-
4185
- // if the state has no URL, ignore one preventDefault from the <a> directive.
4186
- var ignorePreventDefaultCount = isAnchor && !newHref ? 1: 0;
4187
- e.preventDefault = function() {
4188
- if (ignorePreventDefaultCount-- <= 0)
4189
- $timeout.cancel(transition);
4190
- };
4191
- }
4192
- });
4193
- }
4194
- };
4195
- }
4196
-
4197
- /**
4198
- * @ngdoc directive
4199
- * @name ui.router.state.directive:ui-sref-active
4200
- *
4201
- * @requires ui.router.state.$state
4202
- * @requires ui.router.state.$stateParams
4203
- * @requires $interpolate
4204
- *
4205
- * @restrict A
4206
- *
4207
- * @description
4208
- * A directive working alongside ui-sref to add classes to an element when the
4209
- * related ui-sref directive's state is active, and removing them when it is inactive.
4210
- * The primary use-case is to simplify the special appearance of navigation menus
4211
- * relying on `ui-sref`, by having the "active" state's menu button appear different,
4212
- * distinguishing it from the inactive menu items.
4213
- *
4214
- * ui-sref-active can live on the same element as ui-sref or on a parent element. The first
4215
- * ui-sref-active found at the same level or above the ui-sref will be used.
4216
- *
4217
- * Will activate when the ui-sref's target state or any child state is active. If you
4218
- * need to activate only when the ui-sref target state is active and *not* any of
4219
- * it's children, then you will use
4220
- * {@link ui.router.state.directive:ui-sref-active-eq ui-sref-active-eq}
4221
- *
4222
- * @example
4223
- * Given the following template:
4224
- * <pre>
4225
- * <ul>
4226
- * <li ui-sref-active="active" class="item">
4227
- * <a href ui-sref="app.user({user: 'bilbobaggins'})">@bilbobaggins</a>
4228
- * </li>
4229
- * </ul>
4230
- * </pre>
4231
- *
4232
- *
4233
- * When the app state is "app.user" (or any children states), and contains the state parameter "user" with value "bilbobaggins",
4234
- * the resulting HTML will appear as (note the 'active' class):
4235
- * <pre>
4236
- * <ul>
4237
- * <li ui-sref-active="active" class="item active">
4238
- * <a ui-sref="app.user({user: 'bilbobaggins'})" href="/users/bilbobaggins">@bilbobaggins</a>
4239
- * </li>
4240
- * </ul>
4241
- * </pre>
4242
- *
4243
- * The class name is interpolated **once** during the directives link time (any further changes to the
4244
- * interpolated value are ignored).
4245
- *
4246
- * Multiple classes may be specified in a space-separated format:
4247
- * <pre>
4248
- * <ul>
4249
- * <li ui-sref-active='class1 class2 class3'>
4250
- * <a ui-sref="app.user">link</a>
4251
- * </li>
4252
- * </ul>
4253
- * </pre>
4254
- */
4255
-
4256
- /**
4257
- * @ngdoc directive
4258
- * @name ui.router.state.directive:ui-sref-active-eq
4259
- *
4260
- * @requires ui.router.state.$state
4261
- * @requires ui.router.state.$stateParams
4262
- * @requires $interpolate
4263
- *
4264
- * @restrict A
4265
- *
4266
- * @description
4267
- * The same as {@link ui.router.state.directive:ui-sref-active ui-sref-active} but will only activate
4268
- * when the exact target state used in the `ui-sref` is active; no child states.
4269
- *
4270
- */
4271
- $StateRefActiveDirective.$inject = ['$state', '$stateParams', '$interpolate'];
4272
- function $StateRefActiveDirective($state, $stateParams, $interpolate) {
4273
- return {
4274
- restrict: "A",
4275
- controller: ['$scope', '$element', '$attrs', function ($scope, $element, $attrs) {
4276
- var states = [], activeClass;
4277
-
4278
- // There probably isn't much point in $observing this
4279
- // uiSrefActive and uiSrefActiveEq share the same directive object with some
4280
- // slight difference in logic routing
4281
- activeClass = $interpolate($attrs.uiSrefActiveEq || $attrs.uiSrefActive || '', false)($scope);
4282
-
4283
- // Allow uiSref to communicate with uiSrefActive[Equals]
4284
- this.$$addStateInfo = function (newState, newParams) {
4285
- var state = $state.get(newState, stateContext($element));
4286
-
4287
- states.push({
4288
- state: state || { name: newState },
4289
- params: newParams
4290
- });
4291
-
4292
- update();
4293
- };
4294
-
4295
- $scope.$on('$stateChangeSuccess', update);
4296
-
4297
- // Update route state
4298
- function update() {
4299
- if (anyMatch()) {
4300
- $element.addClass(activeClass);
4301
- } else {
4302
- $element.removeClass(activeClass);
4303
- }
4304
- }
4305
-
4306
- function anyMatch() {
4307
- for (var i = 0; i < states.length; i++) {
4308
- if (isMatch(states[i].state, states[i].params)) {
4309
- return true;
4310
- }
4311
- }
4312
- return false;
4313
- }
4314
-
4315
- function isMatch(state, params) {
4316
- if (typeof $attrs.uiSrefActiveEq !== 'undefined') {
4317
- return $state.is(state.name, params);
4318
- } else {
4319
- return $state.includes(state.name, params);
4320
- }
4321
- }
4322
- }]
4323
- };
4324
- }
4325
-
4326
- angular.module('ui.router.state')
4327
- .directive('uiSref', $StateRefDirective)
4328
- .directive('uiSrefActive', $StateRefActiveDirective)
4329
- .directive('uiSrefActiveEq', $StateRefActiveDirective);
4330
-
4331
- /**
4332
- * @ngdoc filter
4333
- * @name ui.router.state.filter:isState
4334
- *
4335
- * @requires ui.router.state.$state
4336
- *
4337
- * @description
4338
- * Translates to {@link ui.router.state.$state#methods_is $state.is("stateName")}.
4339
- */
4340
- $IsStateFilter.$inject = ['$state'];
4341
- function $IsStateFilter($state) {
4342
- var isFilter = function (state) {
4343
- return $state.is(state);
4344
- };
4345
- isFilter.$stateful = true;
4346
- return isFilter;
4347
- }
4348
-
4349
- /**
4350
- * @ngdoc filter
4351
- * @name ui.router.state.filter:includedByState
4352
- *
4353
- * @requires ui.router.state.$state
4354
- *
4355
- * @description
4356
- * Translates to {@link ui.router.state.$state#methods_includes $state.includes('fullOrPartialStateName')}.
4357
- */
4358
- $IncludedByStateFilter.$inject = ['$state'];
4359
- function $IncludedByStateFilter($state) {
4360
- var includesFilter = function (state) {
4361
- return $state.includes(state);
4362
- };
4363
- includesFilter.$stateful = true;
4364
- return includesFilter;
4365
- }
4366
-
4367
- angular.module('ui.router.state')
4368
- .filter('isState', $IsStateFilter)
4369
- .filter('includedByState', $IncludedByStateFilter);
4370
- })(window, window.angular);