praxis 0.16.1 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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) { }]);
|