praxis 0.16.1 → 0.17.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rspec +0 -1
- data/.ruby-version +1 -1
- data/.travis.yml +2 -1
- data/CHANGELOG.md +41 -0
- data/CONTRIBUTING.md +3 -0
- data/lib/api_browser/Gruntfile.js +20 -4
- data/lib/api_browser/app/bower_components/angular-mocks/.bower.json +6 -6
- data/lib/api_browser/app/bower_components/angular-mocks/README.md +11 -5
- data/lib/api_browser/app/bower_components/angular-mocks/angular-mocks.js +475 -216
- data/lib/api_browser/app/bower_components/angular-mocks/bower.json +2 -2
- data/lib/api_browser/app/bower_components/angular-mocks/ngAnimateMock.js +2 -0
- data/lib/api_browser/app/bower_components/angular-mocks/ngMock.js +2 -0
- data/lib/api_browser/app/bower_components/angular-mocks/ngMockE2E.js +2 -0
- data/lib/api_browser/app/bower_components/angular-mocks/package.json +1 -1
- data/lib/api_browser/app/bower_components/angular-sanitize/.bower.json +8 -8
- data/lib/api_browser/app/bower_components/angular-sanitize/README.md +19 -5
- data/lib/api_browser/app/bower_components/angular-sanitize/angular-sanitize.js +186 -127
- data/lib/api_browser/app/bower_components/angular-sanitize/angular-sanitize.min.js +12 -10
- data/lib/api_browser/app/bower_components/angular-sanitize/angular-sanitize.min.js.map +3 -3
- data/lib/api_browser/app/bower_components/angular-sanitize/bower.json +3 -2
- data/lib/api_browser/app/bower_components/angular-sanitize/index.js +2 -0
- data/lib/api_browser/app/bower_components/angular-sanitize/package.json +26 -0
- data/lib/api_browser/app/bower_components/angular-ui-bootstrap-bower/.bower.json +15 -8
- data/lib/api_browser/app/bower_components/angular-ui-bootstrap-bower/bower.json +11 -3
- data/lib/api_browser/app/bower_components/angular-ui-bootstrap-bower/ui-bootstrap-csp.css +6 -0
- data/lib/api_browser/app/bower_components/angular-ui-bootstrap-bower/ui-bootstrap-tpls.js +1177 -453
- data/lib/api_browser/app/bower_components/angular-ui-bootstrap-bower/ui-bootstrap-tpls.min.js +4 -4
- data/lib/api_browser/app/bower_components/angular-ui-bootstrap-bower/ui-bootstrap.js +1066 -404
- data/lib/api_browser/app/bower_components/angular-ui-bootstrap-bower/ui-bootstrap.min.js +3 -3
- data/lib/api_browser/app/bower_components/angular-ui-router/.bower.json +5 -6
- data/lib/api_browser/app/bower_components/angular-ui-router/CHANGELOG.md +208 -3
- data/lib/api_browser/app/bower_components/angular-ui-router/CONTRIBUTING.md +65 -0
- data/lib/api_browser/app/bower_components/angular-ui-router/LICENSE +1 -1
- data/lib/api_browser/app/bower_components/angular-ui-router/README.md +36 -71
- data/lib/api_browser/app/bower_components/angular-ui-router/api/angular-ui-router.d.ts +126 -0
- data/lib/api_browser/app/bower_components/angular-ui-router/bower.json +1 -1
- data/lib/api_browser/app/bower_components/angular-ui-router/release/angular-ui-router.js +1902 -755
- data/lib/api_browser/app/bower_components/angular-ui-router/release/angular-ui-router.min.js +2 -2
- data/lib/api_browser/app/bower_components/angular-ui-router/src/common.js +69 -23
- data/lib/api_browser/app/bower_components/angular-ui-router/src/resolve.js +15 -5
- data/lib/api_browser/app/bower_components/angular-ui-router/src/state.js +556 -295
- data/lib/api_browser/app/bower_components/angular-ui-router/src/stateDirectives.js +101 -42
- data/lib/api_browser/app/bower_components/angular-ui-router/src/stateFilters.js +6 -2
- data/lib/api_browser/app/bower_components/angular-ui-router/src/templateFactory.js +2 -2
- data/lib/api_browser/app/bower_components/angular-ui-router/src/urlMatcherFactory.js +822 -97
- data/lib/api_browser/app/bower_components/angular-ui-router/src/urlRouter.js +274 -120
- data/lib/api_browser/app/bower_components/angular-ui-router/src/viewDirective.js +33 -20
- data/lib/api_browser/app/bower_components/angular-ui-router/src/viewScroll.js +1 -1
- data/lib/api_browser/app/bower_components/angular/.bower.json +5 -5
- data/lib/api_browser/app/bower_components/angular/README.md +2 -5
- data/lib/api_browser/app/bower_components/angular/angular-csp.css +5 -8
- data/lib/api_browser/app/bower_components/angular/angular.js +12975 -6996
- data/lib/api_browser/app/bower_components/angular/angular.min.js +285 -213
- data/lib/api_browser/app/bower_components/angular/angular.min.js.gzip +0 -0
- data/lib/api_browser/app/bower_components/angular/angular.min.js.map +3 -3
- data/lib/api_browser/app/bower_components/angular/bower.json +1 -1
- data/lib/api_browser/app/bower_components/angular/index.js +2 -0
- data/lib/api_browser/app/bower_components/angular/package.json +2 -2
- data/lib/api_browser/app/bower_components/bootstrap-sass/.bower.json +31 -16
- data/lib/api_browser/app/bower_components/bootstrap-sass/CHANGELOG.md +108 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/CONTRIBUTING.md +55 -37
- data/lib/api_browser/app/bower_components/bootstrap-sass/README.md +147 -206
- data/lib/api_browser/app/bower_components/bootstrap-sass/bower.json +19 -8
- data/lib/api_browser/app/bower_components/bootstrap-sass/{dist/fonts → vendor/assets/fonts/bootstrap}/glyphicons-halflings-regular.eot +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/{dist/fonts → vendor/assets/fonts/bootstrap}/glyphicons-halflings-regular.svg +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/{dist/fonts → vendor/assets/fonts/bootstrap}/glyphicons-halflings-regular.ttf +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/{dist/fonts → vendor/assets/fonts/bootstrap}/glyphicons-halflings-regular.woff +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/javascripts/bootstrap.js +12 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/{js → vendor/assets/javascripts/bootstrap}/affix.js +1 -1
- data/lib/api_browser/app/bower_components/bootstrap-sass/{js → vendor/assets/javascripts/bootstrap}/alert.js +1 -1
- data/lib/api_browser/app/bower_components/bootstrap-sass/{js → vendor/assets/javascripts/bootstrap}/button.js +11 -5
- data/lib/api_browser/app/bower_components/bootstrap-sass/{js → vendor/assets/javascripts/bootstrap}/carousel.js +5 -5
- data/lib/api_browser/app/bower_components/bootstrap-sass/{js → vendor/assets/javascripts/bootstrap}/collapse.js +1 -1
- data/lib/api_browser/app/bower_components/bootstrap-sass/{js → vendor/assets/javascripts/bootstrap}/dropdown.js +5 -5
- data/lib/api_browser/app/bower_components/bootstrap-sass/{js → vendor/assets/javascripts/bootstrap}/modal.js +1 -1
- data/lib/api_browser/app/bower_components/bootstrap-sass/{js → vendor/assets/javascripts/bootstrap}/popover.js +1 -1
- data/lib/api_browser/app/bower_components/bootstrap-sass/{js → vendor/assets/javascripts/bootstrap}/scrollspy.js +2 -2
- data/lib/api_browser/app/bower_components/bootstrap-sass/{js → vendor/assets/javascripts/bootstrap}/tab.js +1 -1
- data/lib/api_browser/app/bower_components/bootstrap-sass/{js → vendor/assets/javascripts/bootstrap}/tooltip.js +1 -1
- data/lib/api_browser/app/bower_components/bootstrap-sass/{js → vendor/assets/javascripts/bootstrap}/transition.js +1 -1
- data/lib/api_browser/app/bower_components/bootstrap-sass/vendor/assets/stylesheets/bootstrap.scss +1 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/{lib → vendor/assets/stylesheets/bootstrap}/_alerts.scss +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/{lib → vendor/assets/stylesheets/bootstrap}/_badges.scss +6 -6
- data/lib/api_browser/app/bower_components/bootstrap-sass/{lib → vendor/assets/stylesheets/bootstrap}/_breadcrumbs.scss +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/{lib → vendor/assets/stylesheets/bootstrap}/_button-groups.scss +7 -33
- data/lib/api_browser/app/bower_components/bootstrap-sass/{lib → vendor/assets/stylesheets/bootstrap}/_buttons.scss +2 -5
- data/lib/api_browser/app/bower_components/bootstrap-sass/{lib → vendor/assets/stylesheets/bootstrap}/_carousel.scss +1 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/{lib → vendor/assets/stylesheets/bootstrap}/_close.scss +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/{lib → vendor/assets/stylesheets/bootstrap}/_code.scss +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/{lib → vendor/assets/stylesheets/bootstrap}/_component-animations.scss +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/{lib → vendor/assets/stylesheets/bootstrap}/_dropdowns.scss +3 -8
- data/lib/api_browser/app/bower_components/bootstrap-sass/{lib → vendor/assets/stylesheets/bootstrap}/_forms.scss +11 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/{lib → vendor/assets/stylesheets/bootstrap}/_glyphicons.scss +5 -5
- data/lib/api_browser/app/bower_components/bootstrap-sass/{lib → vendor/assets/stylesheets/bootstrap}/_grid.scss +12 -26
- data/lib/api_browser/app/bower_components/bootstrap-sass/{lib → vendor/assets/stylesheets/bootstrap}/_input-groups.scss +1 -1
- data/lib/api_browser/app/bower_components/bootstrap-sass/{lib → vendor/assets/stylesheets/bootstrap}/_jumbotron.scss +8 -2
- data/lib/api_browser/app/bower_components/bootstrap-sass/{lib → vendor/assets/stylesheets/bootstrap}/_labels.scss +6 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/{lib → vendor/assets/stylesheets/bootstrap}/_list-group.scss +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/{lib → vendor/assets/stylesheets/bootstrap}/_media.scss +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/{lib → vendor/assets/stylesheets/bootstrap}/_mixins.scss +38 -51
- data/lib/api_browser/app/bower_components/bootstrap-sass/{lib → vendor/assets/stylesheets/bootstrap}/_modals.scss +2 -5
- data/lib/api_browser/app/bower_components/bootstrap-sass/{lib → vendor/assets/stylesheets/bootstrap}/_navbar.scss +41 -53
- data/lib/api_browser/app/bower_components/bootstrap-sass/{lib → vendor/assets/stylesheets/bootstrap}/_navs.scss +0 -20
- data/lib/api_browser/app/bower_components/bootstrap-sass/{lib → vendor/assets/stylesheets/bootstrap}/_normalize.scss +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/{lib → vendor/assets/stylesheets/bootstrap}/_pager.scss +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/{lib → vendor/assets/stylesheets/bootstrap}/_pagination.scss +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/{lib → vendor/assets/stylesheets/bootstrap}/_panels.scss +11 -1
- data/lib/api_browser/app/bower_components/bootstrap-sass/{lib → vendor/assets/stylesheets/bootstrap}/_popovers.scss +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/{lib → vendor/assets/stylesheets/bootstrap}/_print.scss +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/{lib → vendor/assets/stylesheets/bootstrap}/_progress-bars.scss +0 -12
- data/lib/api_browser/app/bower_components/bootstrap-sass/{lib → vendor/assets/stylesheets/bootstrap}/_responsive-utilities.scss +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/{lib → vendor/assets/stylesheets/bootstrap}/_scaffolding.scss +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/{lib → vendor/assets/stylesheets/bootstrap}/_tables.scss +5 -18
- data/lib/api_browser/app/bower_components/bootstrap-sass/{lib → vendor/assets/stylesheets/bootstrap}/_theme.scss +2 -2
- data/lib/api_browser/app/bower_components/bootstrap-sass/{lib → vendor/assets/stylesheets/bootstrap}/_thumbnails.scss +9 -3
- data/lib/api_browser/app/bower_components/bootstrap-sass/{lib → vendor/assets/stylesheets/bootstrap}/_tooltip.scss +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/{lib → vendor/assets/stylesheets/bootstrap}/_type.scss +54 -52
- data/lib/api_browser/app/bower_components/bootstrap-sass/{lib → vendor/assets/stylesheets/bootstrap}/_utilities.scss +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/{lib → vendor/assets/stylesheets/bootstrap}/_variables.scss +20 -11
- data/lib/api_browser/app/bower_components/bootstrap-sass/{lib → vendor/assets/stylesheets/bootstrap}/_wells.scss +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/{lib → vendor/assets/stylesheets/bootstrap}/bootstrap.scss +0 -0
- data/lib/api_browser/app/bower_components/lodash/.bower.json +9 -13
- data/lib/api_browser/app/bower_components/lodash/LICENSE.txt +3 -3
- data/lib/api_browser/app/bower_components/lodash/bower.json +4 -7
- data/lib/api_browser/app/bower_components/lodash/lodash.js +12235 -0
- data/lib/api_browser/app/bower_components/lodash/lodash.min.js +98 -0
- data/lib/api_browser/app/index.html +0 -1
- data/lib/api_browser/app/js/app.js +2 -5
- data/lib/api_browser/app/js/controllers/action.js +21 -37
- data/lib/api_browser/app/js/controllers/controller.js +23 -1
- data/lib/api_browser/app/js/controllers/menu.js +46 -14
- data/lib/api_browser/app/js/controllers/type.js +2 -9
- data/lib/api_browser/app/js/directives/attribute_description.js +15 -5
- data/lib/api_browser/app/js/directives/attribute_table.js +6 -6
- data/lib/api_browser/app/js/directives/fixed_if_fits.js +20 -0
- data/lib/api_browser/app/js/directives/no_container.js +6 -6
- data/lib/api_browser/app/js/directives/type_placeholder.js +21 -0
- data/lib/api_browser/app/js/factories/Configuration.js +13 -0
- data/lib/api_browser/app/js/factories/Documentation.js +0 -3
- data/lib/api_browser/app/js/factories/normalize_attributes.js +19 -0
- data/lib/api_browser/app/js/factories/template_for.js +113 -0
- data/lib/api_browser/app/sass/modules/_body.scss +26 -4
- data/lib/api_browser/app/sass/modules/_sidebar.scss +68 -1
- data/lib/api_browser/app/sass/praxis.scss +1 -5
- data/lib/api_browser/app/sass/variables/_bootstrap-variables.scss +13 -4
- data/lib/api_browser/app/views/action.html +13 -17
- data/lib/api_browser/app/views/controller.html +32 -4
- data/lib/api_browser/app/views/directives/attribute_description.html +1 -1
- data/lib/api_browser/app/views/directives/attribute_description/{_default.html → default.html} +0 -0
- data/lib/api_browser/app/views/directives/attribute_description/example.html +13 -0
- data/lib/api_browser/app/views/directives/attribute_description/headers.html +8 -0
- data/lib/api_browser/app/views/directives/attribute_description/member_options.html +4 -0
- data/lib/api_browser/app/views/directives/attribute_description/values.html +14 -0
- data/lib/api_browser/app/views/directives/attribute_table.html +2 -2
- data/lib/api_browser/app/views/home.html +1 -3
- data/lib/api_browser/app/views/layout.html +3 -36
- data/lib/api_browser/app/views/menu.html +45 -0
- data/lib/api_browser/app/views/navbar.html +1 -1
- data/lib/api_browser/app/views/type.html +2 -2
- data/lib/api_browser/app/views/type/{_details.html → details.html} +6 -6
- data/lib/api_browser/app/views/types/embedded/default.html +10 -0
- data/lib/api_browser/app/views/types/embedded/links.html +11 -0
- data/lib/api_browser/app/views/types/embedded/struct.html +2 -0
- data/lib/api_browser/app/views/types/label/link.html +1 -0
- data/lib/api_browser/app/views/types/label/primitive.html +1 -0
- data/lib/api_browser/app/views/types/label/primitive_collection.html +1 -0
- data/lib/api_browser/app/views/types/label/type.html +1 -0
- data/lib/api_browser/app/views/types/label/type_collection.html +1 -0
- data/lib/api_browser/app/views/{directives/request_body/_default.html → types/standalone/default.html} +1 -1
- data/lib/api_browser/app/views/types/standalone/struct.html +1 -0
- data/lib/api_browser/bower.json +9 -9
- data/lib/api_browser/package.json +1 -1
- data/lib/praxis.rb +10 -4
- data/lib/praxis/action_definition.rb +16 -4
- data/lib/praxis/action_definition/headers_dsl_compiler.rb +5 -2
- data/lib/praxis/api_definition.rb +3 -1
- data/lib/praxis/api_general_info.rb +49 -5
- data/lib/praxis/application.rb +12 -4
- data/lib/praxis/bootloader.rb +1 -0
- data/lib/praxis/bootloader_stages/environment.rb +2 -0
- data/lib/praxis/bootloader_stages/routing.rb +1 -1
- data/lib/praxis/bootloader_stages/subgroup_loader.rb +1 -0
- data/lib/praxis/exceptions/validation.rb +7 -0
- data/lib/praxis/handlers/plain.rb +16 -0
- data/lib/praxis/handlers/xml.rb +4 -4
- data/lib/praxis/links.rb +13 -3
- data/lib/praxis/media_type_identifier.rb +3 -0
- data/lib/praxis/multipart/parser.rb +41 -48
- data/lib/praxis/multipart/part.rb +196 -3
- data/lib/praxis/request.rb +14 -11
- data/lib/praxis/request_stages/request_stage.rb +4 -0
- data/lib/praxis/request_stages/response.rb +10 -9
- data/lib/praxis/request_stages/validate.rb +1 -7
- data/lib/praxis/request_stages/validate_params_and_headers.rb +30 -5
- data/lib/praxis/request_stages/validate_payload.rb +14 -5
- data/lib/praxis/resource_definition.rb +117 -15
- data/lib/praxis/response.rb +6 -5
- data/lib/praxis/response_definition.rb +51 -5
- data/lib/praxis/responses/http.rb +5 -0
- data/lib/praxis/responses/multipart_ok.rb +51 -0
- data/lib/praxis/responses/validation_error.rb +7 -7
- data/lib/praxis/restful_doc_generator.rb +9 -4
- data/lib/praxis/route.rb +3 -2
- data/lib/praxis/router.rb +26 -16
- data/lib/praxis/router/rack.rb +51 -0
- data/lib/praxis/router/simple.rb +146 -0
- data/lib/praxis/routing_config.rb +2 -2
- data/lib/praxis/trait.rb +1 -1
- data/lib/praxis/types/fuzzy_hash.rb +49 -0
- data/lib/praxis/types/media_type_common.rb +1 -1
- data/lib/praxis/types/multipart.rb +47 -12
- data/lib/praxis/types/multipart_array.rb +320 -0
- data/lib/praxis/types/multipart_array/part_definition.rb +52 -0
- data/lib/praxis/validation_handler.rb +10 -0
- data/lib/praxis/version.rb +2 -2
- data/praxis.gemspec +3 -3
- data/spec/api_browser/directives/type_placeholder_spec.js +134 -0
- data/spec/api_browser/factories/normalize_attributes_spec.js +97 -0
- data/spec/api_browser/factories/template_for_spec.js +67 -0
- data/spec/functional_spec.rb +111 -45
- data/spec/praxis/action_definition_spec.rb +31 -7
- data/spec/praxis/api_definition_spec.rb +2 -2
- data/spec/praxis/api_general_info_spec.rb +25 -0
- data/spec/praxis/application_spec.rb +24 -11
- data/spec/praxis/handlers/xml_spec.rb +55 -33
- data/spec/praxis/links_spec.rb +18 -1
- data/spec/praxis/media_type_collection_spec.rb +1 -1
- data/spec/praxis/media_type_spec.rb +2 -2
- data/spec/praxis/multipart/parser_spec.rb +21 -13
- data/spec/praxis/plugins/praxis_mapper_plugin_spec.rb +1 -1
- data/spec/praxis/request_spec.rb +52 -24
- data/spec/praxis/{request_stages_action_spec.rb → request_stages/action_spec.rb} +1 -1
- data/spec/praxis/{request_stage_spec.rb → request_stages/request_stage_spec.rb} +0 -0
- data/spec/praxis/{request_stages_validate_spec.rb → request_stages/validate_spec.rb} +1 -1
- data/spec/praxis/resource_definition_spec.rb +30 -4
- data/spec/praxis/response_definition_spec.rb +60 -19
- data/spec/praxis/response_spec.rb +2 -2
- data/spec/praxis/responses/validation_error_spec.rb +33 -16
- data/spec/praxis/route_spec.rb +4 -2
- data/spec/praxis/router_spec.rb +28 -12
- data/spec/praxis/routing_config_spec.rb +11 -5
- data/spec/praxis/types/collection_spec.rb +1 -1
- data/spec/praxis/types/fuzzy_hash_spec.rb +20 -0
- data/spec/praxis/types/multipart_array/part_definition_spec.rb +5 -0
- data/spec/praxis/types/multipart_array_spec.rb +334 -0
- data/spec/praxis/types/multipart_spec.rb +14 -5
- data/spec/spec_app/app/controllers/instances.rb +20 -10
- data/spec/spec_app/app/controllers/volumes.rb +8 -4
- data/spec/spec_app/app/responses/bulk_response.rb +0 -6
- data/spec/spec_app/config/environment.rb +13 -0
- data/spec/spec_app/design/api.rb +7 -10
- data/spec/spec_app/design/media_types/instance.rb +3 -1
- data/spec/spec_app/design/resources/instances.rb +50 -41
- data/spec/spec_app/design/resources/volume_snapshots.rb +39 -0
- data/spec/spec_app/design/resources/volumes.rb +11 -6
- data/spec/spec_helper.rb +3 -1
- metadata +125 -218
- data/lib/api_browser/app/bower_components/angular-ui-router/src/compat.js +0 -146
- data/lib/api_browser/app/bower_components/bootstrap-sass/CNAME +0 -1
- data/lib/api_browser/app/bower_components/bootstrap-sass/DOCS-LICENSE +0 -319
- data/lib/api_browser/app/bower_components/bootstrap-sass/Gemfile +0 -5
- data/lib/api_browser/app/bower_components/bootstrap-sass/Gemfile.lock +0 -14
- data/lib/api_browser/app/bower_components/bootstrap-sass/Gruntfile.js +0 -244
- data/lib/api_browser/app/bower_components/bootstrap-sass/LICENSE +0 -176
- data/lib/api_browser/app/bower_components/bootstrap-sass/LICENSE-MIT +0 -21
- data/lib/api_browser/app/bower_components/bootstrap-sass/Rakefile +0 -44
- data/lib/api_browser/app/bower_components/bootstrap-sass/_config.yml +0 -25
- data/lib/api_browser/app/bower_components/bootstrap-sass/_includes/ads.html +0 -1
- data/lib/api_browser/app/bower_components/bootstrap-sass/_includes/footer.html +0 -34
- data/lib/api_browser/app/bower_components/bootstrap-sass/_includes/header.html +0 -42
- data/lib/api_browser/app/bower_components/bootstrap-sass/_includes/nav-about.html +0 -12
- data/lib/api_browser/app/bower_components/bootstrap-sass/_includes/nav-components.html +0 -137
- data/lib/api_browser/app/bower_components/bootstrap-sass/_includes/nav-css.html +0 -99
- data/lib/api_browser/app/bower_components/bootstrap-sass/_includes/nav-customize.html +0 -40
- data/lib/api_browser/app/bower_components/bootstrap-sass/_includes/nav-getting-started.html +0 -44
- data/lib/api_browser/app/bower_components/bootstrap-sass/_includes/nav-javascript.html +0 -88
- data/lib/api_browser/app/bower_components/bootstrap-sass/_includes/nav-main.html +0 -37
- data/lib/api_browser/app/bower_components/bootstrap-sass/_includes/old-bs-docs.html +0 -8
- data/lib/api_browser/app/bower_components/bootstrap-sass/_includes/social-buttons.html +0 -16
- data/lib/api_browser/app/bower_components/bootstrap-sass/_layouts/default.html +0 -79
- data/lib/api_browser/app/bower_components/bootstrap-sass/_layouts/home.html +0 -47
- data/lib/api_browser/app/bower_components/bootstrap-sass/about.html +0 -93
- data/lib/api_browser/app/bower_components/bootstrap-sass/browserstack.json +0 -37
- data/lib/api_browser/app/bower_components/bootstrap-sass/components.html +0 -3689
- data/lib/api_browser/app/bower_components/bootstrap-sass/composer.json +0 -28
- data/lib/api_browser/app/bower_components/bootstrap-sass/css.html +0 -2674
- data/lib/api_browser/app/bower_components/bootstrap-sass/customize.html +0 -1715
- data/lib/api_browser/app/bower_components/bootstrap-sass/dist/css/bootstrap-theme.css +0 -427
- data/lib/api_browser/app/bower_components/bootstrap-sass/dist/css/bootstrap-theme.min.css +0 -1
- data/lib/api_browser/app/bower_components/bootstrap-sass/dist/css/bootstrap.css +0 -6350
- data/lib/api_browser/app/bower_components/bootstrap-sass/dist/css/bootstrap.min.css +0 -1
- data/lib/api_browser/app/bower_components/bootstrap-sass/dist/js/bootstrap.js +0 -2002
- data/lib/api_browser/app/bower_components/bootstrap-sass/dist/js/bootstrap.min.js +0 -9
- data/lib/api_browser/app/bower_components/bootstrap-sass/docs-assets/css/docs.css +0 -1195
- data/lib/api_browser/app/bower_components/bootstrap-sass/docs-assets/css/pygments-manni.css +0 -66
- data/lib/api_browser/app/bower_components/bootstrap-sass/docs-assets/ico/apple-touch-icon-144-precomposed.png +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/docs-assets/ico/favicon.png +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/docs-assets/js/application.js +0 -103
- data/lib/api_browser/app/bower_components/bootstrap-sass/docs-assets/js/customizer.js +0 -332
- data/lib/api_browser/app/bower_components/bootstrap-sass/docs-assets/js/filesaver.js +0 -169
- data/lib/api_browser/app/bower_components/bootstrap-sass/docs-assets/js/holder.js +0 -404
- data/lib/api_browser/app/bower_components/bootstrap-sass/docs-assets/js/ie8-responsive-file-warning.js +0 -12
- data/lib/api_browser/app/bower_components/bootstrap-sass/docs-assets/js/jszip.js +0 -1467
- data/lib/api_browser/app/bower_components/bootstrap-sass/docs-assets/js/less.js +0 -9
- data/lib/api_browser/app/bower_components/bootstrap-sass/docs-assets/js/raw-files.js +0 -3
- data/lib/api_browser/app/bower_components/bootstrap-sass/docs-assets/js/uglify.js +0 -14
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/carousel/carousel.css +0 -148
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/carousel/index.html +0 -206
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/grid/grid.css +0 -28
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/grid/index.html +0 -148
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/jumbotron-narrow/index.html +0 -82
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/jumbotron-narrow/jumbotron-narrow.css +0 -79
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/jumbotron/index.html +0 -99
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/jumbotron/jumbotron.css +0 -5
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/justified-nav/index.html +0 -83
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/justified-nav/justified-nav.css +0 -88
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/navbar-fixed-top/index.html +0 -91
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/navbar-fixed-top/navbar-fixed-top.css +0 -4
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/navbar-static-top/index.html +0 -92
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/navbar-static-top/navbar-static-top.css +0 -7
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/navbar/index.html +0 -88
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/navbar/navbar.css +0 -8
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/non-responsive/index.html +0 -101
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/non-responsive/non-responsive.css +0 -116
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/offcanvas/index.html +0 -130
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/offcanvas/offcanvas.css +0 -50
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/offcanvas/offcanvas.js +0 -5
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/screenshots/carousel.jpg +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/screenshots/grid.jpg +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/screenshots/jumbotron-narrow.jpg +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/screenshots/jumbotron.jpg +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/screenshots/justified-nav.jpg +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/screenshots/navbar-fixed.jpg +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/screenshots/navbar-static.jpg +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/screenshots/navbar.jpg +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/screenshots/non-responsive.jpg +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/screenshots/offcanvas.jpg +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/screenshots/sign-in.jpg +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/screenshots/starter-template.jpg +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/screenshots/sticky-footer-navbar.jpg +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/screenshots/sticky-footer.jpg +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/screenshots/theme.jpg +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/signin/index.html +0 -50
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/signin/signin.css +0 -40
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/starter-template/index.html +0 -68
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/starter-template/starter-template.css +0 -7
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/sticky-footer-navbar/index.html +0 -91
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/sticky-footer-navbar/sticky-footer-navbar.css +0 -45
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/sticky-footer/index.html +0 -55
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/sticky-footer/sticky-footer.css +0 -38
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/theme/index.html +0 -384
- data/lib/api_browser/app/bower_components/bootstrap-sass/examples/theme/theme.css +0 -14
- data/lib/api_browser/app/bower_components/bootstrap-sass/fonts/glyphicons-halflings-regular.eot +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/fonts/glyphicons-halflings-regular.svg +0 -229
- data/lib/api_browser/app/bower_components/bootstrap-sass/fonts/glyphicons-halflings-regular.ttf +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/fonts/glyphicons-halflings-regular.woff +0 -0
- data/lib/api_browser/app/bower_components/bootstrap-sass/getting-started.html +0 -1021
- data/lib/api_browser/app/bower_components/bootstrap-sass/index.html +0 -16
- data/lib/api_browser/app/bower_components/bootstrap-sass/javascript.html +0 -1983
- data/lib/api_browser/app/bower_components/bootstrap-sass/js/tests/index.html +0 -52
- data/lib/api_browser/app/bower_components/bootstrap-sass/js/tests/unit/affix.js +0 -25
- data/lib/api_browser/app/bower_components/bootstrap-sass/js/tests/unit/alert.js +0 -62
- data/lib/api_browser/app/bower_components/bootstrap-sass/js/tests/unit/button.js +0 -116
- data/lib/api_browser/app/bower_components/bootstrap-sass/js/tests/unit/carousel.js +0 -87
- data/lib/api_browser/app/bower_components/bootstrap-sass/js/tests/unit/collapse.js +0 -164
- data/lib/api_browser/app/bower_components/bootstrap-sass/js/tests/unit/dropdown.js +0 -219
- data/lib/api_browser/app/bower_components/bootstrap-sass/js/tests/unit/modal.js +0 -196
- data/lib/api_browser/app/bower_components/bootstrap-sass/js/tests/unit/phantom.js +0 -69
- data/lib/api_browser/app/bower_components/bootstrap-sass/js/tests/unit/popover.js +0 -133
- data/lib/api_browser/app/bower_components/bootstrap-sass/js/tests/unit/scrollspy.js +0 -37
- data/lib/api_browser/app/bower_components/bootstrap-sass/js/tests/unit/tab.js +0 -86
- data/lib/api_browser/app/bower_components/bootstrap-sass/js/tests/unit/tooltip.js +0 -437
- data/lib/api_browser/app/bower_components/bootstrap-sass/js/tests/unit/transition.js +0 -13
- data/lib/api_browser/app/bower_components/bootstrap-sass/js/tests/vendor/jquery.js +0 -6
- data/lib/api_browser/app/bower_components/bootstrap-sass/js/tests/vendor/qunit.css +0 -232
- data/lib/api_browser/app/bower_components/bootstrap-sass/js/tests/vendor/qunit.js +0 -1510
- data/lib/api_browser/app/bower_components/bootstrap-sass/package.json +0 -40
- data/lib/api_browser/app/bower_components/lodash/dist/lodash.compat.js +0 -7157
- data/lib/api_browser/app/bower_components/lodash/dist/lodash.compat.min.js +0 -61
- data/lib/api_browser/app/bower_components/lodash/dist/lodash.js +0 -6785
- data/lib/api_browser/app/bower_components/lodash/dist/lodash.min.js +0 -56
- data/lib/api_browser/app/bower_components/lodash/dist/lodash.underscore.js +0 -4979
- data/lib/api_browser/app/bower_components/lodash/dist/lodash.underscore.min.js +0 -39
- data/lib/api_browser/app/js/directives/attribute_table_row.js +0 -17
- data/lib/api_browser/app/js/directives/request_body.js +0 -25
- data/lib/api_browser/app/js/directives/request_headers.js +0 -17
- data/lib/api_browser/app/js/directives/request_parameters.js +0 -17
- data/lib/api_browser/app/js/directives/type_label.js +0 -52
- data/lib/api_browser/app/js/factories/PayloadTemplates.js +0 -10
- data/lib/api_browser/app/js/factories/TemplateProvider.js +0 -45
- data/lib/api_browser/app/js/factories/TypeTemplates.js +0 -11
- data/lib/api_browser/app/views/directives/attribute_description/_example.html +0 -13
- data/lib/api_browser/app/views/directives/attribute_description/_headers.html +0 -8
- data/lib/api_browser/app/views/directives/attribute_table_row/_default.html +0 -10
- data/lib/api_browser/app/views/directives/attribute_table_row/_links.html +0 -11
- data/lib/api_browser/app/views/directives/attribute_table_row/_struct.html +0 -2
- data/lib/api_browser/app/views/directives/request_body/_struct.html +0 -1
- data/lib/api_browser/app/views/resource/_actions.html +0 -27
@@ -1,5 +1,7 @@
|
|
1
|
-
function parseStateRef(ref) {
|
2
|
-
var
|
1
|
+
function parseStateRef(ref, current) {
|
2
|
+
var preparsed = ref.match(/^\s*({[^}]*})\s*$/), parsed;
|
3
|
+
if (preparsed) ref = current + '(' + preparsed[1] + ')';
|
4
|
+
parsed = ref.replace(/\n/g, " ").match(/^([^(]+?)\s*(\((.*)\))?$/);
|
3
5
|
if (!parsed || parsed.length !== 4) throw new Error("Invalid state ref '" + ref + "'");
|
4
6
|
return { state: parsed[1], paramExpr: parsed[3] || null };
|
5
7
|
}
|
@@ -43,7 +45,7 @@ function stateContext(el) {
|
|
43
45
|
* Here's an example of how you'd use ui-sref and how it would compile. If you have the
|
44
46
|
* following template:
|
45
47
|
* <pre>
|
46
|
-
* <a ui-sref="home">Home</a> | <a ui-sref="about">About</a>
|
48
|
+
* <a ui-sref="home">Home</a> | <a ui-sref="about">About</a> | <a ui-sref="{page: 2}">Next page</a>
|
47
49
|
*
|
48
50
|
* <ul>
|
49
51
|
* <li ng-repeat="contact in contacts">
|
@@ -52,9 +54,9 @@ function stateContext(el) {
|
|
52
54
|
* </ul>
|
53
55
|
* </pre>
|
54
56
|
*
|
55
|
-
* Then the compiled html would be (assuming Html5Mode is off):
|
57
|
+
* Then the compiled html would be (assuming Html5Mode is off and current state is contacts):
|
56
58
|
* <pre>
|
57
|
-
* <a href="#/home" ui-sref="home">Home</a> | <a href="#/about" ui-sref="about">About</a>
|
59
|
+
* <a href="#/home" ui-sref="home">Home</a> | <a href="#/about" ui-sref="about">About</a> | <a href="#/contacts?page=2" ui-sref="{page: 2}">Next page</a>
|
58
60
|
*
|
59
61
|
* <ul>
|
60
62
|
* <li ng-repeat="contact in contacts">
|
@@ -76,21 +78,24 @@ function stateContext(el) {
|
|
76
78
|
*/
|
77
79
|
$StateRefDirective.$inject = ['$state', '$timeout'];
|
78
80
|
function $StateRefDirective($state, $timeout) {
|
79
|
-
var allowedOptions = ['location', 'inherit', 'reload'];
|
81
|
+
var allowedOptions = ['location', 'inherit', 'reload', 'absolute'];
|
80
82
|
|
81
83
|
return {
|
82
84
|
restrict: 'A',
|
83
|
-
require: '?^uiSrefActive',
|
85
|
+
require: ['?^uiSrefActive', '?^uiSrefActiveEq'],
|
84
86
|
link: function(scope, element, attrs, uiSrefActive) {
|
85
|
-
var ref = parseStateRef(attrs.uiSref);
|
87
|
+
var ref = parseStateRef(attrs.uiSref, $state.current.name);
|
86
88
|
var params = null, url = null, base = stateContext(element) || $state.$current;
|
89
|
+
// SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
|
90
|
+
var hrefKind = Object.prototype.toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
|
91
|
+
'xlink:href' : 'href';
|
92
|
+
var newHref = null, isAnchor = element.prop("tagName").toUpperCase() === "A";
|
87
93
|
var isForm = element[0].nodeName === "FORM";
|
88
|
-
var attr = isForm ? "action" :
|
94
|
+
var attr = isForm ? "action" : hrefKind, nav = true;
|
89
95
|
|
90
|
-
var options = {
|
91
|
-
relative: base
|
92
|
-
};
|
96
|
+
var options = { relative: base, inherit: true };
|
93
97
|
var optionsOverride = scope.$eval(attrs.uiSrefOpts) || {};
|
98
|
+
|
94
99
|
angular.forEach(allowedOptions, function(option) {
|
95
100
|
if (option in optionsOverride) {
|
96
101
|
options[option] = optionsOverride[option];
|
@@ -98,26 +103,27 @@ function $StateRefDirective($state, $timeout) {
|
|
98
103
|
});
|
99
104
|
|
100
105
|
var update = function(newVal) {
|
101
|
-
if (newVal) params = newVal;
|
106
|
+
if (newVal) params = angular.copy(newVal);
|
102
107
|
if (!nav) return;
|
103
108
|
|
104
|
-
|
109
|
+
newHref = $state.href(ref.state, params, options);
|
105
110
|
|
106
|
-
|
107
|
-
|
111
|
+
var activeDirective = uiSrefActive[1] || uiSrefActive[0];
|
112
|
+
if (activeDirective) {
|
113
|
+
activeDirective.$$addStateInfo(ref.state, params);
|
108
114
|
}
|
109
|
-
if (
|
115
|
+
if (newHref === null) {
|
110
116
|
nav = false;
|
111
117
|
return false;
|
112
118
|
}
|
113
|
-
|
119
|
+
attrs.$set(attr, newHref);
|
114
120
|
};
|
115
121
|
|
116
122
|
if (ref.paramExpr) {
|
117
123
|
scope.$watch(ref.paramExpr, function(newVal, oldVal) {
|
118
124
|
if (newVal !== params) update(newVal);
|
119
125
|
}, true);
|
120
|
-
params = scope.$eval(ref.paramExpr);
|
126
|
+
params = angular.copy(scope.$eval(ref.paramExpr));
|
121
127
|
}
|
122
128
|
update();
|
123
129
|
|
@@ -127,10 +133,17 @@ function $StateRefDirective($state, $timeout) {
|
|
127
133
|
var button = e.which || e.button;
|
128
134
|
if ( !(button > 1 || e.ctrlKey || e.metaKey || e.shiftKey || element.attr('target')) ) {
|
129
135
|
// HACK: This is to allow ng-clicks to be processed before the transition is initiated:
|
130
|
-
$timeout(function() {
|
136
|
+
var transition = $timeout(function() {
|
131
137
|
$state.go(ref.state, params, options);
|
132
138
|
});
|
133
139
|
e.preventDefault();
|
140
|
+
|
141
|
+
// if the state has no URL, ignore one preventDefault from the <a> directive.
|
142
|
+
var ignorePreventDefaultCount = isAnchor && !newHref ? 1: 0;
|
143
|
+
e.preventDefault = function() {
|
144
|
+
if (ignorePreventDefaultCount-- <= 0)
|
145
|
+
$timeout.cancel(transition);
|
146
|
+
};
|
134
147
|
}
|
135
148
|
});
|
136
149
|
}
|
@@ -148,12 +161,20 @@ function $StateRefDirective($state, $timeout) {
|
|
148
161
|
* @restrict A
|
149
162
|
*
|
150
163
|
* @description
|
151
|
-
* A directive working alongside ui-sref to add classes to an element when the
|
164
|
+
* A directive working alongside ui-sref to add classes to an element when the
|
152
165
|
* related ui-sref directive's state is active, and removing them when it is inactive.
|
153
|
-
* The primary use-case is to simplify the special appearance of navigation menus
|
166
|
+
* The primary use-case is to simplify the special appearance of navigation menus
|
154
167
|
* relying on `ui-sref`, by having the "active" state's menu button appear different,
|
155
168
|
* distinguishing it from the inactive menu items.
|
156
169
|
*
|
170
|
+
* ui-sref-active can live on the same element as ui-sref or on a parent element. The first
|
171
|
+
* ui-sref-active found at the same level or above the ui-sref will be used.
|
172
|
+
*
|
173
|
+
* Will activate when the ui-sref's target state or any child state is active. If you
|
174
|
+
* need to activate only when the ui-sref target state is active and *not* any of
|
175
|
+
* it's children, then you will use
|
176
|
+
* {@link ui.router.state.directive:ui-sref-active-eq ui-sref-active-eq}
|
177
|
+
*
|
157
178
|
* @example
|
158
179
|
* Given the following template:
|
159
180
|
* <pre>
|
@@ -163,8 +184,9 @@ function $StateRefDirective($state, $timeout) {
|
|
163
184
|
* </li>
|
164
185
|
* </ul>
|
165
186
|
* </pre>
|
166
|
-
*
|
167
|
-
*
|
187
|
+
*
|
188
|
+
*
|
189
|
+
* When the app state is "app.user" (or any children states), and contains the state parameter "user" with value "bilbobaggins",
|
168
190
|
* the resulting HTML will appear as (note the 'active' class):
|
169
191
|
* <pre>
|
170
192
|
* <ul>
|
@@ -173,10 +195,10 @@ function $StateRefDirective($state, $timeout) {
|
|
173
195
|
* </li>
|
174
196
|
* </ul>
|
175
197
|
* </pre>
|
176
|
-
*
|
177
|
-
* The class name is interpolated **once** during the directives link time (any further changes to the
|
178
|
-
* interpolated value are ignored).
|
179
|
-
*
|
198
|
+
*
|
199
|
+
* The class name is interpolated **once** during the directives link time (any further changes to the
|
200
|
+
* interpolated value are ignored).
|
201
|
+
*
|
180
202
|
* Multiple classes may be specified in a space-separated format:
|
181
203
|
* <pre>
|
182
204
|
* <ul>
|
@@ -186,20 +208,43 @@ function $StateRefDirective($state, $timeout) {
|
|
186
208
|
* </ul>
|
187
209
|
* </pre>
|
188
210
|
*/
|
189
|
-
|
190
|
-
|
191
|
-
|
211
|
+
|
212
|
+
/**
|
213
|
+
* @ngdoc directive
|
214
|
+
* @name ui.router.state.directive:ui-sref-active-eq
|
215
|
+
*
|
216
|
+
* @requires ui.router.state.$state
|
217
|
+
* @requires ui.router.state.$stateParams
|
218
|
+
* @requires $interpolate
|
219
|
+
*
|
220
|
+
* @restrict A
|
221
|
+
*
|
222
|
+
* @description
|
223
|
+
* The same as {@link ui.router.state.directive:ui-sref-active ui-sref-active} but will only activate
|
224
|
+
* when the exact target state used in the `ui-sref` is active; no child states.
|
225
|
+
*
|
226
|
+
*/
|
227
|
+
$StateRefActiveDirective.$inject = ['$state', '$stateParams', '$interpolate'];
|
228
|
+
function $StateRefActiveDirective($state, $stateParams, $interpolate) {
|
229
|
+
return {
|
192
230
|
restrict: "A",
|
193
|
-
controller: ['$scope', '$element', '$attrs', function($scope, $element, $attrs) {
|
194
|
-
var
|
231
|
+
controller: ['$scope', '$element', '$attrs', function ($scope, $element, $attrs) {
|
232
|
+
var states = [], activeClass;
|
195
233
|
|
196
234
|
// There probably isn't much point in $observing this
|
197
|
-
|
235
|
+
// uiSrefActive and uiSrefActiveEq share the same directive object with some
|
236
|
+
// slight difference in logic routing
|
237
|
+
activeClass = $interpolate($attrs.uiSrefActiveEq || $attrs.uiSrefActive || '', false)($scope);
|
238
|
+
|
239
|
+
// Allow uiSref to communicate with uiSrefActive[Equals]
|
240
|
+
this.$$addStateInfo = function (newState, newParams) {
|
241
|
+
var state = $state.get(newState, stateContext($element));
|
242
|
+
|
243
|
+
states.push({
|
244
|
+
state: state || { name: newState },
|
245
|
+
params: newParams
|
246
|
+
});
|
198
247
|
|
199
|
-
// Allow uiSref to communicate with uiSrefActive
|
200
|
-
this.$$setStateInfo = function(newState, newParams) {
|
201
|
-
state = $state.get(newState, stateContext($element));
|
202
|
-
params = newParams;
|
203
248
|
update();
|
204
249
|
};
|
205
250
|
|
@@ -207,15 +252,28 @@ function $StateActiveDirective($state, $stateParams, $interpolate) {
|
|
207
252
|
|
208
253
|
// Update route state
|
209
254
|
function update() {
|
210
|
-
if (
|
255
|
+
if (anyMatch()) {
|
211
256
|
$element.addClass(activeClass);
|
212
257
|
} else {
|
213
258
|
$element.removeClass(activeClass);
|
214
259
|
}
|
215
260
|
}
|
216
261
|
|
217
|
-
function
|
218
|
-
|
262
|
+
function anyMatch() {
|
263
|
+
for (var i = 0; i < states.length; i++) {
|
264
|
+
if (isMatch(states[i].state, states[i].params)) {
|
265
|
+
return true;
|
266
|
+
}
|
267
|
+
}
|
268
|
+
return false;
|
269
|
+
}
|
270
|
+
|
271
|
+
function isMatch(state, params) {
|
272
|
+
if (typeof $attrs.uiSrefActiveEq !== 'undefined') {
|
273
|
+
return $state.is(state.name, params);
|
274
|
+
} else {
|
275
|
+
return $state.includes(state.name, params);
|
276
|
+
}
|
219
277
|
}
|
220
278
|
}]
|
221
279
|
};
|
@@ -223,4 +281,5 @@ function $StateActiveDirective($state, $stateParams, $interpolate) {
|
|
223
281
|
|
224
282
|
angular.module('ui.router.state')
|
225
283
|
.directive('uiSref', $StateRefDirective)
|
226
|
-
.directive('uiSrefActive', $
|
284
|
+
.directive('uiSrefActive', $StateRefActiveDirective)
|
285
|
+
.directive('uiSrefActiveEq', $StateRefActiveDirective);
|
@@ -9,9 +9,11 @@
|
|
9
9
|
*/
|
10
10
|
$IsStateFilter.$inject = ['$state'];
|
11
11
|
function $IsStateFilter($state) {
|
12
|
-
|
12
|
+
var isFilter = function (state) {
|
13
13
|
return $state.is(state);
|
14
14
|
};
|
15
|
+
isFilter.$stateful = true;
|
16
|
+
return isFilter;
|
15
17
|
}
|
16
18
|
|
17
19
|
/**
|
@@ -25,9 +27,11 @@ function $IsStateFilter($state) {
|
|
25
27
|
*/
|
26
28
|
$IncludedByStateFilter.$inject = ['$state'];
|
27
29
|
function $IncludedByStateFilter($state) {
|
28
|
-
|
30
|
+
var includesFilter = function (state) {
|
29
31
|
return $state.includes(state);
|
30
32
|
};
|
33
|
+
includesFilter.$stateful = true;
|
34
|
+
return includesFilter;
|
31
35
|
}
|
32
36
|
|
33
37
|
angular.module('ui.router.state')
|
@@ -83,13 +83,13 @@ function $TemplateFactory( $http, $templateCache, $injector) {
|
|
83
83
|
if (isFunction(url)) url = url(params);
|
84
84
|
if (url == null) return null;
|
85
85
|
else return $http
|
86
|
-
.get(url, { cache: $templateCache })
|
86
|
+
.get(url, { cache: $templateCache, headers: { Accept: 'text/html' }})
|
87
87
|
.then(function(response) { return response.data; });
|
88
88
|
};
|
89
89
|
|
90
90
|
/**
|
91
91
|
* @ngdoc function
|
92
|
-
* @name ui.router.util.$templateFactory#
|
92
|
+
* @name ui.router.util.$templateFactory#fromProvider
|
93
93
|
* @methodOf ui.router.util.$templateFactory
|
94
94
|
*
|
95
95
|
* @description
|
@@ -1,3 +1,5 @@
|
|
1
|
+
var $$UMFP; // reference to $UrlMatcherFactoryProvider
|
2
|
+
|
1
3
|
/**
|
2
4
|
* @ngdoc object
|
3
5
|
* @name ui.router.util.type:UrlMatcher
|
@@ -8,24 +10,24 @@
|
|
8
10
|
* of search parameters. Multiple search parameter names are separated by '&'. Search parameters
|
9
11
|
* do not influence whether or not a URL is matched, but their values are passed through into
|
10
12
|
* the matched parameters returned by {@link ui.router.util.type:UrlMatcher#methods_exec exec}.
|
11
|
-
*
|
13
|
+
*
|
12
14
|
* Path parameter placeholders can be specified using simple colon/catch-all syntax or curly brace
|
13
15
|
* syntax, which optionally allows a regular expression for the parameter to be specified:
|
14
16
|
*
|
15
17
|
* * `':'` name - colon placeholder
|
16
18
|
* * `'*'` name - catch-all placeholder
|
17
19
|
* * `'{' name '}'` - curly placeholder
|
18
|
-
* * `'{' name ':' regexp '}'` - curly placeholder with regexp. Should the
|
19
|
-
* curly braces, they must be in matched pairs or escaped with a backslash.
|
20
|
+
* * `'{' name ':' regexp|type '}'` - curly placeholder with regexp or type name. Should the
|
21
|
+
* regexp itself contain curly braces, they must be in matched pairs or escaped with a backslash.
|
20
22
|
*
|
21
23
|
* Parameter names may contain only word characters (latin letters, digits, and underscore) and
|
22
|
-
* must be unique within the pattern (across both path and search parameters). For colon
|
24
|
+
* must be unique within the pattern (across both path and search parameters). For colon
|
23
25
|
* placeholders or curly placeholders without an explicit regexp, a path parameter matches any
|
24
26
|
* number of characters other than '/'. For catch-all placeholders the path parameter matches
|
25
27
|
* any number of characters.
|
26
|
-
*
|
28
|
+
*
|
27
29
|
* Examples:
|
28
|
-
*
|
30
|
+
*
|
29
31
|
* * `'/hello/'` - Matches only if the path is exactly '/hello/'. There is no special treatment for
|
30
32
|
* trailing slashes, and patterns have to match the entire path, not just a prefix.
|
31
33
|
* * `'/user/:id'` - Matches '/user/bob' or '/user/1234!!!' or even '/user/' but not '/user' or
|
@@ -37,26 +39,34 @@
|
|
37
39
|
* * `'/files/{path:.*}'` - Matches any URL starting with '/files/' and captures the rest of the
|
38
40
|
* path into the parameter 'path'.
|
39
41
|
* * `'/files/*path'` - ditto.
|
42
|
+
* * `'/calendar/{start:date}'` - Matches "/calendar/2014-11-12" (because the pattern defined
|
43
|
+
* in the built-in `date` Type matches `2014-11-12`) and provides a Date object in $stateParams.start
|
44
|
+
*
|
45
|
+
* @param {string} pattern The pattern to compile into a matcher.
|
46
|
+
* @param {Object} config A configuration object hash:
|
47
|
+
* @param {Object=} parentMatcher Used to concatenate the pattern/config onto
|
48
|
+
* an existing UrlMatcher
|
40
49
|
*
|
41
|
-
*
|
42
|
-
*
|
50
|
+
* * `caseInsensitive` - `true` if URL matching should be case insensitive, otherwise `false`, the default value (for backward compatibility) is `false`.
|
51
|
+
* * `strict` - `false` if matching against a URL with a trailing slash should be treated as equivalent to a URL without a trailing slash, the default value is `true`.
|
43
52
|
*
|
44
53
|
* @property {string} prefix A static prefix of this pattern. The matcher guarantees that any
|
45
54
|
* URL matching this matcher (i.e. any string for which {@link ui.router.util.type:UrlMatcher#methods_exec exec()} returns
|
46
55
|
* non-null) will start with this prefix.
|
47
56
|
*
|
48
|
-
* @property {string} source The pattern that was passed into the
|
57
|
+
* @property {string} source The pattern that was passed into the constructor
|
49
58
|
*
|
50
59
|
* @property {string} sourcePath The path portion of the source property
|
51
60
|
*
|
52
61
|
* @property {string} sourceSearch The search portion of the source property
|
53
62
|
*
|
54
|
-
* @property {string} regex The constructed regex that will be used to match against the url when
|
63
|
+
* @property {string} regex The constructed regex that will be used to match against the url when
|
55
64
|
* it is time to determine which url will match.
|
56
65
|
*
|
57
|
-
* @returns {Object} New UrlMatcher object
|
66
|
+
* @returns {Object} New `UrlMatcher` object
|
58
67
|
*/
|
59
|
-
function UrlMatcher(pattern,
|
68
|
+
function UrlMatcher(pattern, config, parentMatcher) {
|
69
|
+
config = extend({ params: {} }, isObject(config) ? config : {});
|
60
70
|
|
61
71
|
// Find all placeholders and create a compiled pattern, using either classic or curly syntax:
|
62
72
|
// '*' name
|
@@ -65,68 +75,96 @@ function UrlMatcher(pattern, caseInsensitiveMatch) {
|
|
65
75
|
// '{' name ':' regexp '}'
|
66
76
|
// The regular expression is somewhat complicated due to the need to allow curly braces
|
67
77
|
// inside the regular expression. The placeholder regexp breaks down as follows:
|
68
|
-
// ([:*])(\w+)
|
69
|
-
// \{(\w+)(?:\:( ... ))?\}
|
70
|
-
// (?: ... | ... | ... )+
|
71
|
-
// [^{}\\]+
|
72
|
-
// \\.
|
73
|
-
// \{(?:[^{}\\]+|\\.)*\}
|
74
|
-
var placeholder
|
75
|
-
|
78
|
+
// ([:*])([\w\[\]]+) - classic placeholder ($1 / $2) (search version has - for snake-case)
|
79
|
+
// \{([\w\[\]]+)(?:\:( ... ))?\} - curly brace placeholder ($3) with optional regexp/type ... ($4) (search version has - for snake-case
|
80
|
+
// (?: ... | ... | ... )+ - the regexp consists of any number of atoms, an atom being either
|
81
|
+
// [^{}\\]+ - anything other than curly braces or backslash
|
82
|
+
// \\. - a backslash escape
|
83
|
+
// \{(?:[^{}\\]+|\\.)*\} - a matched set of curly braces containing other atoms
|
84
|
+
var placeholder = /([:*])([\w\[\]]+)|\{([\w\[\]]+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,
|
85
|
+
searchPlaceholder = /([:]?)([\w\[\]-]+)|\{([\w\[\]-]+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,
|
86
|
+
compiled = '^', last = 0, m,
|
76
87
|
segments = this.segments = [],
|
77
|
-
|
88
|
+
parentParams = parentMatcher ? parentMatcher.params : {},
|
89
|
+
params = this.params = parentMatcher ? parentMatcher.params.$$new() : new $$UMFP.ParamSet(),
|
90
|
+
paramNames = [];
|
78
91
|
|
79
|
-
function addParameter(id) {
|
80
|
-
|
81
|
-
if (
|
82
|
-
|
83
|
-
params
|
92
|
+
function addParameter(id, type, config, location) {
|
93
|
+
paramNames.push(id);
|
94
|
+
if (parentParams[id]) return parentParams[id];
|
95
|
+
if (!/^\w+(-+\w+)*(?:\[\])?$/.test(id)) throw new Error("Invalid parameter name '" + id + "' in pattern '" + pattern + "'");
|
96
|
+
if (params[id]) throw new Error("Duplicate parameter name '" + id + "' in pattern '" + pattern + "'");
|
97
|
+
params[id] = new $$UMFP.Param(id, type, config, location);
|
98
|
+
return params[id];
|
84
99
|
}
|
85
100
|
|
86
|
-
function quoteRegExp(string) {
|
87
|
-
|
101
|
+
function quoteRegExp(string, pattern, squash, optional) {
|
102
|
+
var surroundPattern = ['',''], result = string.replace(/[\\\[\]\^$*+?.()|{}]/g, "\\$&");
|
103
|
+
if (!pattern) return result;
|
104
|
+
switch(squash) {
|
105
|
+
case false: surroundPattern = ['(', ')' + (optional ? "?" : "")]; break;
|
106
|
+
case true: surroundPattern = ['?(', ')?']; break;
|
107
|
+
default: surroundPattern = ['(' + squash + "|", ')?']; break;
|
108
|
+
}
|
109
|
+
return result + surroundPattern[0] + pattern + surroundPattern[1];
|
88
110
|
}
|
89
111
|
|
90
112
|
this.source = pattern;
|
91
113
|
|
92
114
|
// Split into static segments separated by path parameter placeholders.
|
93
115
|
// The number of segments is always 1 more than the number of parameters.
|
94
|
-
|
116
|
+
function matchDetails(m, isSearch) {
|
117
|
+
var id, regexp, segment, type, cfg, arrayMode;
|
118
|
+
id = m[2] || m[3]; // IE[78] returns '' for unmatched groups instead of null
|
119
|
+
cfg = config.params[id];
|
120
|
+
segment = pattern.substring(last, m.index);
|
121
|
+
regexp = isSearch ? m[4] : m[4] || (m[1] == '*' ? '.*' : null);
|
122
|
+
type = $$UMFP.type(regexp || "string") || inherit($$UMFP.type("string"), { pattern: new RegExp(regexp, config.caseInsensitive ? 'i' : undefined) });
|
123
|
+
return {
|
124
|
+
id: id, regexp: regexp, segment: segment, type: type, cfg: cfg
|
125
|
+
};
|
126
|
+
}
|
127
|
+
|
128
|
+
var p, param, segment;
|
95
129
|
while ((m = placeholder.exec(pattern))) {
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
compiled += quoteRegExp(segment
|
101
|
-
|
102
|
-
segments.push(segment);
|
130
|
+
p = matchDetails(m, false);
|
131
|
+
if (p.segment.indexOf('?') >= 0) break; // we're into the search part
|
132
|
+
|
133
|
+
param = addParameter(p.id, p.type, p.cfg, "path");
|
134
|
+
compiled += quoteRegExp(p.segment, param.type.pattern.source, param.squash, param.isOptional);
|
135
|
+
segments.push(p.segment);
|
103
136
|
last = placeholder.lastIndex;
|
104
137
|
}
|
105
138
|
segment = pattern.substring(last);
|
106
139
|
|
107
140
|
// Find any search parameter names and remove them from the last segment
|
108
141
|
var i = segment.indexOf('?');
|
142
|
+
|
109
143
|
if (i >= 0) {
|
110
144
|
var search = this.sourceSearch = segment.substring(i);
|
111
145
|
segment = segment.substring(0, i);
|
112
|
-
this.sourcePath = pattern.substring(0, last+i);
|
146
|
+
this.sourcePath = pattern.substring(0, last + i);
|
113
147
|
|
114
|
-
|
115
|
-
|
148
|
+
if (search.length > 0) {
|
149
|
+
last = 0;
|
150
|
+
while ((m = searchPlaceholder.exec(search))) {
|
151
|
+
p = matchDetails(m, true);
|
152
|
+
param = addParameter(p.id, p.type, p.cfg, "search");
|
153
|
+
last = placeholder.lastIndex;
|
154
|
+
// check if ?&
|
155
|
+
}
|
156
|
+
}
|
116
157
|
} else {
|
117
158
|
this.sourcePath = pattern;
|
118
159
|
this.sourceSearch = '';
|
119
160
|
}
|
120
161
|
|
121
|
-
compiled += quoteRegExp(segment) + '$';
|
162
|
+
compiled += quoteRegExp(segment) + (config.strict === false ? '\/?' : '') + '$';
|
122
163
|
segments.push(segment);
|
123
|
-
|
124
|
-
|
125
|
-
}else{
|
126
|
-
this.regexp = new RegExp(compiled);
|
127
|
-
}
|
128
|
-
|
164
|
+
|
165
|
+
this.regexp = new RegExp(compiled, config.caseInsensitive ? 'i' : undefined);
|
129
166
|
this.prefix = segments[0];
|
167
|
+
this.$$paramNames = paramNames;
|
130
168
|
}
|
131
169
|
|
132
170
|
/**
|
@@ -142,19 +180,25 @@ function UrlMatcher(pattern, caseInsensitiveMatch) {
|
|
142
180
|
*
|
143
181
|
* @example
|
144
182
|
* The following two matchers are equivalent:
|
145
|
-
*
|
183
|
+
* <pre>
|
146
184
|
* new UrlMatcher('/user/{id}?q').concat('/details?date');
|
147
185
|
* new UrlMatcher('/user/{id}/details?q&date');
|
148
|
-
*
|
186
|
+
* </pre>
|
149
187
|
*
|
150
188
|
* @param {string} pattern The pattern to append.
|
151
|
-
* @
|
189
|
+
* @param {Object} config An object hash of the configuration for the matcher.
|
190
|
+
* @returns {UrlMatcher} A matcher for the concatenated pattern.
|
152
191
|
*/
|
153
|
-
UrlMatcher.prototype.concat = function (pattern) {
|
192
|
+
UrlMatcher.prototype.concat = function (pattern, config) {
|
154
193
|
// Because order of search parameters is irrelevant, we can add our own search
|
155
194
|
// parameters to the end of the new pattern. Parse the new pattern by itself
|
156
195
|
// and then join the bits together, but it's much easier to do this on a string level.
|
157
|
-
|
196
|
+
var defaultConfig = {
|
197
|
+
caseInsensitive: $$UMFP.caseInsensitive(),
|
198
|
+
strict: $$UMFP.strictMode(),
|
199
|
+
squash: $$UMFP.defaultSquashPolicy()
|
200
|
+
};
|
201
|
+
return new UrlMatcher(this.sourcePath + pattern + this.sourceSearch, extend(defaultConfig, config), this);
|
158
202
|
};
|
159
203
|
|
160
204
|
UrlMatcher.prototype.toString = function () {
|
@@ -174,10 +218,12 @@ UrlMatcher.prototype.toString = function () {
|
|
174
218
|
* as optional.
|
175
219
|
*
|
176
220
|
* @example
|
177
|
-
*
|
178
|
-
* new UrlMatcher('/user/{id}?q&r').exec('/user/bob', {
|
179
|
-
*
|
180
|
-
*
|
221
|
+
* <pre>
|
222
|
+
* new UrlMatcher('/user/{id}?q&r').exec('/user/bob', {
|
223
|
+
* x: '1', q: 'hello'
|
224
|
+
* });
|
225
|
+
* // returns { id: 'bob', q: 'hello', r: null }
|
226
|
+
* </pre>
|
181
227
|
*
|
182
228
|
* @param {string} path The URL path to match, e.g. `$location.path()`.
|
183
229
|
* @param {Object} searchParams URL search parameters, e.g. `$location.search()`.
|
@@ -186,15 +232,38 @@ UrlMatcher.prototype.toString = function () {
|
|
186
232
|
UrlMatcher.prototype.exec = function (path, searchParams) {
|
187
233
|
var m = this.regexp.exec(path);
|
188
234
|
if (!m) return null;
|
235
|
+
searchParams = searchParams || {};
|
189
236
|
|
190
|
-
var
|
191
|
-
nPath = this.segments.length-1,
|
192
|
-
values = {}, i;
|
237
|
+
var paramNames = this.parameters(), nTotal = paramNames.length,
|
238
|
+
nPath = this.segments.length - 1,
|
239
|
+
values = {}, i, j, cfg, paramName;
|
193
240
|
|
194
241
|
if (nPath !== m.length - 1) throw new Error("Unbalanced capture group in route '" + this.source + "'");
|
195
242
|
|
196
|
-
|
197
|
-
|
243
|
+
function decodePathArray(string) {
|
244
|
+
function reverseString(str) { return str.split("").reverse().join(""); }
|
245
|
+
function unquoteDashes(str) { return str.replace(/\\-/g, "-"); }
|
246
|
+
|
247
|
+
var split = reverseString(string).split(/-(?!\\)/);
|
248
|
+
var allReversed = map(split, reverseString);
|
249
|
+
return map(allReversed, unquoteDashes).reverse();
|
250
|
+
}
|
251
|
+
|
252
|
+
for (i = 0; i < nPath; i++) {
|
253
|
+
paramName = paramNames[i];
|
254
|
+
var param = this.params[paramName];
|
255
|
+
var paramVal = m[i+1];
|
256
|
+
// if the param value matches a pre-replace pair, replace the value before decoding.
|
257
|
+
for (j = 0; j < param.replace; j++) {
|
258
|
+
if (param.replace[j].from === paramVal) paramVal = param.replace[j].to;
|
259
|
+
}
|
260
|
+
if (paramVal && param.array === true) paramVal = decodePathArray(paramVal);
|
261
|
+
values[paramName] = param.value(paramVal);
|
262
|
+
}
|
263
|
+
for (/**/; i < nTotal; i++) {
|
264
|
+
paramName = paramNames[i];
|
265
|
+
values[paramName] = this.params[paramName].value(searchParams[paramName]);
|
266
|
+
}
|
198
267
|
|
199
268
|
return values;
|
200
269
|
};
|
@@ -206,12 +275,29 @@ UrlMatcher.prototype.exec = function (path, searchParams) {
|
|
206
275
|
*
|
207
276
|
* @description
|
208
277
|
* Returns the names of all path and search parameters of this pattern in an unspecified order.
|
209
|
-
*
|
278
|
+
*
|
210
279
|
* @returns {Array.<string>} An array of parameter names. Must be treated as read-only. If the
|
211
280
|
* pattern has no parameters, an empty array is returned.
|
212
281
|
*/
|
213
|
-
UrlMatcher.prototype.parameters = function () {
|
214
|
-
return this
|
282
|
+
UrlMatcher.prototype.parameters = function (param) {
|
283
|
+
if (!isDefined(param)) return this.$$paramNames;
|
284
|
+
return this.params[param] || null;
|
285
|
+
};
|
286
|
+
|
287
|
+
/**
|
288
|
+
* @ngdoc function
|
289
|
+
* @name ui.router.util.type:UrlMatcher#validate
|
290
|
+
* @methodOf ui.router.util.type:UrlMatcher
|
291
|
+
*
|
292
|
+
* @description
|
293
|
+
* Checks an object hash of parameters to validate their correctness according to the parameter
|
294
|
+
* types of this `UrlMatcher`.
|
295
|
+
*
|
296
|
+
* @param {Object} params The object hash of parameters to validate.
|
297
|
+
* @returns {boolean} Returns `true` if `params` validates, otherwise `false`.
|
298
|
+
*/
|
299
|
+
UrlMatcher.prototype.validates = function (params) {
|
300
|
+
return this.params.$$validates(params);
|
215
301
|
};
|
216
302
|
|
217
303
|
/**
|
@@ -225,31 +311,54 @@ UrlMatcher.prototype.parameters = function () {
|
|
225
311
|
* treated as empty strings.
|
226
312
|
*
|
227
313
|
* @example
|
228
|
-
*
|
314
|
+
* <pre>
|
229
315
|
* new UrlMatcher('/user/{id}?q').format({ id:'bob', q:'yes' });
|
230
316
|
* // returns '/user/bob?q=yes'
|
231
|
-
*
|
317
|
+
* </pre>
|
232
318
|
*
|
233
319
|
* @param {Object} values the values to substitute for the parameters in this pattern.
|
234
320
|
* @returns {string} the formatted URL (path and optionally search part).
|
235
321
|
*/
|
236
322
|
UrlMatcher.prototype.format = function (values) {
|
237
|
-
|
238
|
-
|
323
|
+
values = values || {};
|
324
|
+
var segments = this.segments, params = this.parameters(), paramset = this.params;
|
325
|
+
if (!this.validates(values)) return null;
|
239
326
|
|
240
|
-
var nPath = segments.length-1, nTotal = params.length,
|
241
|
-
result = segments[0], i, search, value;
|
327
|
+
var i, search = false, nPath = segments.length - 1, nTotal = params.length, result = segments[0];
|
242
328
|
|
243
|
-
|
244
|
-
|
245
|
-
// TODO: Maybe we should throw on null here? It's not really good style to use '' and null interchangeabley
|
246
|
-
if (value != null) result += encodeURIComponent(value);
|
247
|
-
result += segments[i+1];
|
329
|
+
function encodeDashes(str) { // Replace dashes with encoded "\-"
|
330
|
+
return encodeURIComponent(str).replace(/-/g, function(c) { return '%5C%' + c.charCodeAt(0).toString(16).toUpperCase(); });
|
248
331
|
}
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
332
|
+
|
333
|
+
for (i = 0; i < nTotal; i++) {
|
334
|
+
var isPathParam = i < nPath;
|
335
|
+
var name = params[i], param = paramset[name], value = param.value(values[name]);
|
336
|
+
var isDefaultValue = param.isOptional && param.type.equals(param.value(), value);
|
337
|
+
var squash = isDefaultValue ? param.squash : false;
|
338
|
+
var encoded = param.type.encode(value);
|
339
|
+
|
340
|
+
if (isPathParam) {
|
341
|
+
var nextSegment = segments[i + 1];
|
342
|
+
if (squash === false) {
|
343
|
+
if (encoded != null) {
|
344
|
+
if (isArray(encoded)) {
|
345
|
+
result += map(encoded, encodeDashes).join("-");
|
346
|
+
} else {
|
347
|
+
result += encodeURIComponent(encoded);
|
348
|
+
}
|
349
|
+
}
|
350
|
+
result += nextSegment;
|
351
|
+
} else if (squash === true) {
|
352
|
+
var capture = result.match(/\/$/) ? /\/?(.*)/ : /(.*)/;
|
353
|
+
result += nextSegment.match(capture)[1];
|
354
|
+
} else if (isString(squash)) {
|
355
|
+
result += squash + nextSegment;
|
356
|
+
}
|
357
|
+
} else {
|
358
|
+
if (encoded == null || (isDefaultValue && squash !== false)) continue;
|
359
|
+
if (!isArray(encoded)) encoded = [ encoded ];
|
360
|
+
encoded = map(encoded, encodeURIComponent).join('&' + name + '=');
|
361
|
+
result += (search ? '&' : '?') + (name + '=' + encoded);
|
253
362
|
search = true;
|
254
363
|
}
|
255
364
|
}
|
@@ -257,6 +366,194 @@ UrlMatcher.prototype.format = function (values) {
|
|
257
366
|
return result;
|
258
367
|
};
|
259
368
|
|
369
|
+
/**
|
370
|
+
* @ngdoc object
|
371
|
+
* @name ui.router.util.type:Type
|
372
|
+
*
|
373
|
+
* @description
|
374
|
+
* Implements an interface to define custom parameter types that can be decoded from and encoded to
|
375
|
+
* string parameters matched in a URL. Used by {@link ui.router.util.type:UrlMatcher `UrlMatcher`}
|
376
|
+
* objects when matching or formatting URLs, or comparing or validating parameter values.
|
377
|
+
*
|
378
|
+
* See {@link ui.router.util.$urlMatcherFactory#methods_type `$urlMatcherFactory#type()`} for more
|
379
|
+
* information on registering custom types.
|
380
|
+
*
|
381
|
+
* @param {Object} config A configuration object which contains the custom type definition. The object's
|
382
|
+
* properties will override the default methods and/or pattern in `Type`'s public interface.
|
383
|
+
* @example
|
384
|
+
* <pre>
|
385
|
+
* {
|
386
|
+
* decode: function(val) { return parseInt(val, 10); },
|
387
|
+
* encode: function(val) { return val && val.toString(); },
|
388
|
+
* equals: function(a, b) { return this.is(a) && a === b; },
|
389
|
+
* is: function(val) { return angular.isNumber(val) isFinite(val) && val % 1 === 0; },
|
390
|
+
* pattern: /\d+/
|
391
|
+
* }
|
392
|
+
* </pre>
|
393
|
+
*
|
394
|
+
* @property {RegExp} pattern The regular expression pattern used to match values of this type when
|
395
|
+
* coming from a substring of a URL.
|
396
|
+
*
|
397
|
+
* @returns {Object} Returns a new `Type` object.
|
398
|
+
*/
|
399
|
+
function Type(config) {
|
400
|
+
extend(this, config);
|
401
|
+
}
|
402
|
+
|
403
|
+
/**
|
404
|
+
* @ngdoc function
|
405
|
+
* @name ui.router.util.type:Type#is
|
406
|
+
* @methodOf ui.router.util.type:Type
|
407
|
+
*
|
408
|
+
* @description
|
409
|
+
* Detects whether a value is of a particular type. Accepts a native (decoded) value
|
410
|
+
* and determines whether it matches the current `Type` object.
|
411
|
+
*
|
412
|
+
* @param {*} val The value to check.
|
413
|
+
* @param {string} key Optional. If the type check is happening in the context of a specific
|
414
|
+
* {@link ui.router.util.type:UrlMatcher `UrlMatcher`} object, this is the name of the
|
415
|
+
* parameter in which `val` is stored. Can be used for meta-programming of `Type` objects.
|
416
|
+
* @returns {Boolean} Returns `true` if the value matches the type, otherwise `false`.
|
417
|
+
*/
|
418
|
+
Type.prototype.is = function(val, key) {
|
419
|
+
return true;
|
420
|
+
};
|
421
|
+
|
422
|
+
/**
|
423
|
+
* @ngdoc function
|
424
|
+
* @name ui.router.util.type:Type#encode
|
425
|
+
* @methodOf ui.router.util.type:Type
|
426
|
+
*
|
427
|
+
* @description
|
428
|
+
* Encodes a custom/native type value to a string that can be embedded in a URL. Note that the
|
429
|
+
* return value does *not* need to be URL-safe (i.e. passed through `encodeURIComponent()`), it
|
430
|
+
* only needs to be a representation of `val` that has been coerced to a string.
|
431
|
+
*
|
432
|
+
* @param {*} val The value to encode.
|
433
|
+
* @param {string} key The name of the parameter in which `val` is stored. Can be used for
|
434
|
+
* meta-programming of `Type` objects.
|
435
|
+
* @returns {string} Returns a string representation of `val` that can be encoded in a URL.
|
436
|
+
*/
|
437
|
+
Type.prototype.encode = function(val, key) {
|
438
|
+
return val;
|
439
|
+
};
|
440
|
+
|
441
|
+
/**
|
442
|
+
* @ngdoc function
|
443
|
+
* @name ui.router.util.type:Type#decode
|
444
|
+
* @methodOf ui.router.util.type:Type
|
445
|
+
*
|
446
|
+
* @description
|
447
|
+
* Converts a parameter value (from URL string or transition param) to a custom/native value.
|
448
|
+
*
|
449
|
+
* @param {string} val The URL parameter value to decode.
|
450
|
+
* @param {string} key The name of the parameter in which `val` is stored. Can be used for
|
451
|
+
* meta-programming of `Type` objects.
|
452
|
+
* @returns {*} Returns a custom representation of the URL parameter value.
|
453
|
+
*/
|
454
|
+
Type.prototype.decode = function(val, key) {
|
455
|
+
return val;
|
456
|
+
};
|
457
|
+
|
458
|
+
/**
|
459
|
+
* @ngdoc function
|
460
|
+
* @name ui.router.util.type:Type#equals
|
461
|
+
* @methodOf ui.router.util.type:Type
|
462
|
+
*
|
463
|
+
* @description
|
464
|
+
* Determines whether two decoded values are equivalent.
|
465
|
+
*
|
466
|
+
* @param {*} a A value to compare against.
|
467
|
+
* @param {*} b A value to compare against.
|
468
|
+
* @returns {Boolean} Returns `true` if the values are equivalent/equal, otherwise `false`.
|
469
|
+
*/
|
470
|
+
Type.prototype.equals = function(a, b) {
|
471
|
+
return a == b;
|
472
|
+
};
|
473
|
+
|
474
|
+
Type.prototype.$subPattern = function() {
|
475
|
+
var sub = this.pattern.toString();
|
476
|
+
return sub.substr(1, sub.length - 2);
|
477
|
+
};
|
478
|
+
|
479
|
+
Type.prototype.pattern = /.*/;
|
480
|
+
|
481
|
+
Type.prototype.toString = function() { return "{Type:" + this.name + "}"; };
|
482
|
+
|
483
|
+
/** Given an encoded string, or a decoded object, returns a decoded object */
|
484
|
+
Type.prototype.$normalize = function(val) {
|
485
|
+
return this.is(val) ? val : this.decode(val);
|
486
|
+
};
|
487
|
+
|
488
|
+
/*
|
489
|
+
* Wraps an existing custom Type as an array of Type, depending on 'mode'.
|
490
|
+
* e.g.:
|
491
|
+
* - urlmatcher pattern "/path?{queryParam[]:int}"
|
492
|
+
* - url: "/path?queryParam=1&queryParam=2
|
493
|
+
* - $stateParams.queryParam will be [1, 2]
|
494
|
+
* if `mode` is "auto", then
|
495
|
+
* - url: "/path?queryParam=1 will create $stateParams.queryParam: 1
|
496
|
+
* - url: "/path?queryParam=1&queryParam=2 will create $stateParams.queryParam: [1, 2]
|
497
|
+
*/
|
498
|
+
Type.prototype.$asArray = function(mode, isSearch) {
|
499
|
+
if (!mode) return this;
|
500
|
+
if (mode === "auto" && !isSearch) throw new Error("'auto' array mode is for query parameters only");
|
501
|
+
|
502
|
+
function ArrayType(type, mode) {
|
503
|
+
function bindTo(type, callbackName) {
|
504
|
+
return function() {
|
505
|
+
return type[callbackName].apply(type, arguments);
|
506
|
+
};
|
507
|
+
}
|
508
|
+
|
509
|
+
// Wrap non-array value as array
|
510
|
+
function arrayWrap(val) { return isArray(val) ? val : (isDefined(val) ? [ val ] : []); }
|
511
|
+
// Unwrap array value for "auto" mode. Return undefined for empty array.
|
512
|
+
function arrayUnwrap(val) {
|
513
|
+
switch(val.length) {
|
514
|
+
case 0: return undefined;
|
515
|
+
case 1: return mode === "auto" ? val[0] : val;
|
516
|
+
default: return val;
|
517
|
+
}
|
518
|
+
}
|
519
|
+
function falsey(val) { return !val; }
|
520
|
+
|
521
|
+
// Wraps type (.is/.encode/.decode) functions to operate on each value of an array
|
522
|
+
function arrayHandler(callback, allTruthyMode) {
|
523
|
+
return function handleArray(val) {
|
524
|
+
val = arrayWrap(val);
|
525
|
+
var result = map(val, callback);
|
526
|
+
if (allTruthyMode === true)
|
527
|
+
return filter(result, falsey).length === 0;
|
528
|
+
return arrayUnwrap(result);
|
529
|
+
};
|
530
|
+
}
|
531
|
+
|
532
|
+
// Wraps type (.equals) functions to operate on each value of an array
|
533
|
+
function arrayEqualsHandler(callback) {
|
534
|
+
return function handleArray(val1, val2) {
|
535
|
+
var left = arrayWrap(val1), right = arrayWrap(val2);
|
536
|
+
if (left.length !== right.length) return false;
|
537
|
+
for (var i = 0; i < left.length; i++) {
|
538
|
+
if (!callback(left[i], right[i])) return false;
|
539
|
+
}
|
540
|
+
return true;
|
541
|
+
};
|
542
|
+
}
|
543
|
+
|
544
|
+
this.encode = arrayHandler(bindTo(type, 'encode'));
|
545
|
+
this.decode = arrayHandler(bindTo(type, 'decode'));
|
546
|
+
this.is = arrayHandler(bindTo(type, 'is'), true);
|
547
|
+
this.equals = arrayEqualsHandler(bindTo(type, 'equals'));
|
548
|
+
this.pattern = type.pattern;
|
549
|
+
this.$normalize = arrayHandler(bindTo(type, '$normalize'));
|
550
|
+
this.name = type.name;
|
551
|
+
this.$arrayMode = mode;
|
552
|
+
}
|
553
|
+
|
554
|
+
return new ArrayType(this, mode);
|
555
|
+
};
|
556
|
+
|
260
557
|
|
261
558
|
|
262
559
|
/**
|
@@ -264,25 +561,147 @@ UrlMatcher.prototype.format = function (values) {
|
|
264
561
|
* @name ui.router.util.$urlMatcherFactory
|
265
562
|
*
|
266
563
|
* @description
|
267
|
-
* Factory for {@link ui.router.util.type:UrlMatcher} instances. The factory
|
268
|
-
* under the name `$urlMatcherFactoryProvider`.
|
564
|
+
* Factory for {@link ui.router.util.type:UrlMatcher `UrlMatcher`} instances. The factory
|
565
|
+
* is also available to providers under the name `$urlMatcherFactoryProvider`.
|
269
566
|
*/
|
270
567
|
function $UrlMatcherFactory() {
|
568
|
+
$$UMFP = this;
|
569
|
+
|
570
|
+
var isCaseInsensitive = false, isStrictMode = true, defaultSquashPolicy = false;
|
271
571
|
|
272
|
-
|
572
|
+
function valToString(val) { return val != null ? val.toString().replace(/\//g, "%2F") : val; }
|
573
|
+
function valFromString(val) { return val != null ? val.toString().replace(/%2F/g, "/") : val; }
|
574
|
+
|
575
|
+
var $types = {}, enqueue = true, typeQueue = [], injector, defaultTypes = {
|
576
|
+
string: {
|
577
|
+
encode: valToString,
|
578
|
+
decode: valFromString,
|
579
|
+
// TODO: in 1.0, make string .is() return false if value is undefined/null by default.
|
580
|
+
// In 0.2.x, string params are optional by default for backwards compat
|
581
|
+
is: function(val) { return val == null || !isDefined(val) || typeof val === "string"; },
|
582
|
+
pattern: /[^/]*/
|
583
|
+
},
|
584
|
+
int: {
|
585
|
+
encode: valToString,
|
586
|
+
decode: function(val) { return parseInt(val, 10); },
|
587
|
+
is: function(val) { return isDefined(val) && this.decode(val.toString()) === val; },
|
588
|
+
pattern: /\d+/
|
589
|
+
},
|
590
|
+
bool: {
|
591
|
+
encode: function(val) { return val ? 1 : 0; },
|
592
|
+
decode: function(val) { return parseInt(val, 10) !== 0; },
|
593
|
+
is: function(val) { return val === true || val === false; },
|
594
|
+
pattern: /0|1/
|
595
|
+
},
|
596
|
+
date: {
|
597
|
+
encode: function (val) {
|
598
|
+
if (!this.is(val))
|
599
|
+
return undefined;
|
600
|
+
return [ val.getFullYear(),
|
601
|
+
('0' + (val.getMonth() + 1)).slice(-2),
|
602
|
+
('0' + val.getDate()).slice(-2)
|
603
|
+
].join("-");
|
604
|
+
},
|
605
|
+
decode: function (val) {
|
606
|
+
if (this.is(val)) return val;
|
607
|
+
var match = this.capture.exec(val);
|
608
|
+
return match ? new Date(match[1], match[2] - 1, match[3]) : undefined;
|
609
|
+
},
|
610
|
+
is: function(val) { return val instanceof Date && !isNaN(val.valueOf()); },
|
611
|
+
equals: function (a, b) { return this.is(a) && this.is(b) && a.toISOString() === b.toISOString(); },
|
612
|
+
pattern: /[0-9]{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[1-2][0-9]|3[0-1])/,
|
613
|
+
capture: /([0-9]{4})-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])/
|
614
|
+
},
|
615
|
+
json: {
|
616
|
+
encode: angular.toJson,
|
617
|
+
decode: angular.fromJson,
|
618
|
+
is: angular.isObject,
|
619
|
+
equals: angular.equals,
|
620
|
+
pattern: /[^/]*/
|
621
|
+
},
|
622
|
+
any: { // does not encode/decode
|
623
|
+
encode: angular.identity,
|
624
|
+
decode: angular.identity,
|
625
|
+
equals: angular.equals,
|
626
|
+
pattern: /.*/
|
627
|
+
}
|
628
|
+
};
|
629
|
+
|
630
|
+
function getDefaultConfig() {
|
631
|
+
return {
|
632
|
+
strict: isStrictMode,
|
633
|
+
caseInsensitive: isCaseInsensitive
|
634
|
+
};
|
635
|
+
}
|
636
|
+
|
637
|
+
function isInjectable(value) {
|
638
|
+
return (isFunction(value) || (isArray(value) && isFunction(value[value.length - 1])));
|
639
|
+
}
|
640
|
+
|
641
|
+
/**
|
642
|
+
* [Internal] Get the default value of a parameter, which may be an injectable function.
|
643
|
+
*/
|
644
|
+
$UrlMatcherFactory.$$getDefaultValue = function(config) {
|
645
|
+
if (!isInjectable(config.value)) return config.value;
|
646
|
+
if (!injector) throw new Error("Injectable functions cannot be called at configuration time");
|
647
|
+
return injector.invoke(config.value);
|
648
|
+
};
|
273
649
|
|
274
650
|
/**
|
275
651
|
* @ngdoc function
|
276
|
-
* @name ui.router.util.$urlMatcherFactory#
|
652
|
+
* @name ui.router.util.$urlMatcherFactory#caseInsensitive
|
277
653
|
* @methodOf ui.router.util.$urlMatcherFactory
|
278
654
|
*
|
279
655
|
* @description
|
280
|
-
*
|
281
|
-
*
|
282
|
-
* @param {
|
656
|
+
* Defines whether URL matching should be case sensitive (the default behavior), or not.
|
657
|
+
*
|
658
|
+
* @param {boolean} value `false` to match URL in a case sensitive manner; otherwise `true`;
|
659
|
+
* @returns {boolean} the current value of caseInsensitive
|
283
660
|
*/
|
284
|
-
this.
|
285
|
-
|
661
|
+
this.caseInsensitive = function(value) {
|
662
|
+
if (isDefined(value))
|
663
|
+
isCaseInsensitive = value;
|
664
|
+
return isCaseInsensitive;
|
665
|
+
};
|
666
|
+
|
667
|
+
/**
|
668
|
+
* @ngdoc function
|
669
|
+
* @name ui.router.util.$urlMatcherFactory#strictMode
|
670
|
+
* @methodOf ui.router.util.$urlMatcherFactory
|
671
|
+
*
|
672
|
+
* @description
|
673
|
+
* Defines whether URLs should match trailing slashes, or not (the default behavior).
|
674
|
+
*
|
675
|
+
* @param {boolean=} value `false` to match trailing slashes in URLs, otherwise `true`.
|
676
|
+
* @returns {boolean} the current value of strictMode
|
677
|
+
*/
|
678
|
+
this.strictMode = function(value) {
|
679
|
+
if (isDefined(value))
|
680
|
+
isStrictMode = value;
|
681
|
+
return isStrictMode;
|
682
|
+
};
|
683
|
+
|
684
|
+
/**
|
685
|
+
* @ngdoc function
|
686
|
+
* @name ui.router.util.$urlMatcherFactory#defaultSquashPolicy
|
687
|
+
* @methodOf ui.router.util.$urlMatcherFactory
|
688
|
+
*
|
689
|
+
* @description
|
690
|
+
* Sets the default behavior when generating or matching URLs with default parameter values.
|
691
|
+
*
|
692
|
+
* @param {string} value A string that defines the default parameter URL squashing behavior.
|
693
|
+
* `nosquash`: When generating an href with a default parameter value, do not squash the parameter value from the URL
|
694
|
+
* `slash`: When generating an href with a default parameter value, squash (remove) the parameter value, and, if the
|
695
|
+
* parameter is surrounded by slashes, squash (remove) one slash from the URL
|
696
|
+
* any other string, e.g. "~": When generating an href with a default parameter value, squash (remove)
|
697
|
+
* the parameter value from the URL and replace it with this string.
|
698
|
+
*/
|
699
|
+
this.defaultSquashPolicy = function(value) {
|
700
|
+
if (!isDefined(value)) return defaultSquashPolicy;
|
701
|
+
if (value !== true && value !== false && !isString(value))
|
702
|
+
throw new Error("Invalid squash policy: " + value + ". Valid policies: false, true, arbitrary-string");
|
703
|
+
defaultSquashPolicy = value;
|
704
|
+
return value;
|
286
705
|
};
|
287
706
|
|
288
707
|
/**
|
@@ -291,13 +710,14 @@ function $UrlMatcherFactory() {
|
|
291
710
|
* @methodOf ui.router.util.$urlMatcherFactory
|
292
711
|
*
|
293
712
|
* @description
|
294
|
-
* Creates a {@link ui.router.util.type:UrlMatcher} for the specified pattern.
|
295
|
-
*
|
713
|
+
* Creates a {@link ui.router.util.type:UrlMatcher `UrlMatcher`} for the specified pattern.
|
714
|
+
*
|
296
715
|
* @param {string} pattern The URL pattern.
|
297
|
-
* @
|
716
|
+
* @param {Object} config The config object hash.
|
717
|
+
* @returns {UrlMatcher} The UrlMatcher.
|
298
718
|
*/
|
299
|
-
this.compile = function (pattern) {
|
300
|
-
return new UrlMatcher(pattern,
|
719
|
+
this.compile = function (pattern, config) {
|
720
|
+
return new UrlMatcher(pattern, extend(getDefaultConfig(), config));
|
301
721
|
};
|
302
722
|
|
303
723
|
/**
|
@@ -306,20 +726,325 @@ function $UrlMatcherFactory() {
|
|
306
726
|
* @methodOf ui.router.util.$urlMatcherFactory
|
307
727
|
*
|
308
728
|
* @description
|
309
|
-
* Returns true if the specified object is a UrlMatcher
|
729
|
+
* Returns true if the specified object is a `UrlMatcher`, or false otherwise.
|
310
730
|
*
|
311
731
|
* @param {Object} object The object to perform the type check against.
|
312
|
-
* @returns {Boolean} Returns `true` if the object
|
732
|
+
* @returns {Boolean} Returns `true` if the object matches the `UrlMatcher` interface, by
|
733
|
+
* implementing all the same methods.
|
313
734
|
*/
|
314
735
|
this.isMatcher = function (o) {
|
315
|
-
|
736
|
+
if (!isObject(o)) return false;
|
737
|
+
var result = true;
|
738
|
+
|
739
|
+
forEach(UrlMatcher.prototype, function(val, name) {
|
740
|
+
if (isFunction(val)) {
|
741
|
+
result = result && (isDefined(o[name]) && isFunction(o[name]));
|
742
|
+
}
|
743
|
+
});
|
744
|
+
return result;
|
745
|
+
};
|
746
|
+
|
747
|
+
/**
|
748
|
+
* @ngdoc function
|
749
|
+
* @name ui.router.util.$urlMatcherFactory#type
|
750
|
+
* @methodOf ui.router.util.$urlMatcherFactory
|
751
|
+
*
|
752
|
+
* @description
|
753
|
+
* Registers a custom {@link ui.router.util.type:Type `Type`} object that can be used to
|
754
|
+
* generate URLs with typed parameters.
|
755
|
+
*
|
756
|
+
* @param {string} name The type name.
|
757
|
+
* @param {Object|Function} definition The type definition. See
|
758
|
+
* {@link ui.router.util.type:Type `Type`} for information on the values accepted.
|
759
|
+
* @param {Object|Function} definitionFn (optional) A function that is injected before the app
|
760
|
+
* runtime starts. The result of this function is merged into the existing `definition`.
|
761
|
+
* See {@link ui.router.util.type:Type `Type`} for information on the values accepted.
|
762
|
+
*
|
763
|
+
* @returns {Object} Returns `$urlMatcherFactoryProvider`.
|
764
|
+
*
|
765
|
+
* @example
|
766
|
+
* This is a simple example of a custom type that encodes and decodes items from an
|
767
|
+
* array, using the array index as the URL-encoded value:
|
768
|
+
*
|
769
|
+
* <pre>
|
770
|
+
* var list = ['John', 'Paul', 'George', 'Ringo'];
|
771
|
+
*
|
772
|
+
* $urlMatcherFactoryProvider.type('listItem', {
|
773
|
+
* encode: function(item) {
|
774
|
+
* // Represent the list item in the URL using its corresponding index
|
775
|
+
* return list.indexOf(item);
|
776
|
+
* },
|
777
|
+
* decode: function(item) {
|
778
|
+
* // Look up the list item by index
|
779
|
+
* return list[parseInt(item, 10)];
|
780
|
+
* },
|
781
|
+
* is: function(item) {
|
782
|
+
* // Ensure the item is valid by checking to see that it appears
|
783
|
+
* // in the list
|
784
|
+
* return list.indexOf(item) > -1;
|
785
|
+
* }
|
786
|
+
* });
|
787
|
+
*
|
788
|
+
* $stateProvider.state('list', {
|
789
|
+
* url: "/list/{item:listItem}",
|
790
|
+
* controller: function($scope, $stateParams) {
|
791
|
+
* console.log($stateParams.item);
|
792
|
+
* }
|
793
|
+
* });
|
794
|
+
*
|
795
|
+
* // ...
|
796
|
+
*
|
797
|
+
* // Changes URL to '/list/3', logs "Ringo" to the console
|
798
|
+
* $state.go('list', { item: "Ringo" });
|
799
|
+
* </pre>
|
800
|
+
*
|
801
|
+
* This is a more complex example of a type that relies on dependency injection to
|
802
|
+
* interact with services, and uses the parameter name from the URL to infer how to
|
803
|
+
* handle encoding and decoding parameter values:
|
804
|
+
*
|
805
|
+
* <pre>
|
806
|
+
* // Defines a custom type that gets a value from a service,
|
807
|
+
* // where each service gets different types of values from
|
808
|
+
* // a backend API:
|
809
|
+
* $urlMatcherFactoryProvider.type('dbObject', {}, function(Users, Posts) {
|
810
|
+
*
|
811
|
+
* // Matches up services to URL parameter names
|
812
|
+
* var services = {
|
813
|
+
* user: Users,
|
814
|
+
* post: Posts
|
815
|
+
* };
|
816
|
+
*
|
817
|
+
* return {
|
818
|
+
* encode: function(object) {
|
819
|
+
* // Represent the object in the URL using its unique ID
|
820
|
+
* return object.id;
|
821
|
+
* },
|
822
|
+
* decode: function(value, key) {
|
823
|
+
* // Look up the object by ID, using the parameter
|
824
|
+
* // name (key) to call the correct service
|
825
|
+
* return services[key].findById(value);
|
826
|
+
* },
|
827
|
+
* is: function(object, key) {
|
828
|
+
* // Check that object is a valid dbObject
|
829
|
+
* return angular.isObject(object) && object.id && services[key];
|
830
|
+
* }
|
831
|
+
* equals: function(a, b) {
|
832
|
+
* // Check the equality of decoded objects by comparing
|
833
|
+
* // their unique IDs
|
834
|
+
* return a.id === b.id;
|
835
|
+
* }
|
836
|
+
* };
|
837
|
+
* });
|
838
|
+
*
|
839
|
+
* // In a config() block, you can then attach URLs with
|
840
|
+
* // type-annotated parameters:
|
841
|
+
* $stateProvider.state('users', {
|
842
|
+
* url: "/users",
|
843
|
+
* // ...
|
844
|
+
* }).state('users.item', {
|
845
|
+
* url: "/{user:dbObject}",
|
846
|
+
* controller: function($scope, $stateParams) {
|
847
|
+
* // $stateParams.user will now be an object returned from
|
848
|
+
* // the Users service
|
849
|
+
* },
|
850
|
+
* // ...
|
851
|
+
* });
|
852
|
+
* </pre>
|
853
|
+
*/
|
854
|
+
this.type = function (name, definition, definitionFn) {
|
855
|
+
if (!isDefined(definition)) return $types[name];
|
856
|
+
if ($types.hasOwnProperty(name)) throw new Error("A type named '" + name + "' has already been defined.");
|
857
|
+
|
858
|
+
$types[name] = new Type(extend({ name: name }, definition));
|
859
|
+
if (definitionFn) {
|
860
|
+
typeQueue.push({ name: name, def: definitionFn });
|
861
|
+
if (!enqueue) flushTypeQueue();
|
862
|
+
}
|
863
|
+
return this;
|
316
864
|
};
|
317
|
-
|
865
|
+
|
866
|
+
// `flushTypeQueue()` waits until `$urlMatcherFactory` is injected before invoking the queued `definitionFn`s
|
867
|
+
function flushTypeQueue() {
|
868
|
+
while(typeQueue.length) {
|
869
|
+
var type = typeQueue.shift();
|
870
|
+
if (type.pattern) throw new Error("You cannot override a type's .pattern at runtime.");
|
871
|
+
angular.extend($types[type.name], injector.invoke(type.def));
|
872
|
+
}
|
873
|
+
}
|
874
|
+
|
875
|
+
// Register default types. Store them in the prototype of $types.
|
876
|
+
forEach(defaultTypes, function(type, name) { $types[name] = new Type(extend({name: name}, type)); });
|
877
|
+
$types = inherit($types, {});
|
878
|
+
|
318
879
|
/* No need to document $get, since it returns this */
|
319
|
-
this.$get = function () {
|
880
|
+
this.$get = ['$injector', function ($injector) {
|
881
|
+
injector = $injector;
|
882
|
+
enqueue = false;
|
883
|
+
flushTypeQueue();
|
884
|
+
|
885
|
+
forEach(defaultTypes, function(type, name) {
|
886
|
+
if (!$types[name]) $types[name] = new Type(type);
|
887
|
+
});
|
320
888
|
return this;
|
889
|
+
}];
|
890
|
+
|
891
|
+
this.Param = function Param(id, type, config, location) {
|
892
|
+
var self = this;
|
893
|
+
config = unwrapShorthand(config);
|
894
|
+
type = getType(config, type, location);
|
895
|
+
var arrayMode = getArrayMode();
|
896
|
+
type = arrayMode ? type.$asArray(arrayMode, location === "search") : type;
|
897
|
+
if (type.name === "string" && !arrayMode && location === "path" && config.value === undefined)
|
898
|
+
config.value = ""; // for 0.2.x; in 0.3.0+ do not automatically default to ""
|
899
|
+
var isOptional = config.value !== undefined;
|
900
|
+
var squash = getSquashPolicy(config, isOptional);
|
901
|
+
var replace = getReplace(config, arrayMode, isOptional, squash);
|
902
|
+
|
903
|
+
function unwrapShorthand(config) {
|
904
|
+
var keys = isObject(config) ? objectKeys(config) : [];
|
905
|
+
var isShorthand = indexOf(keys, "value") === -1 && indexOf(keys, "type") === -1 &&
|
906
|
+
indexOf(keys, "squash") === -1 && indexOf(keys, "array") === -1;
|
907
|
+
if (isShorthand) config = { value: config };
|
908
|
+
config.$$fn = isInjectable(config.value) ? config.value : function () { return config.value; };
|
909
|
+
return config;
|
910
|
+
}
|
911
|
+
|
912
|
+
function getType(config, urlType, location) {
|
913
|
+
if (config.type && urlType) throw new Error("Param '"+id+"' has two type configurations.");
|
914
|
+
if (urlType) return urlType;
|
915
|
+
if (!config.type) return (location === "config" ? $types.any : $types.string);
|
916
|
+
return config.type instanceof Type ? config.type : new Type(config.type);
|
917
|
+
}
|
918
|
+
|
919
|
+
// array config: param name (param[]) overrides default settings. explicit config overrides param name.
|
920
|
+
function getArrayMode() {
|
921
|
+
var arrayDefaults = { array: (location === "search" ? "auto" : false) };
|
922
|
+
var arrayParamNomenclature = id.match(/\[\]$/) ? { array: true } : {};
|
923
|
+
return extend(arrayDefaults, arrayParamNomenclature, config).array;
|
924
|
+
}
|
925
|
+
|
926
|
+
/**
|
927
|
+
* returns false, true, or the squash value to indicate the "default parameter url squash policy".
|
928
|
+
*/
|
929
|
+
function getSquashPolicy(config, isOptional) {
|
930
|
+
var squash = config.squash;
|
931
|
+
if (!isOptional || squash === false) return false;
|
932
|
+
if (!isDefined(squash) || squash == null) return defaultSquashPolicy;
|
933
|
+
if (squash === true || isString(squash)) return squash;
|
934
|
+
throw new Error("Invalid squash policy: '" + squash + "'. Valid policies: false, true, or arbitrary string");
|
935
|
+
}
|
936
|
+
|
937
|
+
function getReplace(config, arrayMode, isOptional, squash) {
|
938
|
+
var replace, configuredKeys, defaultPolicy = [
|
939
|
+
{ from: "", to: (isOptional || arrayMode ? undefined : "") },
|
940
|
+
{ from: null, to: (isOptional || arrayMode ? undefined : "") }
|
941
|
+
];
|
942
|
+
replace = isArray(config.replace) ? config.replace : [];
|
943
|
+
if (isString(squash))
|
944
|
+
replace.push({ from: squash, to: undefined });
|
945
|
+
configuredKeys = map(replace, function(item) { return item.from; } );
|
946
|
+
return filter(defaultPolicy, function(item) { return indexOf(configuredKeys, item.from) === -1; }).concat(replace);
|
947
|
+
}
|
948
|
+
|
949
|
+
/**
|
950
|
+
* [Internal] Get the default value of a parameter, which may be an injectable function.
|
951
|
+
*/
|
952
|
+
function $$getDefaultValue() {
|
953
|
+
if (!injector) throw new Error("Injectable functions cannot be called at configuration time");
|
954
|
+
var defaultValue = injector.invoke(config.$$fn);
|
955
|
+
if (defaultValue !== null && defaultValue !== undefined && !self.type.is(defaultValue))
|
956
|
+
throw new Error("Default value (" + defaultValue + ") for parameter '" + self.id + "' is not an instance of Type (" + self.type.name + ")");
|
957
|
+
return defaultValue;
|
958
|
+
}
|
959
|
+
|
960
|
+
/**
|
961
|
+
* [Internal] Gets the decoded representation of a value if the value is defined, otherwise, returns the
|
962
|
+
* default value, which may be the result of an injectable function.
|
963
|
+
*/
|
964
|
+
function $value(value) {
|
965
|
+
function hasReplaceVal(val) { return function(obj) { return obj.from === val; }; }
|
966
|
+
function $replace(value) {
|
967
|
+
var replacement = map(filter(self.replace, hasReplaceVal(value)), function(obj) { return obj.to; });
|
968
|
+
return replacement.length ? replacement[0] : value;
|
969
|
+
}
|
970
|
+
value = $replace(value);
|
971
|
+
return !isDefined(value) ? $$getDefaultValue() : self.type.$normalize(value);
|
972
|
+
}
|
973
|
+
|
974
|
+
function toString() { return "{Param:" + id + " " + type + " squash: '" + squash + "' optional: " + isOptional + "}"; }
|
975
|
+
|
976
|
+
extend(this, {
|
977
|
+
id: id,
|
978
|
+
type: type,
|
979
|
+
location: location,
|
980
|
+
array: arrayMode,
|
981
|
+
squash: squash,
|
982
|
+
replace: replace,
|
983
|
+
isOptional: isOptional,
|
984
|
+
value: $value,
|
985
|
+
dynamic: undefined,
|
986
|
+
config: config,
|
987
|
+
toString: toString
|
988
|
+
});
|
321
989
|
};
|
990
|
+
|
991
|
+
function ParamSet(params) {
|
992
|
+
extend(this, params || {});
|
993
|
+
}
|
994
|
+
|
995
|
+
ParamSet.prototype = {
|
996
|
+
$$new: function() {
|
997
|
+
return inherit(this, extend(new ParamSet(), { $$parent: this}));
|
998
|
+
},
|
999
|
+
$$keys: function () {
|
1000
|
+
var keys = [], chain = [], parent = this,
|
1001
|
+
ignore = objectKeys(ParamSet.prototype);
|
1002
|
+
while (parent) { chain.push(parent); parent = parent.$$parent; }
|
1003
|
+
chain.reverse();
|
1004
|
+
forEach(chain, function(paramset) {
|
1005
|
+
forEach(objectKeys(paramset), function(key) {
|
1006
|
+
if (indexOf(keys, key) === -1 && indexOf(ignore, key) === -1) keys.push(key);
|
1007
|
+
});
|
1008
|
+
});
|
1009
|
+
return keys;
|
1010
|
+
},
|
1011
|
+
$$values: function(paramValues) {
|
1012
|
+
var values = {}, self = this;
|
1013
|
+
forEach(self.$$keys(), function(key) {
|
1014
|
+
values[key] = self[key].value(paramValues && paramValues[key]);
|
1015
|
+
});
|
1016
|
+
return values;
|
1017
|
+
},
|
1018
|
+
$$equals: function(paramValues1, paramValues2) {
|
1019
|
+
var equal = true, self = this;
|
1020
|
+
forEach(self.$$keys(), function(key) {
|
1021
|
+
var left = paramValues1 && paramValues1[key], right = paramValues2 && paramValues2[key];
|
1022
|
+
if (!self[key].type.equals(left, right)) equal = false;
|
1023
|
+
});
|
1024
|
+
return equal;
|
1025
|
+
},
|
1026
|
+
$$validates: function $$validate(paramValues) {
|
1027
|
+
var keys = this.$$keys(), i, param, rawVal, normalized, encoded;
|
1028
|
+
for (i = 0; i < keys.length; i++) {
|
1029
|
+
param = this[keys[i]];
|
1030
|
+
rawVal = paramValues[keys[i]];
|
1031
|
+
if ((rawVal === undefined || rawVal === null) && param.isOptional)
|
1032
|
+
break; // There was no parameter value, but the param is optional
|
1033
|
+
normalized = param.type.$normalize(rawVal);
|
1034
|
+
if (!param.type.is(normalized))
|
1035
|
+
return false; // The value was not of the correct Type, and could not be decoded to the correct Type
|
1036
|
+
encoded = param.type.encode(normalized);
|
1037
|
+
if (angular.isString(encoded) && !param.type.pattern.exec(encoded))
|
1038
|
+
return false; // The value was of the correct type, but when encoded, did not match the Type's regexp
|
1039
|
+
}
|
1040
|
+
return true;
|
1041
|
+
},
|
1042
|
+
$$parent: undefined
|
1043
|
+
};
|
1044
|
+
|
1045
|
+
this.ParamSet = ParamSet;
|
322
1046
|
}
|
323
1047
|
|
324
1048
|
// Register as a provider so it's available to other providers
|
325
1049
|
angular.module('ui.router.util').provider('$urlMatcherFactory', $UrlMatcherFactory);
|
1050
|
+
angular.module('ui.router.util').run(['$urlMatcherFactory', function($urlMatcherFactory) { }]);
|