diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 000000000000..95b6716d2957 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,51 @@ +# Configuration file for https://circleci.com/gh/angular/material2 + +# Note: YAML anchors allow an object to be re-used, reducing duplication. +# The ampersand declares an alias for an object, then later the `<<: *name` +# syntax dereferences it. +# See http://blog.daemonl.com/2016/02/yaml.html +# To validate changes, use an online parser, eg. +# http://yaml-online-parser.appspot.com/ + +# Settings common to each job +anchor_1: &job_defaults + working_directory: ~/ng + docker: + - image: angular/ngcontainer:0.0.4 + +# After checkout, rebase on top of master. +# Similar to travis behavior, but not quite the same. +# By default, PRs are not rebased on top of master, which we want. +# See https://discuss.circleci.com/t/1662 +anchor_2: &post_checkout + post: git pull --ff-only origin "refs/pull/${CI_PULL_REQUEST//*pull\//}/merge" + +version: 2 +jobs: + build: + <<: *job_defaults + steps: + - checkout: + <<: *post_checkout + - restore_cache: + key: material2-{{ .Branch }}-{{ checksum "package-lock.json" }} + + - run: bazel run @nodejs//:npm install + # For some reason, circleci needs the postinstall to be run explicitly. + # This may be unnecessary once ngcontainer uses nodejs 8 + - run: bazel run @nodejs//:npm run postinstall + - run: bazel build src/cdk/... + - save_cache: + key: material2-{{ .Branch }}-{{ checksum "package-lock.json" }} + paths: + - "node_modules" + +workflows: + version: 2 + default_workflow: + jobs: + - build + +notify: + webhooks: + - url: https://us-central1-test-jperrott.cloudfunctions.net/circleCi diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index abe16bb71d0c..b727d641bbe5 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,69 +1,180 @@ # Angular Material components -src/lib/list/** @jelbourn @crisbeto -src/lib/tooltip/** @andrewseguin -src/lib/button-toggle/** @tinayuangao -src/lib/snack-bar/** @jelbourn @crisbeto @josephperrott -src/lib/radio/** @tinayuangao @devversion -src/lib/card/** @jelbourn -src/lib/input/** @mmalerba -src/lib/progress-spinner/** @jelbourn @crisbeto @josephperrott -src/lib/datepicker/** @mmalerba -src/lib/sort/** @andrewseguin -src/lib/autocomplete/** @kara @crisbeto -src/lib/chips/** @tinayuangao -src/lib/icon/** @jelbourn -src/lib/dialog/** @jelbourn @crisbeto -src/lib/progress-bar/** @jelbourn @crisbeto @josephperrott -src/lib/grid-list/** @kara @jelbourn -src/lib/select/** @kara @crisbeto -src/lib/expansion/** @josephperrott @jelbourn -src/lib/slide-toggle/** @devversion -src/lib/toolbar/** @devversion -src/lib/button/** @tinayuangao -src/lib/checkbox/** @tinayuangao @devversion -src/lib/table/** @andrewseguin -src/lib/slider/** @mmalerba -src/lib/sidenav/** @mmalerba -src/lib/menu/** @kara @crisbeto -src/lib/paginator/** @andrewseguin -src/lib/tabs/** @andrewseguin +/src/lib/* @jelbourn +/src/lib/autocomplete/** @kara @crisbeto +/src/lib/button-toggle/** @tinayuangao +/src/lib/button/** @tinayuangao +/src/lib/card/** @jelbourn +/src/lib/checkbox/** @tinayuangao @devversion +/src/lib/chips/** @tinayuangao +/src/lib/datepicker/** @mmalerba +/src/lib/dialog/** @jelbourn @crisbeto +/src/lib/expansion/** @josephperrott @jelbourn +/src/lib/form-field/** @mmalerba +/src/lib/grid-list/** @kara @jelbourn +/src/lib/icon/** @jelbourn +/src/lib/input/** @mmalerba +/src/lib/list/** @jelbourn @crisbeto @devversion +/src/lib/menu/** @kara @crisbeto +/src/lib/paginator/** @andrewseguin +/src/lib/progress-bar/** @jelbourn @crisbeto @josephperrott +/src/lib/progress-spinner/** @jelbourn @crisbeto @josephperrott +/src/lib/radio/** @tinayuangao @devversion +/src/lib/select/** @kara @crisbeto +/src/lib/sidenav/** @mmalerba +/src/lib/slide-toggle/** @devversion +/src/lib/slider/** @mmalerba +/src/lib/snack-bar/** @jelbourn @crisbeto @josephperrott +/src/lib/sort/** @andrewseguin +/src/lib/stepper/** @mmalerba +/src/lib/table/** @andrewseguin +/src/lib/tabs/** @andrewseguin +/src/lib/toolbar/** @devversion +/src/lib/tooltip/** @andrewseguin # Angular Material core -src/lib/core/selection/** @tinayuangao @jelbourn -src/lib/core/selection/pseudo*/** @crisbeto @jelbourn -src/lib/core/theming/** @jelbourn -src/lib/core/option/** @kara @crisbeto -src/lib/core/rxjs/** @jelbourn -src/lib/core/ripple/** @devversion -src/lib/core/a11y/** @jelbourn @devversion -src/lib/core/compatibility/** @jelbourn -src/lib/core/overlay/** @jelbourn @crisbeto -src/lib/core/overlay/scroll/** @andrewseguin @crisbeto -src/lib/core/platform/** @jelbourn @devversion -src/lib/core/bidi/** @jelbourn -src/lib/core/placeholder/** @kara @mmalerba -src/lib/core/portal/** @jelbourn -src/lib/core/typography/** @crisbeto -src/lib/core/datetime/** @mmalerba +/src/lib/core/* @jelbourn +/src/lib/core/animation/** @jelbourn +/src/lib/core/common-behaviors/** @jelbourn @devversion +/src/lib/core/datetime/** @mmalerba +/src/lib/core/error/** @crisbeto @mmalerba +/src/lib/core/gestures/** @jelbourn +/src/lib/core/line/** @jelbourn +/src/lib/core/option/** @kara @crisbeto +/src/lib/core/placeholder/** @kara @mmalerba +/src/lib/core/label/** @kara @mmalerba +/src/lib/core/ripple/** @devversion +/src/lib/core/selection/** @tinayuangao @jelbourn +/src/lib/core/selection/pseudo*/** @crisbeto @jelbourn +/src/lib/core/style/** @jelbourn +/src/lib/core/testing/** @jelbourn +/src/lib/core/theming/** @jelbourn +/src/lib/core/typography/** @crisbeto +/src/lib/core/util/** @jelbourn # CDK -src/cdk/coercion/** @jelbourn -src/cdk/rxjs/** @jelbourn -src/cdk/observers/** @jelbourn @crisbeto -src/cdk/collections/** @jelbourn @crisbeto @andrewseguin -src/cdk/a11y/** @jelbourn @devversion -src/cdk/platform/** @jelbourn @devversion -src/cdk/bidi/** @jelbourn -src/cdk/table/** @andrewseguin -src/cdk/portal/** @jelbourn +/src/cdk/* @jelbourn +/src/cdk/a11y/** @jelbourn @devversion +/src/cdk/accordion/** @josephperrott +/src/cdk/bidi/** @jelbourn +/src/cdk/coercion/** @jelbourn +/src/cdk/collections/** @jelbourn @crisbeto @andrewseguin +/src/cdk/keycodes/** @jelbourn +/src/cdk/layout/** @josephperrott +/src/cdk/observers/** @jelbourn @crisbeto +/src/cdk/overlay/** @jelbourn @crisbeto +/src/cdk/platform/** @jelbourn @devversion +/src/cdk/portal/** @jelbourn +/src/cdk/scrolling/** @andrewseguin @crisbeto +/src/cdk/stepper/** @mmalerba +/src/cdk/table/** @andrewseguin +/src/cdk/testing/** @devversion -# Tooling -tools/** @devversion @jelbourn -test/** @devversion @jelbourn -scripts/** @devversion @jelbourn +# Moment adapter package +/src/material-moment-adapter/** @mmalerba -# Docs examples -src/material-examples/** @amcdnl @jelbourn +# Material experimental package +/src/material-experimental/** @jelbourn -# Moment adapter package -src/material-moment-adapter/** @mmalerba +# Docs examples & guides +/guides/** @amcdnl @jelbourn +/src/material-examples/** @amcdnl @jelbourn + +# Demo app +/src/demo-app/* @jelbourn +/src/demo-app/a11y/** @tinayuangao +/src/demo-app/autocomplete/** @kara @crisbeto +/src/demo-app/baseline/** @mmalerba +/src/demo-app/button-toggle/** @tinayuangao +/src/demo-app/button/** @tinayuangao +/src/demo-app/card/** @jelbourn +/src/demo-app/checkbox/** @tinayuangao @devversion +/src/demo-app/chips/** @tinayuangao +/src/demo-app/dataset/** @andrewseguin +/src/demo-app/datepicker/** @mmalerba +/src/demo-app/demo-app/** @jelbourn +/src/demo-app/dialog/** @jelbourn @crisbeto +/src/demo-app/drawer/** @mmalerba +/src/demo-app/expansion/** @josephperrott +/src/demo-app/focus-origin/** @mmalerba +/src/demo-app/gestures/** @jelbourn +/src/demo-app/grid-list/** @kara @jelbourn +/src/demo-app/icon/** @jelbourn +/src/demo-app/input/** @mmalerba +/src/demo-app/list/** @jelbourn @crisbeto @devversion +/src/demo-app/live-announcer/** @jelbourn +/src/demo-app/menu/** @kara @crisbeto +/src/demo-app/overlay/** @jelbourn @crisbeto +/src/demo-app/platform/** @jelbourn @devversion +/src/demo-app/portal/** @jelbourn +/src/demo-app/progress-bar/** @jelbourn @crisbeto @josephperrott +/src/demo-app/progress-spinner/** @jelbourn @crisbeto @josephperrott +/src/demo-app/radio/** @tinayuangao @devversion +/src/demo-app/ripple/** @devversion +/src/demo-app/screen-type/** @josephperrott +/src/demo-app/select/** @kara @crisbeto +/src/demo-app/sidenav/** @mmalerba +/src/demo-app/slide-toggle/** @devversion +/src/demo-app/slider/** @mmalerba +/src/demo-app/snack-bar/** @jelbourn @crisbeto @josephperrott +/src/demo-app/stepper/** @mmalerba +/src/demo-app/table/** @andrewseguin +/src/demo-app/tabs/** @andrewseguin +/src/demo-app/toolbar/** @devversion +/src/demo-app/tooltip/** @andrewseguin +/src/demo-app/typography/** @crisbeto + +# E2E app +/e2e/* @jelbourn +/e2e/components/block-scroll-strategy-e2e.spec.ts @andrewseguin @crisbeto +/e2e/components/button-e2e.spec.ts @tinayuangao +/e2e/components/button-toggle-e2e.spec.ts @tinayuangao +/e2e/components/card-e2e.spec.ts @jelbourn +/e2e/components/checkbox-e2e.spec.ts @tinayuangao @devversion +/e2e/components/dialog-e2e.spec.ts @jelbourn @crisbeto +/e2e/components/expansion-e2e.spec.ts @josephperrott @jelbourn +/e2e/components/fullscreen-e2e.spec.ts @jelbourn +/e2e/components/grid-list-e2e.spec.ts @kara @jelbourn +/e2e/components/icon-e2e.spec.ts @jelbourn +/e2e/components/input-e2e.spec.ts @mmalerba +/e2e/components/list-e2e.spec.ts @jelbourn @crisbeto @devversion +/e2e/components/menu-e2e.spec.ts @kara @crisbeto +/e2e/components/progress-bar-e2e.spec.ts @jelbourn @crisbeto @josephperrott +/e2e/components/progress-spinner-e2e.spec.ts @jelbourn @crisbeto @josephperrott +/e2e/components/radio-e2e.spec.ts @tinayuangao @devversion +/e2e/components/sidenav-e2e.spec.ts @mmalerba +/e2e/components/slide-toggle-e2e.spec.ts @devversion +/e2e/components/stepper-e2e.spec.ts @mmalerba +/e2e/components/tabs-e2e.spec.ts @andrewseguin +/e2e/components/toolbar-e2e.spec.ts @devversion +/e2e/util/** @jelbourn +/src/e2e-app/* @jelbourn +/src/e2e-app/block-scroll-strategy/** @andrewseguin @crisbeto +/src/e2e-app/button/** @tinayuangao +/src/e2e-app/checkbox/** @tinayuangao @devversion +/src/e2e-app/dialog/** @jelbourn @crisbeto +/src/e2e-app/e2e-app/** @jelbourn +/src/e2e-app/fullscreen/** @jelbourn +/src/e2e-app/grid-list/** @kara @jelbourn +/src/e2e-app/icon/** @jelbourn +/src/e2e-app/input/** @mmalerba +/src/e2e-app/menu/** @kara @crisbeto +/src/e2e-app/progress-bar/** @jelbourn @crisbeto @josephperrott +/src/e2e-app/progress-spinner/** @jelbourn @crisbeto @josephperrott +/src/e2e-app/radio/** @tinayuangao @devversion +/src/e2e-app/sidenav/** @mmalerba +/src/e2e-app/slide-toggle/** @devversion +/src/e2e-app/tabs/** @andrewseguin + +# Universal app +/src/universal-app/** @jelbourn + +# Tooling +/.circleci/** @jelbourn +/scripts/** @devversion @jelbourn +/test/** @devversion @jelbourn +/tools/** @devversion @jelbourn + +# Misc +/* @jelbourn +/.github/** @jelbourn +/src/* @jelbourn diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 68123cd0c4a7..09cdd38d3b85 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -8,9 +8,9 @@ #### What are the steps to reproduce? - -Providing a Plunker (or similar) is the *best* way to get the team to see your issue. -Plunker template: https://goo.gl/DlHd6U +Providing a StackBlitz/Plunker (or similar) is the *best* way to get the team to see your issue.
+Plunker starter (using on `@master`): https://goo.gl/uDmqyY
+StackBlitz starter (using latest `npm` release): https://goo.gl/wwnhMV
#### What is the use-case or motivation for changing an existing behavior? diff --git a/.gitignore b/.gitignore index 24e821d2e946..9804dad5288e 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,4 @@ node_modules npm-debug.log testem.log /.chrome +/.git diff --git a/.travis.yml b/.travis.yml index e88eac92a861..fc47e68f03d4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ sudo: false dist: trusty node_js: - - '8' + - '7' addons: jwt: @@ -11,6 +11,7 @@ addons: # we alias FIREBASE_ACCESS_TOKEN to $SAUCE_ACCESS_KEY in env.sh and set the SAUCE_ACCESS_KEY there - secure: "PKts/IbxuJRWWOEeiGbl8Z9zds0M+hIdCH/g/E4WbQ9yzSvSbdwzfmRfFccQFjxjsrY7+SJMVjsURZy+xUyBpzqgWYHUItnSVqjZb8DlyAU2IXyg8TM9BVLkGGe6k5k4PIFVmfMMMzQwWMM0X0W9w3oYmfHL5egxwSHvf9HIqLolLNXg8sqamIdS5d5KoCXf1c+oRjN/IMBktzNBR6N4OFOZQXVoepXNiIvTWAcTtOPBvFWdKP2n7RVioHKdm4a85aCUpDJp+LYGaLqiQZoRzmzfVTnAhTAPdd4ao5w/+jojrfZIHV55bqYF9rLnQMTneKsiyVNVYJzOLuxmARa/EEKfZld+J3rX4/o4cogrU38YSZF+T7J9g/7CTsnIZ3F6W6m+8iJbIBh55nGOQi5PVe458Q/nGb3fgQd2Z4+6lK9k479H4Ssh/Y7hbVQbepqEVIXzZKqWX6/ZE4iWoR/Q2dm0hySFmmB/R2etixX5JxhnHvgobTYIQ+1liJVp/3YFW1ru64Yg6yz/V291Bhh9g31znmTROCJ/usAmZZaLUqW1TDKnLIMP+M74MF9XERqcWKywXRFwxP4E5uDnx/vAyN49gL+SDfrBUxUtXrTkKZAlglwo9SgA7cOYEPWrionvKcGm87gCBYHFUmXZNQVzh212fpuJYXb/vy0sPDj8La4=" chrome: stable + firefox: latest branches: only: @@ -22,13 +23,14 @@ jobs: - env: "MODE=aot" - env: "MODE=payload" - env: "MODE=prerender" - - env: "MODE=closure-compiler" + # Closure Compiler CI check is temporarily disabled until a new version of + # the tool is released with https://github.com/google/closure-compiler/pull/2600 + # - env: "MODE=closure-compiler" - env: "MODE=e2e" - env: "MODE=saucelabs_required" - env: "MODE=browserstack_required" - env: "MODE=travis_required" - - stage: Deploy - env: "DEPLOY_MODE=build-artifacts" + - env: "DEPLOY_MODE=build-artifacts" - env: "DEPLOY_MODE=docs-content" - env: "DEPLOY_MODE=screenshot-tool" - env: "DEPLOY_MODE=dashboard" @@ -56,3 +58,8 @@ script: cache: directories: - ./node_modules/ + +notifications: + webhooks: + urls: + - https://us-central1-test-jperrott.cloudfunctions.net/travisCi diff --git a/BUILD.bazel b/BUILD.bazel new file mode 100644 index 000000000000..c99090ea12ca --- /dev/null +++ b/BUILD.bazel @@ -0,0 +1,35 @@ +package(default_visibility = ["//visibility:public"]) + +# TODO(jelbourn): figure out if these workarounds are still needed + +# This rule belongs in node_modules/BUILD +# It's here as a workaround for +# https://github.com/bazelbuild/bazel/issues/374#issuecomment-296217940 +filegroup( + name = "node_modules", + # Performance workaround: list individual files + # Reduces the number of files as inputs to nodejs_binary: + # bazel query "deps(:node_modules)" | wc -l + # This won't scale in the general case. + # TODO(alexeagle): figure out what to do + srcs = glob(["/".join(["node_modules", pkg, "**", ext]) for pkg in [ + "@angular", + "jasmine", + "typescript", + "tslib", + "zone.js", + "rxjs", + "@types", + "tsickle", + "hammerjs", + "protobufjs", + "bytebuffer", + "reflect-metadata", + "minimist", + "moment", + ] for ext in [ + "*.js", + "*.json", + "*.d.ts", + ]]), +) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e330f4ecb0c..06c114e8728f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,502 @@ + +# [5.0.0-rc.2](https://github.com/angular/material2/compare/5.0.0-rc.1...5.0.0-rc.2) (2017-11-27) + + +### Bug Fixes + +* **overlay:** disposed overlays not removed from the key event stack ([#8226](https://github.com/angular/material2/issues/8226)) ([461dfaf](https://github.com/angular/material2/commit/461dfaf)) +* **tabs:** fix accidentally setting `top` instead of `width` after removing Renderer use ([#8602](https://github.com/angular/material2/issues/8602)) ([6e865b7](https://github.com/angular/material2/commit/6e865b7)) + +### Features +* The examples on [material.angular.io](https://material.angular.io) are now opened externally via +StackBlitz instead of Plunker. + + + +# [5.0.0-rc.1](https://github.com/angular/material2/compare/5.0.0-rc0...5.0.0-rc.1) (2017-11-20) + + +### Bug Fixes + +* **autosize:** incorrect height with long placeholders ([#8024](https://github.com/angular/material2/issues/8024)) ([ad7cb4a](https://github.com/angular/material2/commit/ad7cb4a)), closes [#8013](https://github.com/angular/material2/issues/8013) +* **cdk-observers:** prevent attribute renaming in closure compilers advanced optimizations ([#7894](https://github.com/angular/material2/issues/7894)) ([8dfe470](https://github.com/angular/material2/commit/8dfe470)) +* **checkbox:** Set aria-checkbox to mixed for indeterminate checkbox ([#8089](https://github.com/angular/material2/issues/8089)) ([3037a90](https://github.com/angular/material2/commit/3037a90)) +* **chip:** fix placeholder and text overlap ([#8468](https://github.com/angular/material2/issues/8468)) ([81db650](https://github.com/angular/material2/commit/81db650)) +* **chips:** remove chip bottom margin in sibling chips ([#8198](https://github.com/angular/material2/issues/8198)) ([d79903a](https://github.com/angular/material2/commit/d79903a)) +* **chips:** use all available space for the input ([#7462](https://github.com/angular/material2/issues/7462)) ([c725249](https://github.com/angular/material2/commit/c725249)) +* **datepicker:** add missing exportAs ([#7782](https://github.com/angular/material2/issues/7782)) ([d6d9ff8](https://github.com/angular/material2/commit/d6d9ff8)) +* **datepicker:** correct DST issues on IE 11 ([#7858](https://github.com/angular/material2/issues/7858)) ([2f2325a](https://github.com/angular/material2/commit/2f2325a)) +* **datepicker:** correct overlay broad style selector ([#8130](https://github.com/angular/material2/issues/8130)) ([f69c8e6](https://github.com/angular/material2/commit/f69c8e6)) +* **datepicker:** prevent `matInput` from clobbering date value ([#7831](https://github.com/angular/material2/issues/7831)) ([4b59ca1](https://github.com/angular/material2/commit/4b59ca1)) +* **drawer:** invalid margin declaration when rendering server-side ([#8324](https://github.com/angular/material2/issues/8324)) ([5600b80](https://github.com/angular/material2/commit/5600b80)) +* **drawer:** missing elevation shadow ([#8387](https://github.com/angular/material2/issues/8387)) ([b0756a2](https://github.com/angular/material2/commit/b0756a2)), closes [#8386](https://github.com/angular/material2/issues/8386) +* **drawer:** re-add openedStart and closedStart events ([#7747](https://github.com/angular/material2/issues/7747)) ([7610c7c](https://github.com/angular/material2/commit/7610c7c)) +* **expansion:** prevent memory leak by calling parent ngOnDestroy ([#8410](https://github.com/angular/material2/issues/8410)) ([f6bd9b0](https://github.com/angular/material2/commit/f6bd9b0)) +* **fab-buttons:** vertically align icons inside fab buttons ([#8442](https://github.com/angular/material2/issues/8442)) ([43217ef](https://github.com/angular/material2/commit/43217ef)) +* **form-field:** jumping underline in Edge and Firefox ([#8480](https://github.com/angular/material2/issues/8480)) ([c7ab877](https://github.com/angular/material2/commit/c7ab877)), closes [#8395](https://github.com/angular/material2/issues/8395) +* **icon:** remove IDs from source icon set from rendered output ([#8266](https://github.com/angular/material2/issues/8266)) ([76806e3](https://github.com/angular/material2/commit/76806e3)) +* **input:** add aria-required to inputs ([#8034](https://github.com/angular/material2/issues/8034)) ([8178d6f](https://github.com/angular/material2/commit/8178d6f)) +* **input:** remove native IE reveal icon ([#8439](https://github.com/angular/material2/issues/8439)) ([47055a7](https://github.com/angular/material2/commit/47055a7)), closes [#8390](https://github.com/angular/material2/issues/8390) +* **select:** error when attempting to open before init ([#8242](https://github.com/angular/material2/issues/8242)) ([ba36d3a](https://github.com/angular/material2/commit/ba36d3a)) +* **progress-spinner:** coerceNumber values ([#7791](https://github.com/angular/material2/issues/7791)) ([b6712f8](https://github.com/angular/material2/commit/b6712f8)) +* **list:** multi-line list item spacing ([#8339](https://github.com/angular/material2/issues/8339)) ([bb504ad](https://github.com/angular/material2/commit/bb504ad)), closes [#8333](https://github.com/angular/material2/issues/8333) +* **menu:** return focus to root trigger when closed by mouse ([#8348](https://github.com/angular/material2/issues/8348)) ([b085dc6](https://github.com/angular/material2/commit/b085dc6)), closes [#8290](https://github.com/angular/material2/issues/8290) +* **overlay:** better handling of server-side rendering ([#8422](https://github.com/angular/material2/issues/8422)) ([0f83b20](https://github.com/angular/material2/commit/0f83b20)), closes [#8412](https://github.com/angular/material2/issues/8412) +* **overlay:** complete key event stream on dispose ([#8341](https://github.com/angular/material2/issues/8341)) ([b437b45](https://github.com/angular/material2/commit/b437b45)) +* **overlay:** remove global keydown listener when there are no open overlays ([#8389](https://github.com/angular/material2/issues/8389)) ([131272a](https://github.com/angular/material2/commit/131272a)) +* **progress-spinner:** default strokeWidth to 10% of the diameter ([#7746](https://github.com/angular/material2/issues/7746)) ([b997353](https://github.com/angular/material2/commit/b997353)) +* **slide-toggle:** drag not working in edge ([#8421](https://github.com/angular/material2/issues/8421)) ([d6f287e](https://github.com/angular/material2/commit/d6f287e)), closes [#8391](https://github.com/angular/material2/issues/8391) +* **snack-bar:** complete onAction observable on close ([#8183](https://github.com/angular/material2/issues/8183)) ([bc8560e](https://github.com/angular/material2/commit/bc8560e)) +* **stepper:** update state when steps change ([#8398](https://github.com/angular/material2/issues/8398)) ([2bc0b41](https://github.com/angular/material2/commit/2bc0b41)) +* **tabs:** detach tab portal when tab hides from view ([#8486](https://github.com/angular/material2/issues/8486)) ([fbf2987](https://github.com/angular/material2/commit/fbf2987)) +* **tooltip:** allow toolip to reopen when closed by detaching overlay ([#8232](https://github.com/angular/material2/issues/8232)) ([0719c38](https://github.com/angular/material2/commit/0719c38)) +* consistently coerce boolean and number properties ([#7283](https://github.com/angular/material2/issues/7283)) ([3ca801a](https://github.com/angular/material2/commit/3ca801a)) +* replace extendObject utility w/ object spread ([#7372](https://github.com/angular/material2/issues/7372)) ([ea54edb](https://github.com/angular/material2/commit/ea54edb)) +* using correct global name in rollup bundle ([#8407](https://github.com/angular/material2/issues/8407)) ([40be1f2](https://github.com/angular/material2/commit/40be1f2)) +* TypeScript interfaces are now documented on https://material.angular.io + +### Features + +* **a11y:** add autoCapture option to cdkTrapFocus ([#7641](https://github.com/angular/material2/issues/7641)) ([20b47d7](https://github.com/angular/material2/commit/20b47d7)) +* **datepicker:** dispatch events when datepicker is opened and closed ([#7792](https://github.com/angular/material2/issues/7792)) ([998153a](https://github.com/angular/material2/commit/998153a)) +* **dialog:** add ariaLabel and focusOnOpen config options ([#6558](https://github.com/angular/material2/issues/6558)) ([dad5922](https://github.com/angular/material2/commit/dad5922)) +* **gestures:** add injection token for specifying Hammer.js options ([#8106](https://github.com/angular/material2/issues/8106)) ([f2a0206](https://github.com/angular/material2/commit/f2a0206)), closes [#7097](https://github.com/angular/material2/issues/7097) +* **menu:** allow disabling ripples on items ([#8388](https://github.com/angular/material2/issues/8388)) ([ce23395](https://github.com/angular/material2/commit/ce23395)), closes [#8261](https://github.com/angular/material2/issues/8261) +* **overlay:** add option to re-use last preferred position when re-applying to open connected overlay ([#7805](https://github.com/angular/material2/issues/7805)) ([f83beb8](https://github.com/angular/material2/commit/f83beb8)) +* **reposition-scroll-strategy:** add option for closing once the user scrolls away ([#8233](https://github.com/angular/material2/issues/8233)) ([58598c4](https://github.com/angular/material2/commit/58598c4)) +* **slider:** support specifying tabindex ([#7848](https://github.com/angular/material2/issues/7848)) ([8e9dade](https://github.com/angular/material2/commit/8e9dade)) +* **tab-nav-bar:** allow setting tabindex for links ([#7809](https://github.com/angular/material2/issues/7809)) ([a041253](https://github.com/angular/material2/commit/a041253)) + + + + +# [5.0.0-rc0 cesium-cephalopod](https://github.com/angular/material2/compare/2.0.0-beta.12...5.0.0-rc0) (2017-11-06) + +### Highlights + +* First release candidate for Angular Material and CDK! The team now believes that APIs and + behaviors are stable and mature enough to exit beta. Please continue to file issues that + help us eliminate more bugs from the forthcoming 5.0.0 release. Moving forward, the _major_ + version number of Angular Material and CDK will update alongside Angular itself. +* A [moment.js](http://momentjs.com/) implementation of the `DateAdapter` for `MatDatepicker` is + now available as `@angular/material-moment-adapter` +* Based on Angular 5.0 +* More consistent naming conventions across the board +* 60+ bug fixes + +### BREAKING CHANGES +* Angular Material now requires Angular 5, which itself requires TypeScript 2.4+ and RxJS 5.5.2+ +* `mat-icon` now uses `HttpClient` from `@angular/common/http` instead of `Http` from + `@angular/http`. Any unit tests that faked icon responses should be changed to use an + `HttpInterceptor`. +* `@angular/cdk/rxjs` has been removed in favor of [RxJS 5.5's lettable operators](https://github.com/ReactiveX/rxjs/blob/master/doc/lettable-operators.md). +* **toolbar:** in previous versions, any content of `mat-toolbar` not wrapped in a + `mat-toolbar-row` would be rendered inside of an implicitly created `mat-toolbar-row`. As of rc0, + this implicit row will no longer be created. This means that any custom application CSS that + targeted this implicitly created `mat-toolbar-row` will no longer apply. Users can re-add the + `mat-toolbar-row` in their own templates to match the original output structure. This + resolves a longstanding issue where `display: flex` styles were difficult to use on `mat-toolbar`. +* **accordion:** move CdkAccordion to `@angular/cdk/accordion` + - `CdkAccordion` and associated classes live in `@angular/cdk/accordion` + - `AccordionChild` is renamed to `CdkAccordionChild` + - `CdkAccordion` no longer has displayMode and hideToggle `@Inputs` + - `CdkAccordionItem` is now a `@Directive` +* **table**: + - The argument order for the `when` property of `matRowDef` and `cdkRowDef` has been changed + from `(rowData, index)` to `(index, rowData)` in order to match `trackBy`. +* **datepicker:** + - `fromIso8601` method on `DateAdapter` removed in favor of `deserialize` + - `DateAdapter` will return an invalid date instead of throwing an error + - The `userSelection` `@Output` of `mat-calendar` has been made internal-only +* **cdk/scrolling:** + - `ScrollDispatcher.getScrollContainers` has been renamed to `getAncestorScrollContainers` to + better match its behavior. + - The `ScrollDispatcher.scrollableReferences` property has been renamed to `scrollContainers`. + - The `ScrollDispatcher.scrollableContainsElement` method has been removed. + - The `Scrollable` class has been renamed to `CdkScrollable` for consistency. + - Any uses of the `ScrollDispatcher.scrolled` method have to be refactored to subscribe to the + returned Observable, instead of passing in the `callback`. Example + ```ts + // Before + scrollDispatcher.scrolled(50, () => ...); + + // After + scrollDispatcher.scrolled(50).subscribe(() => ...); + ``` +* **unique-selection:** move UniqueSelectionDispatcher to `@angular/cdk/collections` + (`UniqueSelectionDispatcher`, `UniqueSelectionDispatcherListener`, and + `UNIQUE_SELECTION_DISPATCHER_PROVIDER`) +* `MATERIAL_COMPATIBILITY_MODE`, `CompatibilityModule`, `NoConflictStyleCompatibilityMode`, + `MatPrefixRejector`, `MdPrefixRejector` symbols have been removed. +* `MAT_CONNECTED_OVERLAY_SCROLL_STRATEGY` is renamed to `CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY` + +### Deprecations +* The following classes have been renamed. The old names are still available as deprecated: +`OverlayOrigin` -> `CdkOverlayOrigin` +`ConnectedOverlayDirective` -> `CdkConnectedOverlay` +`PortalDirective` -> `CdkPortal` +`PortalHostDirective` -> `CdkPortalOutlet` +`ObserveContent` -> `CdkObserveContent` +* The following `@Output` names have been renamed. The old names are still available as deprecated: + - `mat-select` + - `onOpen` -> `opened` + - `onClose` -> `closed` + - `change` -> `selectionChange` + - `mat-sidenav` / `mat-drawer` + - `open` -> `opened` + - `close` -> `closed` + - `mat-menu` + - `close` -> `closed` + - `matMenuTriggerFor` + - `onMenuOpen` -> `menuOpened` + - `onMenuClose` -> `menuClosed` + - `mat-chip` + - `onSelectionChange` -> `selectionChange` + - `remove` -> `removed` + - `destroy` -> `destroyed` + - `mat-tab-group` + - `selectChange` -> `selectedTabChange` + + +### Features + +* **table:** add MatTableDataSource ([#6747](https://github.com/angular/material2/issues/6747)) ([a9600e7](https://github.com/angular/material2/commit/a9600e7)) +* **dialog:** support minWidth, minHeight, maxWidth and maxHeight ([#7488](https://github.com/angular/material2/issues/7488)) ([57f19cd](https://github.com/angular/material2/commit/57f19cd)) +* **overlay:** new keyboard dispatcher for targeting correct overlay ([#6682](https://github.com/angular/material2/issues/6682)) ([a2ca4d6](https://github.com/angular/material2/commit/a2ca4d6)) +* **snack-bar:** set snack bar to be responsive. ([#7485](https://github.com/angular/material2/issues/7485)) ([5b7982f](https://github.com/angular/material2/commit/5b7982f)) +* **sort:** use existing intl provider if one exists ([#7988](https://github.com/angular/material2/issues/7988)) ([c8df2c1](https://github.com/angular/material2/commit/c8df2c1)) +* **dialog:** add optional generic param for config data ([#7447](https://github.com/angular/material2/issues/7447)) ([b29ac45](https://github.com/angular/material2/commit/b29ac45)), closes [#4398](https://github.com/angular/material2/issues/4398) +* Most directives / components now define an `exportAs` name ([#7554](https://github.com/angular/material2/issues/7554)) ([fa441bc](https://github.com/angular/material2/commit/fa441bc)) + +### Bug Fixes + +* **block-scroll-strategy:** disable smooth scrolling before restoring scroll position ([#8132](https://github.com/angular/material2/issues/8132)) ([75bccde](https://github.com/angular/material2/commit/75bccde)), closes [#7139](https://github.com/angular/material2/issues/7139) +* **button:** focus styles not applied to programmatically focused buttons ([#5966](https://github.com/angular/material2/issues/5966)) ([a0bb1a3](https://github.com/angular/material2/commit/a0bb1a3)), closes [#7510](https://github.com/angular/material2/issues/7510) +* **button-toggle:** support two-way binding of value ([#7911](https://github.com/angular/material2/issues/7911)) ([ee4915c](https://github.com/angular/material2/commit/ee4915c)) +* **card:** change image path to https in example ([#7800](https://github.com/angular/material2/issues/7800)) ([65d3630](https://github.com/angular/material2/commit/65d3630)) +* **chip-list:** use role = listbox only if chip list is not empty ([#7664](https://github.com/angular/material2/issues/7664)) ([dc76c09](https://github.com/angular/material2/commit/dc76c09)) +* **chips:** programmatically selected chip stealing focus ([#7978](https://github.com/angular/material2/issues/7978)) ([8168667](https://github.com/angular/material2/commit/8168667)) +* **datepicker:** allow `DateAdapter` authors to have more control ove… ([#7346](https://github.com/angular/material2/issues/7346)) ([9fa075e](https://github.com/angular/material2/commit/9fa075e)) +* **datepicker:** use disabled state from FormControl ([#7514](https://github.com/angular/material2/issues/7514)) ([66e71c8](https://github.com/angular/material2/commit/66e71c8)) +* **dialog:** don't block other dialogs from opening while animating ([#8051](https://github.com/angular/material2/issues/8051)) ([cc4fc11](https://github.com/angular/material2/commit/cc4fc11)), closes [#6560](https://github.com/angular/material2/issues/6560) +* **drawer:** not restoring focus on close ([#7668](https://github.com/angular/material2/issues/7668)) ([3041124](https://github.com/angular/material2/commit/3041124)) +* **drawer:** unable to toggle while drawer is animating ([#6810](https://github.com/angular/material2/issues/6810)) ([085827f](https://github.com/angular/material2/commit/085827f)), closes [#6376](https://github.com/angular/material2/issues/6376) +* **expansion-panel:** prevent content from being clipped ([#7617](https://github.com/angular/material2/issues/7617)) ([aa77aa1](https://github.com/angular/material2/commit/aa77aa1)) +* **focus-trap:** server-side rendering error ([#7635](https://github.com/angular/material2/issues/7635)) ([f7a12b6](https://github.com/angular/material2/commit/f7a12b6)), closes [#7633](https://github.com/angular/material2/issues/7633) +* **focus-trap:** update focus trap attrs to camel case [#6799](https://github.com/angular/material2/issues/6799) ([#6960](https://github.com/angular/material2/issues/6960)) ([c663fad](https://github.com/angular/material2/commit/c663fad)) +* **form-field:** fix underline at different zoom levels ([#7567](https://github.com/angular/material2/issues/7567)) ([5cffd7c](https://github.com/angular/material2/commit/5cffd7c)) +* **form-field:** remove 200px width since it messes up flex layouts ([#7083](https://github.com/angular/material2/issues/7083)) ([160a511](https://github.com/angular/material2/commit/160a511)) +* **form-field:** remove specific mention of matInput in error ([#7727](https://github.com/angular/material2/issues/7727)) ([f17cb99](https://github.com/angular/material2/commit/f17cb99)) +* **icon:** use SafeResourceUrl in getSvgIconFromUrl ([#7535](https://github.com/angular/material2/issues/7535)) ([291a87c](https://github.com/angular/material2/commit/291a87c)) +* **input:** remove IE clear icon ([#8095](https://github.com/angular/material2/issues/8095)) ([2fa679b](https://github.com/angular/material2/commit/2fa679b)), closes [#8076](https://github.com/angular/material2/issues/8076) +* **menu:** add typography mat-font-weight ([7fe1b81](https://github.com/angular/material2/commit/7fe1b81)) +* **menu:** make @Output names consistent [#6677](https://github.com/angular/material2/issues/6677) ([#8053](https://github.com/angular/material2/issues/8053)) ([b2dd17a](https://github.com/angular/material2/commit/b2dd17a)) +* **menu:** not handling keyboard events when opened by mouse ([#4843](https://github.com/angular/material2/issues/4843)) ([d822a39](https://github.com/angular/material2/commit/d822a39)), closes [#4991](https://github.com/angular/material2/issues/4991) +* **menu:** wrong offset for nested menu in a fallback position ([#7562](https://github.com/angular/material2/issues/7562)) ([074f6ce](https://github.com/angular/material2/commit/074f6ce)), closes [#7549](https://github.com/angular/material2/issues/7549) +* **overlay:** CloseScrollStrategy not triggering change detection on close ([#7929](https://github.com/angular/material2/issues/7929)) ([c0ba25a](https://github.com/angular/material2/commit/c0ba25a)), closes [#7922](https://github.com/angular/material2/issues/7922) +* **overlay:** emitting to detachments stream when not attached ([#7944](https://github.com/angular/material2/issues/7944)) ([6fdc237](https://github.com/angular/material2/commit/6fdc237)) +* **overlay:** import BidiModule in OverlayModule ([#7566](https://github.com/angular/material2/issues/7566)) ([4321f32](https://github.com/angular/material2/commit/4321f32)) +* **overlay:** overlay class audits [#6372](https://github.com/angular/material2/issues/6372) ([#8056](https://github.com/angular/material2/issues/8056)) ([cd05b54](https://github.com/angular/material2/commit/cd05b54)) +* **overlay:** wait until after change detection to position overlays ([#6527](https://github.com/angular/material2/issues/6527)) ([f299d25](https://github.com/angular/material2/commit/f299d25)) +* **paginator:** fix select baseline; support mobile ([#7610](https://github.com/angular/material2/issues/7610)) ([c12e4b5](https://github.com/angular/material2/commit/c12e4b5)) +* **progress-spinner:** fallback animation not working ([#7599](https://github.com/angular/material2/issues/7599)) ([4bb696e](https://github.com/angular/material2/commit/4bb696e)) +* **progress-spinner:** inaccurate stroke width on really small spinners ([#7725](https://github.com/angular/material2/issues/7725)) ([f52f078](https://github.com/angular/material2/commit/f52f078)), closes [#7686](https://github.com/angular/material2/issues/7686) +* **progress-spinner:** spinner with narrower stroke not taking up entire element ([#7686](https://github.com/angular/material2/issues/7686)) ([2361983](https://github.com/angular/material2/commit/2361983)), closes [#7674](https://github.com/angular/material2/issues/7674) +* **scroll:** Replace references to scrollableReferences ([#7752](https://github.com/angular/material2/issues/7752)) ([9673f63](https://github.com/angular/material2/commit/9673f63)) +* **select:** errors not shown on submit ([#7640](https://github.com/angular/material2/issues/7640)) ([d2f41a4](https://github.com/angular/material2/commit/d2f41a4)), closes [#7634](https://github.com/angular/material2/issues/7634) +* **select:** make @Output names consistent [#6677](https://github.com/angular/material2/issues/6677) ([#8052](https://github.com/angular/material2/issues/8052)) ([f59abdb](https://github.com/angular/material2/commit/f59abdb)) +* **select:** not scrolling active option into view when typing ([#7620](https://github.com/angular/material2/issues/7620)) ([717f252](https://github.com/angular/material2/commit/717f252)) +* **select:** remove inert focus call ([#7729](https://github.com/angular/material2/issues/7729)) ([70c349c](https://github.com/angular/material2/commit/70c349c)) +* **select:** support typing to select items on when closed ([#7885](https://github.com/angular/material2/issues/7885)) ([8edb416](https://github.com/angular/material2/commit/8edb416)) +* **select:** unable to preselect array value in single selection mode ([#7603](https://github.com/angular/material2/issues/7603)) ([d55aa0c](https://github.com/angular/material2/commit/d55aa0c)), closes [#7584](https://github.com/angular/material2/issues/7584) +* **select:** wrong cursor on disabled select ([#7696](https://github.com/angular/material2/issues/7696)) ([9b4f435](https://github.com/angular/material2/commit/9b4f435)), closes [#7695](https://github.com/angular/material2/issues/7695) +* **selection-list:** fix option value coercion and selection events ([#6901](https://github.com/angular/material2/issues/6901)) ([80671bf](https://github.com/angular/material2/commit/80671bf)), closes [#6864](https://github.com/angular/material2/issues/6864) +* **snack-bar:** add content fade in animation ([#7504](https://github.com/angular/material2/issues/7504)) ([2b9c470](https://github.com/angular/material2/commit/2b9c470)) +* **snackbar:** swap enter and exit animation curves ([#6791](https://github.com/angular/material2/issues/6791)) ([4f571b1](https://github.com/angular/material2/commit/4f571b1)) +* **sort:** fix arrow on width-constrained headers ([#7569](https://github.com/angular/material2/issues/7569)) ([147ae46](https://github.com/angular/material2/commit/147ae46)) +* **spinner:** set initial value for spinner to 0. ([#8139](https://github.com/angular/material2/issues/8139)) ([9e4c636](https://github.com/angular/material2/commit/9e4c636)) +* **stepper:** don't grey out non-linear steps ([#7479](https://github.com/angular/material2/issues/7479)) ([60707b3](https://github.com/angular/material2/commit/60707b3)), closes [#7260](https://github.com/angular/material2/issues/7260) +* **stepper:** error when selectedIndex is pre-set ([#8035](https://github.com/angular/material2/issues/8035)) ([cf11ff2](https://github.com/angular/material2/commit/cf11ff2)), closes [#8031](https://github.com/angular/material2/issues/8031) +* **table:** broaden abstraction for filtering ([#8059](https://github.com/angular/material2/issues/8059)) ([d47b37a](https://github.com/angular/material2/commit/d47b37a)) +* **table:** cell content should not stretch width ([#7666](https://github.com/angular/material2/issues/7666)) ([bb424e2](https://github.com/angular/material2/commit/bb424e2)) +* **table:** empty string should be sorted right ([#8011](https://github.com/angular/material2/issues/8011)) ([58627c4](https://github.com/angular/material2/commit/58627c4)) +* **table:** render cells even if data is falsy ([#7914](https://github.com/angular/material2/issues/7914)) ([f601e83](https://github.com/angular/material2/commit/f601e83)) +* **table:** switch when arguments ([#7516](https://github.com/angular/material2/issues/7516)) ([a2129fc](https://github.com/angular/material2/commit/a2129fc)) +* **table:** Provide a provider if exists. ([#7895](https://github.com/angular/material2/issues/7895)) ([9a05ecd](https://github.com/angular/material2/commit/9a05ecd)), closes [#7344](https://github.com/angular/material2/issues/7344) +* **table:** throw error when missing row defs ([#7751](https://github.com/angular/material2/issues/7751)) ([55476e2](https://github.com/angular/material2/commit/55476e2)) +* **table:** update implicit when using trackby ([#7893](https://github.com/angular/material2/issues/7893)) ([f806286](https://github.com/angular/material2/commit/f806286)) +* **tabs:** incorrect ripple color for tabs with background ([#8123](https://github.com/angular/material2/issues/8123)) ([02d3eb6](https://github.com/angular/material2/commit/02d3eb6)) +* **toolbar:** no longer auto-generate toolbar rows ([#6661](https://github.com/angular/material2/issues/6661)) ([c3405aa](https://github.com/angular/material2/commit/c3405aa)), closes [#6004](https://github.com/angular/material2/issues/6004) [#1718](https://github.com/angular/material2/issues/1718) +* **tooltip:** don't open from programmatic focus ([#7258](https://github.com/angular/material2/issues/7258)) ([90a55fa](https://github.com/angular/material2/commit/90a55fa)), closes [#7245](https://github.com/angular/material2/issues/7245) +* **viewport-ruler:** incorrectly caching viewport size ([#7951](https://github.com/angular/material2/issues/7951)) ([0d6d9cc](https://github.com/angular/material2/commit/0d6d9cc)) +* consistent names for all cdk directives ([#8088](https://github.com/angular/material2/issues/8088)) ([f08b3f0](https://github.com/angular/material2/commit/f08b3f0)) +* don't show sanity check messages in tests ([#8080](https://github.com/angular/material2/issues/8080)) ([d17f9d2](https://github.com/angular/material2/commit/d17f9d2)) +* user-select mixin ignores value ([#7992](https://github.com/angular/material2/issues/7992)) ([eaa4a36](https://github.com/angular/material2/commit/eaa4a36)) + +### Performance Improvements + +* **scroll** remove persistent global scroll listener ([#7560](https://github.com/angular/material2/issues/7560)) ([d6698e1](https://github.com/angular/material2/commit/d6698e1)), closes [#6882](https://github.com/angular/material2/issues/6882) +* **drawer:** drawer content repainting on scroll ([#7719](https://github.com/angular/material2/issues/7719)) ([131e98f](https://github.com/angular/material2/commit/131e98f)) +* **focus-monitor:** use passive touch listener ([#7957](https://github.com/angular/material2/issues/7957)) ([ff7a13b](https://github.com/angular/material2/commit/ff7a13b)) +* **tabs:** avoid repainting while scrolling ([#7889](https://github.com/angular/material2/issues/7889)) ([943395e](https://github.com/angular/material2/commit/943395e)) + +### Code Refactoring + +* **accordion:** move CdkAccordion to [<@S1DQE0YR5|@angular-core-eng>](https://github.com/angular)/cdk ([#7530](https://github.com/angular/material2/issues/7530)) ([4d04472](https://github.com/angular/material2/commit/4d04472)) +* remove compatibility mode ([#7689](https://github.com/angular/material2/issues/7689)) ([dcef604](https://github.com/angular/material2/commit/dcef604)) +* switch to HttpClient ([#6702](https://github.com/angular/material2/issues/6702)) ([0ea4370](https://github.com/angular/material2/commit/0ea4370)) + + +# [2.0.0-beta.12 marble-mustache](https://github.com/angular/material2/compare/2.0.0-beta.11...2.0.0-beta.12) (2017-10-05) + +### Highlights + +* Progress spinner is now entirely css-based. +* Fixed sidenav: the sidenav can now be configured to use fixed positioning. This resolves a longstanding issue where sidenav-container would always introduce a scrolling region. +* `mat-select` is now used inside `mat-form-field`. This makes all of the existing form-field features available with `mat-select`, including hints, errors, prefixes, and suffixes. This also ensures that `mat-select` and `matInput` have a consistent presentation. + +```html + + + {{ state }} + + + +# [2.0.0-beta.12 marble-mustache](https://github.com/angular/material2/compare/2.0.0-beta.11...2.0.0-beta.12) (2017-10-05) +``` + + +### Breaking Changes + +* All "md" prefixes have been removed. See the [deprecation notice in the beta.11 notes for more +information](https://github.com/angular/material2/blob/master/CHANGELOG.md#deprecation-of-md-prefix). +* All cdk re-exports in `@angular/material` have been removed. See the [the beta.10 notes for more +information](https://github.com/angular/material2/blob/master/CHANGELOG.md#breaking-changes-2). +* Previously the `ScrollDispatcher.scrolled` subscription would react both on scroll events and on window resize events. Now it only reacts to scroll events. To react to resize events, subscribe to the `ViewportRuler.change()` stream. +* `UniqueSelectionDispatcher`, `UniqueSelectionDispatcherListener` and `UNIQUE_SELECTION_DISPATCHER_PROVIDER` are no longer +available from @angular/material and instead must be imported from @angular/cdk/collections +* `isFocusTrapEnabled` is now properly marked internal. +* The `OverlayRef.getState` method has been renamed to `OverlayRef.getConfig`. +* `defaultErrorStateMatcher` has been replaced by `ErrorStateMatcher`. For more info, see the [input docs](https://github.com/angular/material2/blob/master/src/lib/input/input.md#custom-error-matcher). + +### Features + +* **autocomplete:** add md-autocomplete classes to overlay panel ([#7176](https://github.com/angular/material2/issues/7176)) ([f8cd790](https://github.com/angular/material2/commit/f8cd790)), closes [#4196](https://github.com/angular/material2/issues/4196) +* **dialog:** add datepicker dialog and popup classes for easy styling ([#7013](https://github.com/angular/material2/issues/7013)) ([0ff8d5d](https://github.com/angular/material2/commit/0ff8d5d)) +* **menu:** support typeahead focus ([#7385](https://github.com/angular/material2/issues/7385)) ([f0d20ca](https://github.com/angular/material2/commit/f0d20ca)) +* **nav-tabs:** add `mat-tab-label-active` class to active nav tab labels ([#7508](https://github.com/angular/material2/issues/7508)) ([00e9338](https://github.com/angular/material2/commit/00e9338)) +* **progress-spinner:** switch to css-based animation ([#6551](https://github.com/angular/material2/issues/6551)) ([630dfad](https://github.com/angular/material2/commit/630dfad)) +* **select:** add support for custom error state matcher ([#7443](https://github.com/angular/material2/issues/7443)) ([a774688](https://github.com/angular/material2/commit/a774688)), closes [#7419](https://github.com/angular/material2/issues/7419) +* **select:** make select work inside form-field ([#6488](https://github.com/angular/material2/issues/6488)) ([d914cc4](https://github.com/angular/material2/commit/d914cc4)) +* **selection-model:** de/select multiple values at the same time ([#7001](https://github.com/angular/material2/issues/7001)) ([e52beeb](https://github.com/angular/material2/commit/e52beeb)) +* **sidenav:** Add support for fixed sidenavs ([#6712](https://github.com/angular/material2/issues/6712)) ([61579bc](https://github.com/angular/material2/commit/61579bc)) +* **sort:** add enter and leave arrow animations ([#7180](https://github.com/angular/material2/issues/7180)) ([2d350a0](https://github.com/angular/material2/commit/2d350a0)) +* **table:** add row when predicate ([#6795](https://github.com/angular/material2/issues/6795)) ([0875b85](https://github.com/angular/material2/commit/0875b85)) +* **viewport-ruler:** add common window resize handler ([#7113](https://github.com/angular/material2/issues/7113)) ([3b0915a](https://github.com/angular/material2/commit/3b0915a)) + +### Bug Fixes + +* add exportAs to missing components ([#7392](https://github.com/angular/material2/issues/7392)) ([31e9775](https://github.com/angular/material2/commit/31e9775)), closes [#7361](https://github.com/angular/material2/issues/7361) +* remove all md prefixes ([#7241](https://github.com/angular/material2/issues/7241)) ([20a23f1](https://github.com/angular/material2/commit/20a23f1)) +* **slide-toggle:** report change to model before firing a change event ([#7076](https://github.com/angular/material2/issues/7076)) ([c82fca8](https://github.com/angular/material2/commit/c82fca8)), closes [#7074](https://github.com/angular/material2/issues/7074) +* remove cdk re-exports from [@angular](https://github.com/angular)/material ([#7112](https://github.com/angular/material2/issues/7112)) ([f9b5ccd](https://github.com/angular/material2/commit/f9b5ccd)) +* **autocomplete:** don't open panel for readonly inputs ([#7271](https://github.com/angular/material2/issues/7271)) ([5f8615f](https://github.com/angular/material2/commit/5f8615f)), closes [#7269](https://github.com/angular/material2/issues/7269) +* **autocomplete:** emit closing action for escape keydown event ([#6250](https://github.com/angular/material2/issues/6250)) ([f4673a5](https://github.com/angular/material2/commit/f4673a5)) +* **autocomplete:** empty not cleaning up on tab ([#7270](https://github.com/angular/material2/issues/7270)) ([6be0462](https://github.com/angular/material2/commit/6be0462)), closes [#7268](https://github.com/angular/material2/issues/7268) +* **autocomplete:** error if panel is added asynchronously ([#7078](https://github.com/angular/material2/issues/7078)) ([504ba70](https://github.com/angular/material2/commit/504ba70)), closes [#7069](https://github.com/angular/material2/issues/7069) +* **autocomplete:** remove invalid aria markup ([#7107](https://github.com/angular/material2/issues/7107)) ([6bd6b9f](https://github.com/angular/material2/commit/6bd6b9f)), closes [#7100](https://github.com/angular/material2/issues/7100) +* **button:** allow for elevation to be overwritten ([#7305](https://github.com/angular/material2/issues/7305)) ([92a868e](https://github.com/angular/material2/commit/92a868e)), closes [#7264](https://github.com/angular/material2/issues/7264) +* **calendar:** not reacting to min/max boundary changes ([#7234](https://github.com/angular/material2/issues/7234)) ([eb012cc](https://github.com/angular/material2/commit/eb012cc)), closes [#7202](https://github.com/angular/material2/issues/7202) +* **checkbox:** defaulting to invalid name and value attributes ([#7130](https://github.com/angular/material2/issues/7130)) ([26788f1](https://github.com/angular/material2/commit/26788f1)) +* **checkbox:** support native tabindex attribute ([#6793](https://github.com/angular/material2/issues/6793)) ([0270cf5](https://github.com/angular/material2/commit/0270cf5)) +* **chips:** do not set chips value if there's no ngControl or value ([#7285](https://github.com/angular/material2/issues/7285)) ([d9ba13f](https://github.com/angular/material2/commit/d9ba13f)) +* **chips:** fix chip list focus and keyboard behaviors ([#7319](https://github.com/angular/material2/issues/7319)) ([f166468](https://github.com/angular/material2/commit/f166468)) +* **common:** don't log doctype warning when rendering server-side ([#6833](https://github.com/angular/material2/issues/6833)) ([f8ed442](https://github.com/angular/material2/commit/f8ed442)) +* **common-module:** check if computed styles are available ([#7003](https://github.com/angular/material2/issues/7003)) ([5da9e64](https://github.com/angular/material2/commit/5da9e64)), closes [#7000](https://github.com/angular/material2/issues/7000) +* **datepicker:** make sure _datepickerInput exists before accessing its ([#7033](https://github.com/angular/material2/issues/7033)) ([2129b7a](https://github.com/angular/material2/commit/2129b7a)) +* **dialog:** directionality not injected into child components ([#7111](https://github.com/angular/material2/issues/7111)) ([daa3880](https://github.com/angular/material2/commit/daa3880)) +* **drawer:** backdrop not transitioning on close ([#6651](https://github.com/angular/material2/issues/6651)) ([80310a5](https://github.com/angular/material2/commit/80310a5)) +* **drawer:** drawer container animating when open by default ([#7129](https://github.com/angular/material2/issues/7129)) ([4d278dd](https://github.com/angular/material2/commit/4d278dd)), closes [#7007](https://github.com/angular/material2/issues/7007) +* **drawer:** drawer container not reacting to drawer removal ([#7060](https://github.com/angular/material2/issues/7060)) ([b0b91f4](https://github.com/angular/material2/commit/b0b91f4)), closes [#6271](https://github.com/angular/material2/issues/6271) +* **drawer:** open event not firing on init ([#7214](https://github.com/angular/material2/issues/7214)) ([ba5653d](https://github.com/angular/material2/commit/ba5653d)), closes [#7208](https://github.com/angular/material2/issues/7208) +* **input:** apply readonly attribute when readonly ([#7439](https://github.com/angular/material2/issues/7439)) ([01622b1](https://github.com/angular/material2/commit/01622b1)) +* **input:** don't highlight container when readonly input is focused ([#7273](https://github.com/angular/material2/issues/7273)) ([f076390](https://github.com/angular/material2/commit/f076390)) +* **input:** make autosize work inside tabs & stepper ([#7341](https://github.com/angular/material2/issues/7341)) ([c6824d5](https://github.com/angular/material2/commit/c6824d5)) +* **list-key-manager:** align matching logic with native listbox ([#7212](https://github.com/angular/material2/issues/7212)) ([846cc13](https://github.com/angular/material2/commit/846cc13)) +* **list-key-manager:** don't focus disabled items in typeahead mode ([#7382](https://github.com/angular/material2/issues/7382)) ([1823b2f](https://github.com/angular/material2/commit/1823b2f)) +* **menu:** multiple close events for a single close ([#7037](https://github.com/angular/material2/issues/7037)) ([2dcb76c](https://github.com/angular/material2/commit/2dcb76c)) +* **menu:** nested menu error when items are rendered in a repeater ([#6766](https://github.com/angular/material2/issues/6766)) ([7a96570](https://github.com/angular/material2/commit/7a96570)), closes [#6765](https://github.com/angular/material2/issues/6765) +* **overlay:** detach method returns undefined ([#7449](https://github.com/angular/material2/issues/7449)) ([0584cdf](https://github.com/angular/material2/commit/0584cdf)), closes [#7408](https://github.com/angular/material2/issues/7408) +* **paginator:** page size selector not working ([#7263](https://github.com/angular/material2/issues/7263)) ([2b3d795](https://github.com/angular/material2/commit/2b3d795)) +* **radio:** defaulting to invalid name attribute ([#7131](https://github.com/angular/material2/issues/7131)) ([c5e162b](https://github.com/angular/material2/commit/c5e162b)) +* **ripple:** handle touch events ([#7299](https://github.com/angular/material2/issues/7299)) ([fe0864b](https://github.com/angular/material2/commit/fe0864b)), closes [#7062](https://github.com/angular/material2/issues/7062) +* **ripple:** remove unused ScrollDispatchModule ([#7528](https://github.com/angular/material2/issues/7528)) ([4a1a68d](https://github.com/angular/material2/commit/4a1a68d)) +* **ripple:** use element coordinates instead of page coordinates ([#7446](https://github.com/angular/material2/issues/7446)) ([7714a5c](https://github.com/angular/material2/commit/7714a5c)), closes [#7436](https://github.com/angular/material2/issues/7436) +* **select:** losing focus when selecting values through binding ([#7296](https://github.com/angular/material2/issues/7296)) ([86bea91](https://github.com/angular/material2/commit/86bea91)), closes [#7092](https://github.com/angular/material2/issues/7092) +* **select:** multiple change events emitted when changing options of a closed select ([#7232](https://github.com/angular/material2/issues/7232)) ([c7ab828](https://github.com/angular/material2/commit/c7ab828)), closes [#7227](https://github.com/angular/material2/issues/7227) +* **select:** prevent nbsp from getting butchered in AOT ([#7363](https://github.com/angular/material2/issues/7363)) ([2e71cac](https://github.com/angular/material2/commit/2e71cac)) +* **select:** theme not being transferred to the panel ([#7342](https://github.com/angular/material2/issues/7342)) ([6b70ca6](https://github.com/angular/material2/commit/6b70ca6)) +* **selection-list:** model not updated when option is selected programmatically ([#7334](https://github.com/angular/material2/issues/7334)) ([f40a7cc](https://github.com/angular/material2/commit/f40a7cc)), closes [#7318](https://github.com/angular/material2/issues/7318) +* **selection-list:** restore focus if active item is destroyed ([#7125](https://github.com/angular/material2/issues/7125)) ([e05f939](https://github.com/angular/material2/commit/e05f939)) +* **selection-list:** tabIndex should respect disabled state ([#7039](https://github.com/angular/material2/issues/7039)) ([c2a9516](https://github.com/angular/material2/commit/c2a9516)) +* **sidenav:** change content from md- to mat- ([#7307](https://github.com/angular/material2/issues/7307)) ([d05dcfa](https://github.com/angular/material2/commit/d05dcfa)) +* **slider:** change event is not being emitted ([#7278](https://github.com/angular/material2/issues/7278)) ([39543a3](https://github.com/angular/material2/commit/39543a3)), closes [#7207](https://github.com/angular/material2/issues/7207) +* **snack-bar:** positioned snack bar animation not starting off-screen ([#7453](https://github.com/angular/material2/issues/7453)) ([58d3bb8](https://github.com/angular/material2/commit/58d3bb8)) +* **snack-bar:** subsequent snack bars not opening; animation issues ([#7086](https://github.com/angular/material2/issues/7086)) ([8e77261](https://github.com/angular/material2/commit/8e77261)), closes [#7063](https://github.com/angular/material2/issues/7063) +* **sort:** fix incorrect conditional grouping ([#7427](https://github.com/angular/material2/issues/7427)) ([f5e916d](https://github.com/angular/material2/commit/f5e916d)) +* **sort:** style changes to fix IE ([#7375](https://github.com/angular/material2/issues/7375)) ([75f26e8](https://github.com/angular/material2/commit/75f26e8)) +* **sort:** throw error on invalid direction ([#7378](https://github.com/angular/material2/issues/7378)) ([cc6f39e](https://github.com/angular/material2/commit/cc6f39e)) +* **stepper:** align appearance with spec ([#7279](https://github.com/angular/material2/issues/7279)) ([4122ae2](https://github.com/angular/material2/commit/4122ae2)), closes [#7260](https://github.com/angular/material2/issues/7260) +* **stepper:** avoid blurry content on IE ([#6992](https://github.com/angular/material2/issues/6992)) ([6f48710](https://github.com/angular/material2/commit/6f48710)) +* **stepper:** selected is always undefined ([#7213](https://github.com/angular/material2/issues/7213)) ([217840c](https://github.com/angular/material2/commit/217840c)) +* **stepper:** switch to OnPush change detection ([#7119](https://github.com/angular/material2/issues/7119)) ([c2c6e04](https://github.com/angular/material2/commit/c2c6e04)) +* **stepper:** unable to internationalize labels ([#7122](https://github.com/angular/material2/issues/7122)) ([6e3bbcb](https://github.com/angular/material2/commit/6e3bbcb)) +* **tabs:** blurry content in IE ([#6954](https://github.com/angular/material2/issues/6954)) ([7a354a0](https://github.com/angular/material2/commit/7a354a0)), closes [#6944](https://github.com/angular/material2/issues/6944) +* **tabs:** update tab output names ([#7134](https://github.com/angular/material2/issues/7134)) ([38268d3](https://github.com/angular/material2/commit/38268d3)) +* **theming:** incorrect green-500 contrast color ([#7492](https://github.com/angular/material2/issues/7492)) ([c1f6ea1](https://github.com/angular/material2/commit/c1f6ea1)), closes [#7490](https://github.com/angular/material2/issues/7490) +* **tooltip:** ensure tooltip stays within viewport ([#6659](https://github.com/angular/material2/issues/6659)) ([c8ddd39](https://github.com/angular/material2/commit/c8ddd39)), closes [#5428](https://github.com/angular/material2/issues/5428) +* **tooltip:** minification issue ([#7430](https://github.com/angular/material2/issues/7430)) ([b121e32](https://github.com/angular/material2/commit/b121e32)) + + +# [2.0.0-beta.11 carapace-parapet](https://github.com/angular/material2/compare/2.0.0-beta.10...2.0.0-beta.11) (2017-09-21) + + +### Highlights +* Each `@angular/material` component is now bundled into its own javascript file. This will allow +tools like webpack to more easily load _only_ the components being used in an application. +* New stepper component! The base behavior lives in `@angular/cdk` with Material Design flavors in +`@angular/material`. + + +### Breaking changes + +* Angular Material now requires **Angular 4.4.3 or greater** +* `MaterialModule` has been removed. ([cf1ece0](https://github.com/angular/material2/commit/cf1ece0)) (#6803) +[See the deprecation notice from beta.3 for more information](https://github.com/angular/material2/blob/master/CHANGELOG.md#materialmodule). +* `MdCoreModule` has been removed. Most of its functionality has been moved to `@angular/cdk` over +the last few releases. +* `FocusOriginMonitor` has been renamed to `FocusMonitor` and moved to `@angular/cdk`. +* **chip-list:** The outputs `select` and `deselect` have been removed in favor of a single + `onSelectionChange` output. +* **overlay:** OverlayState has been renamed to OverlayConfig +* **overlay:** Now that the Overlay is part of the cdk rather than Angular Material directly, +the `themeClass` property has been removed. To add a class to the +overlay for theming, you can do +```ts +overlayContainer.getContainerElement().classList.add('my-theme-class'); +``` +* DateAdapter method `getISODateString` has been renamed to `toIso8601` and a new method +`fromIso8601` has been added. +* **sort:** The sort-change stream `mdSortChange` has been renamed to `sortChange`. + +### Deprecation of "md" prefix. + +In earlier betas, we've had a compatibility mode that allowed people to use either "md" or "mat" +as the selector for Angular Material components. This was created so that these components could +live side-by-side with [AngularJS Material](https://material.angularjs.org) without CSS from +the two libraries colliding. + +For beta.11, we've made the decision to deprecate the "md" prefix completely and use "mat" moving +forward. This affects all class names, properties, inputs, outputs, and selectors (CSS classes were +changed back in February). The "md" prefixes will be removed in the next beta release. + +[You can automatically update your projects with the angular-material-prefix-updater tool.](https://www.npmjs.com/package/angular-material-prefix-updater) +Check out the tool's page for instructions on how to run. + +#### Why are we doing this? +We like the "md" prefix too! We added compatibility mode in order to keep "md" around, but over +time we found that there were too many downsides to continue supporting both prefixes at the same +time: +* Many users found the fact that the CSS used "mat" while templates used "md" confusing. +* Users in compatibility mode found that having "mat" in their templates while TypeScript class +names remained "Md" to be unfriendly. +* Making both prefixes available consistently through templates required [adding many +getters/setters that aliased the "true" property](https://github.com/angular/material2/blob/1cfce8d9ab047d447465bd4e233fd26893830328/src/lib/tooltip/tooltip.ts#L171-L198). +This ends up increasing payload size and complexity of the source code. +* Compatiblity mode itself used [broad directive selectors](https://github.com/angular/material2/blob/87318bc7c83d249036837376609ea099e5aea2d9/src/lib/core/compatibility/compatibility.ts#L107-L187) +to enforce that only one prefix was used at a time. This causes a problem where this broad selector +prevents Angular from throwing an error if an application uses a component without importing its +`NgModule`. + +#### Why not change the styles in AngularJS Material? +We explored this option early on (before creating compatibility mode). We found that changing the +library's styles such that they wouldn't affect the Angular Material components would increase +the specificity. This would have been a significant breaking change, as it would have potentially +broken countless custom styles that relied on a particular specificity working. + +### Other deprecations +* `StyleModule` is deprecated. `FocusOriginMonitor` (the only thing it contained) has been renamed +to `FocusMonitor` and moved to `@angular/cdk/a11y` (`A11yModule`). + + +### Bug Fixes + +* **autocomplete,select:** inconsistent disabled option coloring ([#6640](https://github.com/angular/material2/issues/6640)) ([454781d](https://github.com/angular/material2/commit/454781d)), closes [#6638](https://github.com/angular/material2/issues/6638) +* **autosize:** not resizing on programmatic changes ([#6654](https://github.com/angular/material2/issues/6654)) ([89fea50](https://github.com/angular/material2/commit/89fea50)), closes [#5247](https://github.com/angular/material2/issues/5247) +* **button-toggle:** border radius ignored if option is selected ([#6699](https://github.com/angular/material2/issues/6699)) ([82e14f8](https://github.com/angular/material2/commit/82e14f8)), closes [#6689](https://github.com/angular/material2/issues/6689) +* **checkbox:** label content should not wrap ([#6674](https://github.com/angular/material2/issues/6674)) ([9acab86](https://github.com/angular/material2/commit/9acab86)), closes [#6671](https://github.com/angular/material2/issues/6671) +* **chips:** set appropriate aria-orientation ([#6464](https://github.com/angular/material2/issues/6464)) ([a37aa6a](https://github.com/angular/material2/commit/a37aa6a)) +* **datepicker:** allow date or datetime strings in fromIso8601 ([#7220](https://github.com/angular/material2/issues/7220)) ([8436f8c](https://github.com/angular/material2/commit/8436f8c)) +* **datepicker:** allow ISO 8601 strings as inputs ([#7091](https://github.com/angular/material2/issues/7091)) ([d2ceb2c](https://github.com/angular/material2/commit/d2ceb2c)) +* **datepicker:** backdrop class should be mat- ([#7056](https://github.com/angular/material2/issues/7056)) ([2b61eb6](https://github.com/angular/material2/commit/2b61eb6)) +* **datepicker:** Create a new injection token to avoid overriding LOCALE_ID ([#6708](https://github.com/angular/material2/issues/6708)) ([2635cad](https://github.com/angular/material2/commit/2635cad)) +* **datepicker:** fix wrong datepicker-input value for non MM/DD/YYYY locales ([#6798](https://github.com/angular/material2/issues/6798)) ([29399b8](https://github.com/angular/material2/commit/29399b8)) +* **datepicker:** makes sure the datepickerInput is registered ([#7049](https://github.com/angular/material2/issues/7049)) ([e4d48d7](https://github.com/angular/material2/commit/e4d48d7)) +* **datepicker:** toggle not reacting to disabled state changes in datepicker or input ([#6964](https://github.com/angular/material2/issues/6964)) ([85993d3](https://github.com/angular/material2/commit/85993d3)) +* **expansion-panel:** dark theme header hover color ([#6616](https://github.com/angular/material2/issues/6616)) ([21c68ad](https://github.com/angular/material2/commit/21c68ad)) +* **form-field:** add aria-owns to label element ([#6683](https://github.com/angular/material2/issues/6683)) ([4191b4d](https://github.com/angular/material2/commit/4191b4d)) +* **form-field:** placeholder not floating if autofilled ([#6839](https://github.com/angular/material2/issues/6839)) ([602a861](https://github.com/angular/material2/commit/602a861)), closes [#6837](https://github.com/angular/material2/issues/6837) +* **grid-list:** avoid unnecessary calc declarations ([#6745](https://github.com/angular/material2/issues/6745)) ([255611b](https://github.com/angular/material2/commit/255611b)) +* **grid-list:** styles not cleared when switching to a different styling mode ([#6660](https://github.com/angular/material2/issues/6660)) ([87d607e](https://github.com/angular/material2/commit/87d607e)), closes [#4047](https://github.com/angular/material2/issues/4047) +* **input:** remove resize handle from non-textarea inputs ([#6768](https://github.com/angular/material2/issues/6768)) ([1272f03](https://github.com/angular/material2/commit/1272f03)), closes [#6757](https://github.com/angular/material2/issues/6757) +* **list:** subheader margin being overwritten by typography ([#6735](https://github.com/angular/material2/issues/6735)) ([efe483a](https://github.com/angular/material2/commit/efe483a)) +* **menu:** multiple close events for a single close ([#6961](https://github.com/angular/material2/issues/6961)) ([1cccd4b](https://github.com/angular/material2/commit/1cccd4b)) +* **menu:** nested menu hover not working when trigger is added lazily ([#6807](https://github.com/angular/material2/issues/6807)) ([6b5100b](https://github.com/angular/material2/commit/6b5100b)), closes [#6731](https://github.com/angular/material2/issues/6731) +* **menu:** nested trigger staying highlighted after click ([#6853](https://github.com/angular/material2/issues/6853)) ([04bf3d1](https://github.com/angular/material2/commit/04bf3d1)), closes [#6838](https://github.com/angular/material2/issues/6838) +* **overlay:** rename OverlayState to OverlayConfig ([#6972](https://github.com/angular/material2/issues/6972)) ([1cfce8d](https://github.com/angular/material2/commit/1cfce8d)) +* **progress-bar:** query mode not reversing direction in rtl ([#6922](https://github.com/angular/material2/issues/6922)) ([8a21881](https://github.com/angular/material2/commit/8a21881)) +* **select:** extra whitespace around placeholder ([#6955](https://github.com/angular/material2/issues/6955)) ([9fe6386](https://github.com/angular/material2/commit/9fe6386)), closes [#6923](https://github.com/angular/material2/issues/6923) +* **selection-list:** do not coerece option value to boolean ([#6983](https://github.com/angular/material2/issues/6983)) ([dfe01f2](https://github.com/angular/material2/commit/dfe01f2)) +* **selection-list:** proper style for disabled options ([#6829](https://github.com/angular/material2/issues/6829)) ([547d11f](https://github.com/angular/material2/commit/547d11f)) +* **slide-toggle:** remove side-margin if slide-toggle label is empty ([#6881](https://github.com/angular/material2/issues/6881)) ([a1ec81a](https://github.com/angular/material2/commit/a1ec81a)), closes [#6868](https://github.com/angular/material2/issues/6868) +* **slide-toggle:** support native tabindex attribute ([#6613](https://github.com/angular/material2/issues/6613)) ([8f9f3c8](https://github.com/angular/material2/commit/8f9f3c8)) +* **slider:** thumb disappearing on disabled element with thumb label ([#6641](https://github.com/angular/material2/issues/6641)) ([8243b16](https://github.com/angular/material2/commit/8243b16)), closes [#6631](https://github.com/angular/material2/issues/6631) +* **slider:** update styles when focus and dir change ([#6700](https://github.com/angular/material2/issues/6700)) ([8c49422](https://github.com/angular/material2/commit/8c49422)) +* **slider, drawer:** unsubscribe from directionaly change subject ([#6907](https://github.com/angular/material2/issues/6907)) ([a7ce31e](https://github.com/angular/material2/commit/a7ce31e)), closes [#6892](https://github.com/angular/material2/issues/6892) [#6903](https://github.com/angular/material2/issues/6903) +* **snack-bar:** animation not starting for subsequent snack bars ([#6649](https://github.com/angular/material2/issues/6649)) ([730e7ae](https://github.com/angular/material2/commit/730e7ae)), closes [#6222](https://github.com/angular/material2/issues/6222) +* **sort:** reverse directions and better animation ([#6802](https://github.com/angular/material2/issues/6802)) ([6fa9e6f](https://github.com/angular/material2/commit/6fa9e6f)) +* **table:** gracefully handle undefined/null columns ([#6862](https://github.com/angular/material2/issues/6862)) ([3ddf65b](https://github.com/angular/material2/commit/3ddf65b)) +* **tabs:** fix infinite tab loop ([#6663](https://github.com/angular/material2/issues/6663)) ([67e02b0](https://github.com/angular/material2/commit/67e02b0)), closes [#4639](https://github.com/angular/material2/issues/4639) +* **tabs:** tab spacing on desktop incorrect ([#6681](https://github.com/angular/material2/issues/6681)) ([b678119](https://github.com/angular/material2/commit/b678119)), closes [#3347](https://github.com/angular/material2/issues/3347) +* **tooltip:** closing immediately when triggered on click ([#6590](https://github.com/angular/material2/issues/6590)) ([bcd026f](https://github.com/angular/material2/commit/bcd026f)) +* **tooltip:** ensure tooltip never passes undefined message to ([#7018](https://github.com/angular/material2/issues/7018)) ([f6d1078](https://github.com/angular/material2/commit/f6d1078)) +* add `mat` exportAs and class aliases ([#7106](https://github.com/angular/material2/issues/7106)) ([a96b545](https://github.com/angular/material2/commit/a96b545)) +* **tooltip:** error on trigger escape presses while closed ([#7028](https://github.com/angular/material2/issues/7028)) ([dcf3b27](https://github.com/angular/material2/commit/dcf3b27)), closes [#7009](https://github.com/angular/material2/issues/7009) + +### Features + +* **chip-list:** implement FormFieldControl and ControlValueAccessor ([#6686](https://github.com/angular/material2/issues/6686)) ([7a42706](https://github.com/angular/material2/commit/7a42706)) +* **datepicker:** Add Moment.js adapter ([#6860](https://github.com/angular/material2/issues/6860)) ([9545427](https://github.com/angular/material2/commit/9545427)) +* **dialog:** add afterOpen to MdDialogRef ([#6887](https://github.com/angular/material2/issues/6887)) ([27cbe47](https://github.com/angular/material2/commit/27cbe47)) +* **expansion-panel:** allow for the panel header height to be customized ([#6643](https://github.com/angular/material2/issues/6643)) ([11e2239](https://github.com/angular/material2/commit/11e2239)), closes [#5641](https://github.com/angular/material2/issues/5641) +* **overlay:** replace OverlayContainer themeClass w/ addClass/removeClass methods ([#6975](https://github.com/angular/material2/issues/6975)) ([a944f6e](https://github.com/angular/material2/commit/a944f6e)) +* **selection-list:** add selectAll and deselectAll functions ([#6971](https://github.com/angular/material2/issues/6971)) ([dc9679d](https://github.com/angular/material2/commit/dc9679d)), closes [#6969](https://github.com/angular/material2/issues/6969) +* **sort:** add sorting indicator animation ([#5831](https://github.com/angular/material2/issues/5831)) ([70bd5fc](https://github.com/angular/material2/commit/70bd5fc)) +* **stepper:** Add e2e test ([#6776](https://github.com/angular/material2/issues/6776)) ([bef6271](https://github.com/angular/material2/commit/bef6271)) +* **stepper:** add moduleId to components ([#6780](https://github.com/angular/material2/issues/6780)) ([f375f92](https://github.com/angular/material2/commit/f375f92)) +* **stepper:** Address previous comments + add directionality support ([#6775](https://github.com/angular/material2/issues/6775)) ([c396596](https://github.com/angular/material2/commit/c396596)) +* **stepper:** initial version of stepper ([#6594](https://github.com/angular/material2/issues/6594)) ([87318bc](https://github.com/angular/material2/commit/87318bc)) +* **viewport-ruler:** add common window resize handler ([#6680](https://github.com/angular/material2/issues/6680)) ([881630f](https://github.com/angular/material2/commit/881630f)) +* add `preserveWhitespaces: false` to all components ([#7115](https://github.com/angular/material2/issues/7115)) ([2b0315d](https://github.com/angular/material2/commit/2b0315d)) +* move FocusMonitor into cdk ([#6921](https://github.com/angular/material2/issues/6921)) ([6cfe5c4](https://github.com/angular/material2/commit/6cfe5c4)) + + +### Performance Improvements + +* **dialog:** avoid repaintin dialog content element on scroll ([#6890](https://github.com/angular/material2/issues/6890)) ([51396d0](https://github.com/angular/material2/commit/51396d0)), closes [#6878](https://github.com/angular/material2/issues/6878) +* memory leak when subscribing to zone events ([#6918](https://github.com/angular/material2/issues/6918)) ([f6c9172](https://github.com/angular/material2/commit/f6c9172)), closes [#6905](https://github.com/angular/material2/issues/6905) + + # [2.0.0-beta.10 découpage-panjandrum](https://github.com/angular/material2/compare/2.0.0-beta.8...2.0.0-beta.10) (2017-08-29) @@ -37,15 +536,15 @@ The current set of public `@angular/cdk` subpackages are: a11y, bidi, coercion, collections, keycodes, observers, overlay, platform, portal, rxjs, scrolling, table. -If you are using SystemJS, each package you use must be added to the SystemJS configuration. +If you are using SystemJS, each package you use must be added to the SystemJS configuration. * All `Overlay` code has been moved from `@angular/material` to `@angular/cdk`. The symbols are still re-exported through `@angular/material`, but these re-exports will be removed in a subsequent release. * `cdkScrollable`, `ScrollDispatcher`, and `ViewportRuler` have been moved from overlay into its -own `scrolling` subpackage in `@angular/cdk`. +own `scrolling` subpackage in `@angular/cdk`. * **input:** Inputs have a width of `200px` by default (similar to native input elements). The width can be overridden by via the `mat-form-field` css class. -* **input:** CSS classes have changed from `mat-input-container-` to `mat-form-field-`. +* **input:** CSS classes have changed from `mat-input-container-` to `mat-form-field-`. * **input:** `md-prefix` and `md-suffix` are now `mdPrefix` and `mdSuffix`. * **portal:** `TemplatePortal` now requires a generic type (C) to align with `TemplateRef`. This will usually be `any`. @@ -529,7 +1028,7 @@ a few bugs, but things should mostly work. * `MdIconModule` no longer imports `HttpModule`. If your application depended on `Http` being provided through `MdIconModule`, you should now directly import `HttpModule` into your application. * The `forRoot` method on all Angular Material modules has been removed. It was previously deprecated and a no-op. Importing the modules directly will have the same effect. -* Angular Material now requires TypeScript 2.2, which adds support for mixins. +* Angular Material now requires TypeScript 2.2, which adds support for mixins. ### Highlights @@ -684,7 +1183,7 @@ need to explicitly import `BrowserAnimationsModule` (or `NoopAnimationsModule`) `@angular/platform-browser/animations` as well as installing `@angular/animations`. #### Other changes -* The `DomProjection` service was removed. This was an experimental, undocumented service that we +* The `DomProjection` service was removed. This was an experimental, undocumented service that we ultimately found did not provide a good approach to composing components. * The `config` property was removed from `MdDialogRef`. If you were using this to access the `data` property, you can instead inject that value using the `MD_DIALOG_DATA` of the opened component. @@ -1009,16 +1508,16 @@ as trusted using Angular's `DomSanitizer` service. allows for direct access to the native input element. * All `@Input` properties have been changed to use their camelCase names for binding. The old names are still available as deprecated but will be removed in the next release. -* All `@Directive` selectors are now camelCase to be consistent with Angular core. For example, -`[md-tooltip]` is now `[mdTooltip]` The old selectors are still available as deprecated but will +* All `@Directive` selectors are now camelCase to be consistent with Angular core. For example, +`[md-tooltip]` is now `[mdTooltip]` The old selectors are still available as deprecated but will be removed in the next release. * `` has been renamed to ``. The old selector and symbols are still available as deprecated but will be removed in the next release. -* `` has been renamed to ``. The old selector is still +* `` has been renamed to ``. The old selector is still available as deprecated but will be removed in the next release. -* Several components in `core/`, such as Overlay, have had their prefix changed to `cdk-` (short -for "component dev kit"). This signifies that these are general-purpose tools for building -components that are not coupled to Material Design.The old selectors are still +* Several components in `core/`, such as Overlay, have had their prefix changed to `cdk-` (short +for "component dev kit"). This signifies that these are general-purpose tools for building +components that are not coupled to Material Design.The old selectors are still available as deprecated but will be removed in the next release. The CSS classes have been changed. * The `align` property for `md-checkbox` and `md-radio-button` has been changed to `labelPosition` with values `before` and `after`. @@ -1316,12 +1815,12 @@ In addition to this, each component now has an `index.js` file, so you should no import {MdButtonModule} from '@angular2-material/button' ``` -Instead of +Instead of ```ts import {MdButtonModule} from '@angular2-material/button/button' ``` -* all: material modules must be included with `forRoot()` when bootstrapping. See the [ngModules guide](https://angular.io/docs/ts/latest/cookbook/ngmodule-faq.html#!#q-for-root) for +* all: material modules must be included with `forRoot()` when bootstrapping. See the [ngModules guide](https://angular.io/docs/ts/latest/cookbook/ngmodule-faq.html#!#q-for-root) for more information. ```ts @@ -1337,7 +1836,7 @@ more information. * all: material now depends on TypeScript 2.0 -* input: `md-input` attributes now match the casing of native attributes. Previously they were camel-cased; now they are all lowercase. +* input: `md-input` attributes now match the casing of native attributes. Previously they were camel-cased; now they are all lowercase. Example: `autoComplete` is now `autocomplete`. See [#1066](https://github.com/angular/material2/pull/1066) for a full list. * overlay: overlays are now synchronous. This means actions like creating an overlay no longer return a promise. @@ -1400,7 +1899,7 @@ The MD_XXX_DIRECTIVES constants are now deprecated and will be removed in alpha. * **md-menu** initial version for md-menu (more features coming in alpha.8) ([#893](https://github.com/angular/material2/issues/893)) ([16eb6be](https://github.com/angular/material2/commit/16eb6be)) ([#855](https://github.com/angular/material2/issues/855)) ([e324e47](https://github.com/angular/material2/commit/e324e47)) ([#867](https://github.com/angular/material2/issues/867)) ([9a32489](https://github.com/angular/material2/commit/9a32489)) * **ripple:** initial mdInkRipple implementation ([#681](https://github.com/angular/material2/issues/681)) ([47448cb](https://github.com/angular/material2/commit/47448cb)) * **button:** add ripple to md-button ([32aa461](https://github.com/angular/material2/commit/32aa461)) -* **input:** support autocapitalize and autocorrect attributes ([#858](https://github.com/angular/material2/issues/858)) ([b2471f2](https://github.com/angular/material2/commit/b2471f2)) +* **input:** support autocapitalize and autocorrect attributes ([#858](https://github.com/angular/material2/issues/858)) ([b2471f2](https://github.com/angular/material2/commit/b2471f2)) * **slide-toggle:** add drag functionality to thumb ([#750](https://github.com/angular/material2/issues/750)) ([25b4f21](https://github.com/angular/material2/commit/25b4f21)) @@ -1423,7 +1922,7 @@ The MD_XXX_DIRECTIVES constants are now deprecated and will be removed in alpha. ### Breaking changes * `MdRadioDispatcher` is now `MdUniqueSelectionDispatcher` and is imported from `core` -* Form controls use the new `@angular/forms` package. To make migration easier, you can +* Form controls use the new `@angular/forms` package. To make migration easier, you can alternatively install alpha.5-3, which is the same as alpha.6 without the new forms package. [Please see the docs for the new forms module](https://angular.io/docs/ts/latest/guide/forms.html). @@ -1619,10 +2118,10 @@ This inaugural release includes 6 components: * [md-card](src/components/card) * [md-toolbar](src/components/toolbar) * [md-sidenav](src/components/sidenav) -* [md-checkbox](src/components/checkbox) +* [md-checkbox](src/components/checkbox) * [md-progress-circle and md-spinner](src/components/progress-circle) As the alpha process continues, these components will continue to evolve. There *will* be breaking changes between alpha releases; the alpha releases are here for people that want an -early look or who like to live on the edge and are very tolerant of breaking API and behavior +early look or who like to live on the edge and are very tolerant of breaking API and behavior changes. diff --git a/CODING_STANDARDS.md b/CODING_STANDARDS.md index 1e37d6f3558f..e0b9da487df4 100644 --- a/CODING_STANDARDS.md +++ b/CODING_STANDARDS.md @@ -43,20 +43,22 @@ use `//` style comments for everything else (explanations, background info, etc. In SCSS code, always use `//` style comments. +In HTML code, use `` comments, which will be stripped when packaging a build. + #### Prefer more focused, granular components vs. complex, configurable components. For example, rather than doing this: ```html -Basic button -FAB -pony +Basic button +FAB +pony ``` do this: ```html -Basic button -FAB -pony +Basic button +FAB +pony ``` #### Prefer small, focused modules @@ -125,9 +127,9 @@ class ConfigBuilder { ``` #### RxJS -When dealing with RxJS operators, import the operator functions directly (e.g. -`import "rxjs/operator/map"`), as opposed to using the "patch" imports which pollute the user's -global Observable object (e.g. `import "rxjs/add/operator/map"`): +When dealing with RxJS operators, import the lettable operator (e.g. +`import {map} from 'rxjs/operators/map'`), as opposed to using the "patch" imports which pollute the +user's global Observable object (e.g. `import 'rxjs/add/operator/map'`): ```ts // NO @@ -135,23 +137,10 @@ import 'rxjs/add/operator/map'; someObservable.map(...).subscribe(...); // YES -import {map} from 'rxjs/operator/map'; -map.call(someObservable, ...).subscribe(...); +import {map} from 'rxjs/operators/map'; +someObservable.pipe(map(...)).subscribe(...); ``` -Note that this approach can be inflexible when dealing with long chains of operators. You can use -the `RxChain` class to help with it: - -```ts -// Before -someObservable.filter(...).map(...).do(...); - -// After -RxChain.from(someObservable).call(filter, ...).call(map, ...).call(do, ...).subscribe(...); -``` -Note that not all operators are available via the `RxChain`. If the operator that you need isn't -declared, you can add it to `/core/rxjs/rx-operators.ts`. - #### Access modifiers * Omit the `public` keyword as it is the default behavior. * Use `private` when appropriate and possible, prefixing the name with an underscore. @@ -165,6 +154,24 @@ be part of the user-facing API. This typically applies to symbols used in templa Additionally, the `@docs-private` JsDoc annotation can be used to hide any symbol from the public API docs. + +#### Getters and Setters +* Avoid long or complex getters and setters. If the logic of an accessor would take more than +three lines, introduce a new method to contain the logic. +* A getter should immediately precede its corresponding setter. +* Decorators such as `@Input` should be applied to the getter and not the setter. +* Always use a `readonly` property instead of a getter (with no setter) when possible. + ```ts + /** YES */ + readonly active: boolean; + + /** NO */ + get active(): boolean { + // Using a getter solely to make the property read-only. + return this._active; + } + ``` + #### JsDoc comments All public APIs must have user-facing comments. These are extracted and shown in the documentation @@ -189,7 +196,7 @@ and the return value: * @param config Dialog configuration options. * @returns Reference to the newly-opened dialog. */ - open(component: ComponentType, config?: MdDialogConfig): MdDialogRef { ... } + open(component: ComponentType, config?: MatDialogConfig): MatDialogRef { ... } ``` Boolean properties and return values should use "Whether..." as opposed to "True if...": @@ -198,6 +205,13 @@ Boolean properties and return values should use "Whether..." as opposed to "True disabled: boolean = false; ``` +#### Try-Catch + +Avoid `try-catch` blocks, instead preferring to prevent an error from being thrown in the first +place. When impossible to avoid, the `try-catch` block must include a comment that explains the +specific error being caught and why it cannot be prevented. + + #### Naming ##### General @@ -220,8 +234,9 @@ class UniqueSelectionDispatcher { } Avoid suffixing a class with "Service", as it communicates nothing about what the class does. Try to think of the class name as a person's job title. -Classes that correspond to a directive with an `md-` prefix should also be prefixed with `Md`. -CDK classes should not have a prefix. +Classes that correspond to a directive with an `mat-` prefix should also be prefixed with `Mat`. +CDK classes should only have a `Cdk` prefix when the class is a directive with a `cdk` selector +prefix. ##### Methods The name of a method should capture the action that is performed *by* that method rather than @@ -242,7 +257,8 @@ activateRipple() { #### Inheritance Avoid using inheritance to apply reusable behaviors to multiple components. This limits how many -behaviors can be composed. +behaviors can be composed. Instead, [TypeScript mixins][ts-mixins] can be used to compose multiple +common behaviors into a single component. ### Angular @@ -346,3 +362,5 @@ When it is not super obvious, include a brief description of what a class repres // Portion of the floating panel that sits, invisibly, on top of the input. .mat-datepicker-input-mask { } ``` + +[ts-mixins]: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-2.html#support-for-mix-in-classes diff --git a/LICENSE b/LICENSE index 47bfda24adf2..113bbc0ba162 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License -Copyright (c) 2017 Google, Inc. +Copyright (c) 2017 Google LLC. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 838c2238314d..945d40bfe869 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,11 @@ This is the home for the Angular team's Material Design components built for and with Angular. #### Quick links -[Documentation, demos, and guides][aio] | +[Documentation, demos, and guides][aio] | [Google group](https://groups.google.com/forum/#!forum/angular-material2) | [Contributing](https://github.com/angular/material2/blob/master/CONTRIBUTING.md) | -[Plunker Template](http://plnkr.co/edit/o077B6uEiiIgkC0S06dd?p=preview) +[Plunker Template](https://goo.gl/uDmqyY) | +[StackBlitz Template](https://goo.gl/wwnhMV) ### Getting started @@ -29,17 +30,16 @@ and which pieces are blocked) and make a comment. Also see our [`Good for community contribution`](https://github.com/angular/material2/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+for+community+contribution%22) label. -High level stuff planned for Q3 2017 (July - September): -* As many bug fixes as humanly possible -* Additional features for data-table -* Continued accessibility improvements -* Nested menus -* All components are OnPush -* Improved documentation, guides, examples, and navigation on [material.angular.io][aio] -* Improve library-wide API consistency -* Stepper component -* Sticky header helper -* action-list and selection-list +High level stuff planned for Q4 2017 (October - December): +* RC and stable release +* Research and prototyping for virtual-scroll +* Research and exploration for data visualization +* cdkTree and matTree +* Expanded cdkTable features +* cdk/svg +* cdk/dialog +* Switch build to bazel +* Overlay positioning improvements #### Available features @@ -55,12 +55,12 @@ High level stuff planned for Q3 2017 (July - September): | data-table | Sticky headers & incremental row rendering in-progress | [Docs][28] | | datepicker | | [Docs][25] | | dialog | | [Docs][22] | -| expansion-panel | Needs documentation | - | +| expansion-panel | | [Docs][32] | | grid-list | | [Docs][9] | | icon | | [Docs][10] | | input | | [Docs][5] | | list | Selection and action list planned Q3 | [Docs][8] | -| menu | Nested menu planned Q3 | [Docs][17] | +| menu | | [Docs][17] | | paginator | | [Docs][29] | | progress-bar | | [Docs][12] | | progress-spinner | | [Docs][11] | @@ -72,15 +72,16 @@ High level stuff planned for Q3 2017 (July - September): | slider | | [Docs][16] | | snackbar / toast | | [Docs][21] | | sort-header | | [Docs][30] | +| stepper | | [Docs][33] | | tabs | | [Docs][13] | | textarea | | [Docs][5] | | toolbar | | [Docs][7] | | tooltip | | [Docs][18] | -| ---------------- | ------------------------------------------------------ | ------------ | +| ---------------- | ------------------------------------------------------ | ------------ | | theming | | [Guide][20] | | typography | | [Guide][27] | | layout | See [angular/flex-layout][lay_rp] | [Wiki][0] | -| cdk | Launched, documentation in-progress | | +| cdk | | [Docs][34] | #### In progress, planned, and non-planned features @@ -88,12 +89,11 @@ High level stuff planned for Q3 2017 (July - September): | Feature | Status | Docs | Issue | |------------------|-------------------------------------|--------------|----------------| | tree | In-progress ([sneak peek][31]) | - | [#3175][3175] | -| stepper | In-progress, planned Q3 2017 | - | [#508][0508] | | sticky-header | In-progress, planned Q3 2017 | - | [#474][0474] | | virtual-repeat | Not started, planned Q4 2017 | - | [#823][0823] | | fab speed-dial | Not started, not planned | - | [#860][0860] | | fab toolbar | Not started, not planned | - | - | -| bottom-sheet | Not started, not planned | - | - | +| bottom-sheet | Not started, not planned | - | [#8113][8113] | | bottom-nav | Not started, not planned | - | [#408][0408] | [0]: https://github.com/angular/flex-layout/wiki @@ -115,7 +115,7 @@ High level stuff planned for Q3 2017 (July - September): [16]: https://material.angular.io/components/component/slider [17]: https://material.angular.io/components/component/menu [18]: https://material.angular.io/components/component/tooltip -[19]: https://github.com/angular/material2/blob/master/src/lib/core/ripple/README.md +[19]: https://github.com/angular/material2/blob/master/src/lib/core/ripple/ripple.md [20]: https://material.angular.io/guide/theming [21]: https://material.angular.io/components/component/snack-bar [22]: https://material.angular.io/components/component/dialog @@ -128,6 +128,9 @@ High level stuff planned for Q3 2017 (July - September): [29]: https://material.angular.io/components/component/paginator [30]: https://material.angular.io/components/component/sort [31]: https://tina-material-tree.firebaseapp.com/simple-tree +[32]: https://material.angular.io/components/expansion/overview +[33]: https://material.angular.io/components/stepper/overview +[34]: https://material.angular.io/cdk/categories [0107]: https://github.com/angular/material2/issues/107 [0119]: https://github.com/angular/material2/issues/119 @@ -149,11 +152,11 @@ High level stuff planned for Q3 2017 (July - September): [4191]: https://github.com/angular/material2/pull/4191 [0995]: https://github.com/angular/material2/pull/995 [0474]: https://github.com/angular/material2/pull/474 +[8113]: https://github.com/angular/material2/issues/8113 [aio]: https://material.angular.io -[getting-started]: https://github.com/angular/material2/blob/master/guides/getting-started.md +[getting-started]: https://material.angular.io/guide/getting-started [lay_rp]: https://github.com/angular/flex-layout -[theming]: https://github.com/angular/material2/blob/master/guides/theming.md ## The goal of Angular Material diff --git a/WORKSPACE b/WORKSPACE new file mode 100644 index 000000000000..f3e0c392ef01 --- /dev/null +++ b/WORKSPACE @@ -0,0 +1,36 @@ +workspace(name = "angular_material_src") + +# Add nodejs rules +git_repository( + name = "build_bazel_rules_nodejs", + remote = "https://github.com/bazelbuild/rules_nodejs.git", + # TODO(jelbourn): use the correct tag here. + commit = "31d36ff2acdf630d1e331f38006cf1a5d303d338", +) + +# NOTE: this rule installs nodejs, npm, and yarn, but does NOT install +# your npm dependencies. You must still run the package manager. +load("@build_bazel_rules_nodejs//:defs.bzl", "node_repositories") +node_repositories(package_json = ["//:package.json"]) + +# Add sass rules +git_repository( + name = "io_bazel_rules_sass", + remote = "https://github.com/bazelbuild/rules_sass.git", + tag = "0.0.2", +) + +load("@io_bazel_rules_sass//sass:sass.bzl", "sass_repositories") +sass_repositories() + +# Add TypeScript rules +local_repository( + name = "build_bazel_rules_typescript", + path = "node_modules/@bazel/typescript", +) + +# Add Angular rules +local_repository( + name = "angular", + path = "node_modules/@angular/bazel", +) diff --git a/angular.tsconfig.json b/angular.tsconfig.json new file mode 100644 index 000000000000..214ab3b67a11 --- /dev/null +++ b/angular.tsconfig.json @@ -0,0 +1,20 @@ +// WORKAROUND https://github.com/angular/angular/issues/18810 +// This file is required to run ngc on angular libraries, to write files like +// node_modules/@angular/core/core.ngsummary.json +{ + "compilerOptions": { + "lib": [ + "dom", + "es2015" + ], + "experimentalDecorators": true, + "types": [] + }, + "include": [ + "node_modules/@angular/**/*" + ], + "exclude": [ + "node_modules/@angular/bazel/**", + "node_modules/@angular/compiler-cli/**" + ] +} diff --git a/build-config.js b/build-config.js index 26b3f8199bc7..e47b125d381b 100644 --- a/build-config.js +++ b/build-config.js @@ -4,13 +4,18 @@ */ const {join} = require('path'); +const package = require('./package.json'); + /** Current version of the project*/ -const buildVersion = require('./package.json').version; +const buildVersion = package.version; + +/** Required Angular version for the project. */ +const angularVersion = package.dependencies['@angular/core']; /** License that will be placed inside of all created bundles. */ const buildLicense = `/** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -18,6 +23,7 @@ const buildLicense = `/** module.exports = { projectVersion: buildVersion, + angularVersion: angularVersion, projectDir: __dirname, packagesDir: join(__dirname, 'src'), outputDir: join(__dirname, 'dist'), diff --git a/e2e/components/button-e2e.spec.ts b/e2e/components/button-e2e.spec.ts index db275e50595c..7ec6164b6d33 100644 --- a/e2e/components/button-e2e.spec.ts +++ b/e2e/components/button-e2e.spec.ts @@ -8,7 +8,7 @@ describe('button', () => { it('should prevent click handlers from executing when disabled', async () => { element(by.id('test-button')).click(); - expect(element(by.id('click-counter')).getText()).toEqual('1'); + expect(await element(by.id('click-counter')).getText()).toEqual('1'); await browser.wait(ExpectedConditions.not( ExpectedConditions.presenceOf(element(by.css('div.mat-ripple-element'))))); @@ -16,7 +16,7 @@ describe('button', () => { element(by.id('disable-toggle')).click(); element(by.id('test-button')).click(); - expect(element(by.id('click-counter')).getText()).toEqual('1'); + expect(await element(by.id('click-counter')).getText()).toEqual('1'); await browser.wait(ExpectedConditions.not( ExpectedConditions.presenceOf(element(by.css('div.mat-ripple-element'))))); diff --git a/e2e/components/button-toggle-e2e.spec.ts b/e2e/components/button-toggle-e2e.spec.ts index 8f8b1b3e60c0..b302ee63b556 100644 --- a/e2e/components/button-toggle-e2e.spec.ts +++ b/e2e/components/button-toggle-e2e.spec.ts @@ -6,7 +6,7 @@ describe('button-toggle', () => { beforeEach(() => browser.get('/button-toggle')); it('should show a button-toggle', async () => { - expect(element(by.tagName('md-button-toggle'))).toBeDefined(); + expect(element(by.tagName('mat-button-toggle'))).toBeDefined(); screenshot(); }); diff --git a/e2e/components/card-e2e.spec.ts b/e2e/components/card-e2e.spec.ts index aac7d38abd39..14f0cd6e3b6b 100644 --- a/e2e/components/card-e2e.spec.ts +++ b/e2e/components/card-e2e.spec.ts @@ -1,12 +1,12 @@ import {browser, by, element} from 'protractor'; import {screenshot} from '../screenshot'; -describe('md-card', () => { +describe('mat-card', () => { beforeEach(() => browser.get('/cards')); it('should show a card', async () => { - const card = element(by.tagName('md-card')); + const card = element(by.tagName('mat-card')); expect(card).toBeDefined(); screenshot('fancy card example'); diff --git a/e2e/components/dialog-e2e.spec.ts b/e2e/components/dialog-e2e.spec.ts index d3887c7e0649..b5a9b3ab0a45 100644 --- a/e2e/components/dialog-e2e.spec.ts +++ b/e2e/components/dialog-e2e.spec.ts @@ -14,7 +14,7 @@ describe('dialog', () => { it('should open a dialog', () => { element(by.id('default')).click(); - expectToExist('md-dialog-container'); + expectToExist('mat-dialog-container'); screenshot('simple dialog opened'); }); @@ -29,7 +29,7 @@ describe('dialog', () => { await waitForDialog(); clickOnBackrop(); - expectToExist('md-dialog-container', false); + expectToExist('mat-dialog-container', false); }); it('should close by pressing escape', async () => { @@ -37,7 +37,7 @@ describe('dialog', () => { await waitForDialog(); pressKeys(Key.ESCAPE); - expectToExist('md-dialog-container', false); + expectToExist('mat-dialog-container', false); }); it('should close by pressing escape when the first tabbable element has lost focus', @@ -45,9 +45,9 @@ describe('dialog', () => { element(by.id('default')).click(); await waitForDialog(); - clickElementAtPoint('md-dialog-container', { x: 0, y: 0 }); + clickElementAtPoint('mat-dialog-container', { x: 0, y: 0 }); pressKeys(Key.ESCAPE); - expectToExist('md-dialog-container', false); + expectToExist('mat-dialog-container', false); }); it('should close by clicking on the "close" button', async () => { @@ -55,14 +55,14 @@ describe('dialog', () => { await waitForDialog(); element(by.id('close')).click(); - expectToExist('md-dialog-container', false); + expectToExist('mat-dialog-container', false); }); it('should focus the first focusable element', async () => { element(by.id('default')).click(); await waitForDialog(); - expectFocusOn('md-dialog-container input'); + expectFocusOn('mat-dialog-container input'); }); it('should restore focus to the element that opened the dialog', async () => { @@ -88,7 +88,7 @@ describe('dialog', () => { await waitForDialog(); clickOnBackrop(); - expectToExist('md-dialog-container'); + expectToExist('mat-dialog-container'); }); it('should be able to prevent closing by pressing escape', async () => { @@ -96,11 +96,11 @@ describe('dialog', () => { await waitForDialog(); pressKeys(Key.ESCAPE); - expectToExist('md-dialog-container'); + expectToExist('mat-dialog-container'); }); function waitForDialog() { - return waitForElement('md-dialog-container'); + return waitForElement('mat-dialog-container'); } function clickOnBackrop() { diff --git a/e2e/components/expansion-e2e.spec.ts b/e2e/components/expansion-e2e.spec.ts index 944c4e46653b..4fc3427e13b7 100644 --- a/e2e/components/expansion-e2e.spec.ts +++ b/e2e/components/expansion-e2e.spec.ts @@ -5,21 +5,40 @@ describe('expansion', () => { beforeEach(() => browser.get('/expansion')); - it('should show a expansion panel', async () => { - expect(element(by.css('.mat-expansion-panel'))).toBeDefined(); + it('should show an accordion', async () => { + expect(element(by.css('.mat-accordion'))).toBeDefined(); + screenshot(); + }); + it('should show two panels', async () => { + expect(await element.all(by.css('.mat-expansion-panel')).count()).toBe(2); screenshot(); }); it('should hide contents of the expansion panel on click', async () => { - const panelHeader = element(by.css('.mat-expansion-panel-header')); - const panelContent = element(by.css('.mat-expansion-panel-content')); + const panelHeader = element.all(by.css('.mat-expansion-panel-header')).get(0); + const panelContent = element.all(by.css('.mat-expansion-panel-content')).get(0); - expect(panelContent.isDisplayed()).toBe(false); + expect(await panelContent.isDisplayed()).toBe(false); panelHeader.click(); - expect(panelContent.isDisplayed()).toBe(true); + expect(await panelContent.isDisplayed()).toBe(true); + }); + + it('should emit events for expanding and collapsing', async () => { + const panelHeader = element.all(by.css('.mat-expansion-panel-header')).get(1); + const panelDescription = element + .all(by.css('.mat-expansion-panel-header mat-panel-description')).get(1); + + panelHeader.click(); + + expect(panelDescription.getText()).toContain('Currently I am open'); + + panelHeader.click(); + + expect(panelDescription.getText()).toContain('Currently I am closed'); }); }); + diff --git a/e2e/components/fullscreen-e2e.spec.ts b/e2e/components/fullscreen-e2e.spec.ts index a9f4a8535c65..4404f484537b 100644 --- a/e2e/components/fullscreen-e2e.spec.ts +++ b/e2e/components/fullscreen-e2e.spec.ts @@ -4,36 +4,36 @@ describe('fullscreen', () => { beforeEach(() => browser.get('/fullscreen')); - it('should open a dialog inside a fullscreen element and move it to the document body', () => { + it('should open a dialog inside a fullscreen element and move it to the body', async () => { element(by.id('fullscreen-open')).click(); element(by.id('dialog-open')).click(); - expectOverlayInFullscreen(); + await expectOverlayInFullscreen(); element(by.id('dialog-fullscreen-exit')).click(); - expectOverlayInBody(); + await expectOverlayInBody(); }); - it('should open a dialog inside the document body and move it to a fullscreen element', () => { + it('should open a dialog inside the body and move it to a fullscreen element', async () => { element(by.id('dialog-open')).click(); - expectOverlayInBody(); + await expectOverlayInBody(); element(by.id('dialog-fullscreen-open')).click(); - expectOverlayInFullscreen(); + await expectOverlayInFullscreen(); element(by.id('dialog-fullscreen-exit')).click(); - expectOverlayInBody(); + await expectOverlayInBody(); }); /** Expects the overlay container to be inside of the body element. */ - function expectOverlayInBody() { - expect(browser.isElementPresent(by.css('body > .cdk-overlay-container'))) + async function expectOverlayInBody() { + expect(await browser.isElementPresent(by.css('body > .cdk-overlay-container'))) .toBe(true, 'Expected the overlay container to be inside of the body.'); } /** Expects the overlay container to be in fullscreen mode. */ - function expectOverlayInFullscreen() { - expect(browser.isElementPresent(by.css('#fullscreen-pane > .cdk-overlay-container'))) + async function expectOverlayInFullscreen() { + expect(await browser.isElementPresent(by.css('#fullscreen-pane > .cdk-overlay-container'))) .toBe(true, 'Expected the overlay container to be in fullscreen mode.'); } diff --git a/e2e/components/grid-list-e2e.spec.ts b/e2e/components/grid-list-e2e.spec.ts index a70c8176c8ee..d4f0858099fa 100644 --- a/e2e/components/grid-list-e2e.spec.ts +++ b/e2e/components/grid-list-e2e.spec.ts @@ -6,11 +6,11 @@ describe('grid-list', () => { beforeEach(() => browser.get('/grid-list')); it('should render a grid list container', () => { - expectToExist('md-grid-list'); + expectToExist('mat-grid-list'); screenshot(); }); it('should render list items inside the grid list container', () => { - expectToExist('md-grid-list md-grid-tile'); + expectToExist('mat-grid-list mat-grid-tile'); }); }); diff --git a/e2e/components/icon-e2e.spec.ts b/e2e/components/icon-e2e.spec.ts index 0c17ac1ff1f2..f0261f5d2d85 100644 --- a/e2e/components/icon-e2e.spec.ts +++ b/e2e/components/icon-e2e.spec.ts @@ -13,7 +13,7 @@ describe('icon', () => { it('should have the correct class when used', async () => { const attr = await testIcon.getAttribute('class'); - expect(attr).toContain('md-24'); + expect(attr).toContain('custom-class'); expect(attr).toContain('material-icons'); }); diff --git a/e2e/components/input-e2e.spec.ts b/e2e/components/input-e2e.spec.ts index 9fcd587ecf02..6b860d896929 100644 --- a/e2e/components/input-e2e.spec.ts +++ b/e2e/components/input-e2e.spec.ts @@ -12,20 +12,20 @@ describe('input', () => { describe('text input', () => { beforeEach(() => browser.get('/input')); - it('should update input value when user types', () => { + it('should update input value when user types', async () => { let input = element(by.id('text-input')); input.sendKeys('abc123'); - expect(input.getAttribute('value')).toBe('abc123'); + expect(await input.getAttribute('value')).toBe('abc123'); }); }); describe('number input', () => { beforeEach(() => browser.get('/input')); - it('should update input value when user types', () => { + it('should update input value when user types', async () => { let input = element(by.id('number-input')); input.sendKeys('abc123'); - expect(input.getAttribute('value')).toBe('123'); + expect(await input.getAttribute('value')).toBe('123'); }); it('should increment when increment button clicked', async () => { @@ -40,24 +40,24 @@ describe('input', () => { .click() .perform(); - expect(input.getAttribute('value')).toBe('1'); + expect(await input.getAttribute('value')).toBe('1'); browser.actions() .mouseMove(input, {x: size.width - 5, y: size.height - 5}) .click() .perform(); - expect(input.getAttribute('value')).toBe('0'); + expect(await input.getAttribute('value')).toBe('0'); }); }); describe('textarea', () => { beforeEach(() => browser.get('/input')); - it('should update input value when user types', () => { + it('should update input value when user types', async () => { let input = element(by.id('text-area')); input.sendKeys('abc123'); - expect(input.getAttribute('value')).toBe('abc123'); + expect(await input.getAttribute('value')).toBe('abc123'); }); }); diff --git a/e2e/components/list-e2e.spec.ts b/e2e/components/list-e2e.spec.ts index bdb541373d8f..e0016fd6fa4b 100644 --- a/e2e/components/list-e2e.spec.ts +++ b/e2e/components/list-e2e.spec.ts @@ -6,11 +6,11 @@ describe('list', () => { beforeEach(() => browser.get('/list')); it('should render a list container', () => { - expectToExist('md-list'); + expectToExist('mat-list'); screenshot(); }); it('should render list items inside the list container', () => { - expectToExist('md-list md-list-item'); + expectToExist('mat-list mat-list-item'); }); }); diff --git a/e2e/components/menu-e2e.spec.ts b/e2e/components/menu-e2e.spec.ts index f9e442180c1a..d3368d75f61c 100644 --- a/e2e/components/menu-e2e.spec.ts +++ b/e2e/components/menu-e2e.spec.ts @@ -18,12 +18,12 @@ describe('menu', () => { beforeEach(() => page = new MenuPage()); - it('should open menu when the trigger is clicked', () => { + it('should open menu when the trigger is clicked', async () => { expectToExist(menuSelector, false); page.trigger().click(); expectToExist(menuSelector); - expect(page.menu().getText()).toEqual('One\nTwo\nThree\nFour'); + expect(await page.menu().getText()).toEqual('One\nTwo\nThree\nFour'); screenshot(); }); @@ -34,29 +34,29 @@ describe('menu', () => { screenshot(); }); - it('should run click handlers on regular menu items', () => { + it('should run click handlers on regular menu items', async () => { page.trigger().click(); page.items(0).click(); - expect(page.getResultText()).toEqual('one'); + expect(await page.getResultText()).toEqual('one'); screenshot('one'); page.trigger().click(); page.items(1).click(); - expect(page.getResultText()).toEqual('two'); + expect(await page.getResultText()).toEqual('two'); screenshot('two'); }); - it('should run not run click handlers on disabled menu items', () => { + it('should run not run click handlers on disabled menu items', async () => { page.trigger().click(); page.items(2).click(); - expect(page.getResultText()).toEqual(''); + expect(await page.getResultText()).toEqual(''); screenshot(); }); it('should support multiple triggers opening the same menu', async () => { page.triggerTwo().click(); - expect(page.menu().getText()).toEqual('One\nTwo\nThree\nFour'); + expect(await page.menu().getText()).toEqual('One\nTwo\nThree\nFour'); expectAlignedWith(page.menu(), '#trigger-two'); page.backdrop().click(); @@ -65,7 +65,7 @@ describe('menu', () => { page.trigger().click(); - expect(page.menu().getText()).toEqual('One\nTwo\nThree\nFour'); + expect(await page.menu().getText()).toEqual('One\nTwo\nThree\nFour'); expectAlignedWith(page.menu(), '#trigger'); page.backdrop().click(); @@ -97,9 +97,9 @@ describe('menu', () => { expectFocusOn(page.items(0)); }); - it('should not focus the first item when opened with mouse', () => { + it('should focus the panel when opened by mouse', () => { page.trigger().click(); - expectFocusOn(page.trigger()); + expectFocusOn(page.menu()); }); it('should focus subsequent items when down arrow is pressed', () => { @@ -207,7 +207,7 @@ export class MenuPage { trigger = () => element(by.id('trigger')); triggerTwo = () => element(by.id('trigger-two')); backdrop = () => element(by.css('.cdk-overlay-backdrop')); - items = (index: number) => element.all(by.css('[md-menu-item]')).get(index); + items = (index: number) => element.all(by.css('[mat-menu-item]')).get(index); textArea = () => element(by.id('text')); beforeTrigger = () => element(by.id('before-t')); aboveTrigger = () => element(by.id('above-t')); diff --git a/e2e/components/progress-bar-e2e.spec.ts b/e2e/components/progress-bar-e2e.spec.ts index aa62334f7235..3bf454764820 100644 --- a/e2e/components/progress-bar-e2e.spec.ts +++ b/e2e/components/progress-bar-e2e.spec.ts @@ -5,18 +5,18 @@ describe('progress-bar', () => { beforeEach(() => browser.get('/progress-bar')); it('should render a determinate progress bar', () => { - expectToExist('md-progress-bar[mode="determinate"]'); + expectToExist('mat-progress-bar[mode="determinate"]'); }); it('should render a buffer progress bar', () => { - expectToExist('md-progress-bar[mode="buffer"]'); + expectToExist('mat-progress-bar[mode="buffer"]'); }); it('should render a query progress bar', () => { - expectToExist('md-progress-bar[mode="query"]'); + expectToExist('mat-progress-bar[mode="query"]'); }); it('should render a indeterminate progress bar', () => { - expectToExist('md-progress-bar[mode="indeterminate"]'); + expectToExist('mat-progress-bar[mode="indeterminate"]'); }); }); diff --git a/e2e/components/progress-spinner-e2e.spec.ts b/e2e/components/progress-spinner-e2e.spec.ts index a8e5238cb9ae..07a23f765288 100644 --- a/e2e/components/progress-spinner-e2e.spec.ts +++ b/e2e/components/progress-spinner-e2e.spec.ts @@ -3,15 +3,16 @@ import {browser, by, element} from 'protractor'; describe('progress-spinner', () => { beforeEach(() => browser.get('/progress-spinner')); - it('should render a determinate progress spinner', () => { - expect(element(by.css('md-progress-spinner')).isPresent()).toBe(true); + it('should render a determinate progress spinner', async () => { + expect(await element(by.css('mat-progress-spinner')).isPresent()).toBe(true); }); - it('should render an indeterminate progress spinner', () => { - expect(element(by.css('md-progress-spinner[mode="indeterminate"]')).isPresent()).toBe(true); + it('should render an indeterminate progress spinner', async () => { + expect(await element(by.css('mat-progress-spinner[mode="indeterminate"]')).isPresent()) + .toBe(true); }); - it('should render a spinner', () => { - expect(element(by.css('md-spinner')).isPresent()).toBe(true); + it('should render a spinner', async () => { + expect(await element(by.css('mat-spinner')).isPresent()).toBe(true); }); }); diff --git a/e2e/components/sidenav-e2e.spec.ts b/e2e/components/sidenav-e2e.spec.ts index 445d8bf63b76..9f90e4403652 100644 --- a/e2e/components/sidenav-e2e.spec.ts +++ b/e2e/components/sidenav-e2e.spec.ts @@ -6,7 +6,7 @@ describe('sidenav', () => { beforeEach(() => { browser.get('/sidenav'); - sidenav = element(by.tagName('md-sidenav')); + sidenav = element(by.tagName('mat-sidenav')); }); it('should be closed', () => { diff --git a/e2e/components/slide-toggle-e2e.spec.ts b/e2e/components/slide-toggle-e2e.spec.ts index 2780cb91a008..a8c232d06d54 100644 --- a/e2e/components/slide-toggle-e2e.spec.ts +++ b/e2e/components/slide-toggle-e2e.spec.ts @@ -10,7 +10,7 @@ describe('slide-toggle', () => { beforeEach(() => browser.get('slide-toggle')); it('should render a slide-toggle', () => { - expectToExist('md-slide-toggle'); + expectToExist('mat-slide-toggle'); screenshot(); }); diff --git a/e2e/components/stepper-e2e.spec.ts b/e2e/components/stepper-e2e.spec.ts new file mode 100644 index 000000000000..833ba1d6d40e --- /dev/null +++ b/e2e/components/stepper-e2e.spec.ts @@ -0,0 +1,84 @@ +import { + browser, by, element, ElementFinder, ExpectedConditions +} from 'protractor'; +import {expectFocusOn, expectToExist} from '../util/asserts'; +import {pressKeys} from '../util/actions'; +import {Key} from 'selenium-webdriver'; +import {screenshot} from '../screenshot'; + +describe('stepper', () => { + beforeEach(() => browser.get('/stepper')); + + it('should render a stepper', () => { + expectToExist('mat-horizontal-stepper'); + screenshot('mat-horizontal-stepper'); + }); + + describe('basic behavior', () => { + it('should change steps correctly when stepper button is clicked', async () => { + const previousButton = element.all(by.buttonText('Back')); + const nextButton = element.all(by.buttonText('Next')); + + expect(await element(by.css('mat-step-header[aria-selected="true"]')).getText()) + .toBe('1\nFill out your name'); + + screenshot('start'); + nextButton.get(0).click(); + + expect(await element(by.css('mat-step-header[aria-selected="true"]')).getText()) + .toBe('2\nFill out your address'); + + await browser.wait(ExpectedConditions.not( + ExpectedConditions.presenceOf(element(by.css('div.mat-ripple-element'))))); + screenshot('click next'); + + previousButton.get(0).click(); + + expect(await element(by.css('mat-step-header[aria-selected="true"]')).getText()) + .toBe('1\nFill out your name'); + + await browser.wait(ExpectedConditions.not( + ExpectedConditions.presenceOf(element(by.css('div.mat-ripple-element'))))); + screenshot('click back'); + }); + + it('should change focus with keyboard interaction', () => { + let stepHeaders = element.all(by.css('mat-step-header')); + stepHeaders.get(0).click(); + + expectFocusOn(stepHeaders.get(0)); + + pressKeys(Key.RIGHT); + expectFocusOn(stepHeaders.get(1)); + + pressKeys(Key.RIGHT); + expectFocusOn(stepHeaders.get(2)); + + pressKeys(Key.RIGHT); + expectFocusOn(stepHeaders.get(0)); + + pressKeys(Key.LEFT); + expectFocusOn(stepHeaders.get(2)); + + pressKeys(Key.SPACE, Key.ENTER); + expectFocusOn(stepHeaders.get(2)); + }); + }); + + describe('linear stepper', () => { + let linearButton: ElementFinder; + + beforeEach(() => { + linearButton = element(by.id('toggle-linear')); + linearButton.click(); + }); + + it('should not move to next step when stepper button is clicked', async () => { + let nextButton = element.all(by.buttonText('Next')); + nextButton.get(0).click(); + + expect(await element(by.css('mat-step-header[aria-selected="true"]')).getText()) + .toBe('1\nFill out your name'); + }); + }); +}); diff --git a/e2e/components/tabs-e2e.spec.ts b/e2e/components/tabs-e2e.spec.ts index a2df986deed6..db7f2ed87bec 100644 --- a/e2e/components/tabs-e2e.spec.ts +++ b/e2e/components/tabs-e2e.spec.ts @@ -19,53 +19,53 @@ describe('tabs', () => { beforeEach(() => { browser.get('/tabs'); - tabGroup = element(by.css('md-tab-group')); + tabGroup = element(by.css('mat-tab-group')); tabLabels = element.all(by.css('.mat-tab-label')); - tabBodies = element.all(by.css('md-tab-body')); + tabBodies = element.all(by.css('mat-tab-body')); }); it('should change tabs when the label is clicked', async () => { tabLabels.get(1).click(); - expect(getLabelActiveStates(tabLabels)).toEqual([false, true, false]); - expect(getBodyActiveStates(tabBodies)).toEqual([false, true, false]); + expect(await getLabelActiveStates(tabLabels)).toEqual([false, true, false]); + expect(await getBodyActiveStates(tabBodies)).toEqual([false, true, false]); await browser.wait(ExpectedConditions.not( ExpectedConditions.presenceOf(element(by.css('div.mat-ripple-element'))))); screenshot('click1'); tabLabels.get(0).click(); - expect(getLabelActiveStates(tabLabels)).toEqual([true, false, false]); - expect(getBodyActiveStates(tabBodies)).toEqual([true, false, false]); + expect(await getLabelActiveStates(tabLabels)).toEqual([true, false, false]); + expect(await getBodyActiveStates(tabBodies)).toEqual([true, false, false]); await browser.wait(ExpectedConditions.not( ExpectedConditions.presenceOf(element(by.css('div.mat-ripple-element'))))); screenshot('click0'); }); - it('should change focus with keyboard interaction', () => { + it('should change focus with keyboard interaction', async () => { let right = Key.RIGHT; let left = Key.LEFT; tabLabels.get(0).click(); - expect(getFocusStates(tabLabels)).toEqual([true, false, false]); + expect(await getFocusStates(tabLabels)).toEqual([true, false, false]); pressKeys(right); - expect(getFocusStates(tabLabels)).toEqual([false, true, false]); + expect(await getFocusStates(tabLabels)).toEqual([false, true, false]); pressKeys(right); - expect(getFocusStates(tabLabels)).toEqual([false, false, true]); + expect(await getFocusStates(tabLabels)).toEqual([false, false, true]); pressKeys(right); - expect(getFocusStates(tabLabels)).toEqual([false, false, true]); + expect(await getFocusStates(tabLabels)).toEqual([false, false, true]); pressKeys(left); - expect(getFocusStates(tabLabels)).toEqual([false, true, false]); + expect(await getFocusStates(tabLabels)).toEqual([false, true, false]); pressKeys(left); - expect(getFocusStates(tabLabels)).toEqual([true, false, false]); + expect(await getFocusStates(tabLabels)).toEqual([true, false, false]); pressKeys(left); - expect(getFocusStates(tabLabels)).toEqual([true, false, false]); + expect(await getFocusStates(tabLabels)).toEqual([true, false, false]); }); }); }); @@ -74,8 +74,8 @@ describe('tabs', () => { * Returns an array of true/false that represents the focus states of the provided elements. */ async function getFocusStates(elements: ElementArrayFinder) { - return elements.map(async (element) => { - let elementText = await element!.getText(); + return elements.map(async el => { + let elementText = await el!.getText(); let activeText = await browser.driver.switchTo().activeElement().getText(); return activeText === elementText; @@ -97,8 +97,8 @@ function getBodyActiveStates(elements: ElementArrayFinder) { * each element. */ async function getClassStates(elements: ElementArrayFinder, className: string) { - return elements.map(async (element) => { - let classes = await element!.getAttribute('class'); + return elements.map(async el => { + let classes = await el!.getAttribute('class'); return classes.split(/ +/g).indexOf(className) >= 0; }); } diff --git a/e2e/components/toolbar-e2e.spec.ts b/e2e/components/toolbar-e2e.spec.ts index 6b03e4abcb56..eaabb2c36aaa 100644 --- a/e2e/components/toolbar-e2e.spec.ts +++ b/e2e/components/toolbar-e2e.spec.ts @@ -1,12 +1,12 @@ import {browser, by, element} from 'protractor'; import {screenshot} from '../screenshot'; -describe('md-toolbar', () => { +describe('mat-toolbar', () => { beforeEach(() => browser.get('/toolbar')); it('should show a toolbar', async () => { - expect(element(by.tagName('md-toolbar'))).toBeDefined(); + expect(element(by.tagName('mat-toolbar'))).toBeDefined(); screenshot('multiple toolbar components'); }); diff --git a/e2e/index-e2e.spec.ts b/e2e/index-e2e.spec.ts index f0fce6fd3827..9464a17605c3 100644 --- a/e2e/index-e2e.spec.ts +++ b/e2e/index-e2e.spec.ts @@ -3,8 +3,8 @@ import {browser} from 'protractor'; describe('hello, protractor', () => { describe('index', () => { browser.get('/'); - it('should have a title', () => { - expect(browser.getTitle()).toBe('Angular Material'); + it('should have a title', async () => { + expect(await browser.getTitle()).toBe('Angular Material'); }); }); }); diff --git a/e2e/util/asserts.ts b/e2e/util/asserts.ts index f507d096e399..d508ca7c79ca 100644 --- a/e2e/util/asserts.ts +++ b/e2e/util/asserts.ts @@ -24,8 +24,8 @@ export function expectFocusOn(element: FinderResult, expected = true): void { */ export function expectLocation(element: FinderResult, {x, y}: Point): void { getElement(element).getLocation().then((location: Point) => { - expect(location.x).toEqual(x); - expect(location.y).toEqual(y); + expect(Math.round(location.x)).toEqual(Math.round(x)); + expect(Math.round(location.y)).toEqual(Math.round(y)); }); } diff --git a/guides/bidirectionality.md b/guides/bidirectionality.md index 9b21647e416e..1b7bca1a2d76 100644 --- a/guides/bidirectionality.md +++ b/guides/bidirectionality.md @@ -9,9 +9,9 @@ All Angular Material components automatically reflect the LTR/RTL direction of their container. ### Reading the text-direction in your own components -`@angular/cdk` provides a `Directionality` injectable that can be used by any component +`@angular/cdk/bidi` provides a `Directionality` injectable that can be used by any component in your application. To consume this injectable, you must import `BidiModule` -from `@angular/cdk`. +from `@angular/cdk/bidi`. `Directionality` provides two useful properties: * `value`: the current text direction, either `'ltr'` or `'rtl'`. @@ -32,4 +32,4 @@ export class MyCustomComponent { }); } } -``` \ No newline at end of file +``` diff --git a/guides/cdk-table.md b/guides/cdk-table.md index 08a7027b3bfd..365ac59234c7 100644 --- a/guides/cdk-table.md +++ b/guides/cdk-table.md @@ -7,7 +7,7 @@ built. Because it enforces no opinions on these matters, developers have full co interaction patterns associated with the table. For a Material Design styled table, see the -[documentation for ``](https://material.angular.io/components/table) which builds on +[documentation for ``](https://material.angular.io/components/table) which builds on top of the CDK data-table. diff --git a/guides/compatibility-mode.md b/guides/compatibility-mode.md deleted file mode 100644 index 0efea29183f3..000000000000 --- a/guides/compatibility-mode.md +++ /dev/null @@ -1,22 +0,0 @@ -## What is compatibility mode? -Compatibility mode for `@angular/material` allows the components to exist side-by-side with -AngularJS Material components without any CSS collisions. - -## What does compatibility mode do? -When enabled, compatibility mode enforces that all template APIs use the prefix `mat-` instead of -`md-`. This will prevent any CSS from AngularJS Material from affecting the components in -`@angular/material`. - -## How is compatibility mode enabled? -Import `NoConflictStyleCompatibilityMode` into your application's root `NgModule`. - - -## Example - -```html - - - - - -``` diff --git a/guides/creating-a-custom-form-field-control.md b/guides/creating-a-custom-form-field-control.md new file mode 100644 index 000000000000..738b24b25d73 --- /dev/null +++ b/guides/creating-a-custom-form-field-control.md @@ -0,0 +1,351 @@ +It is possible to create custom form field controls that can be used inside ``. This +can be useful if you need to create a component that shares a lot of common behavior with a form +field, but adds some additional logic. + +For example in this guide we'll learn how to create a custom input for inputting US telephone +numbers and hook it up to work with ``. Here is what we'll build by the end of this +guide: + + + +In order to learn how to build custom form field controls, let's start with a simple input component +that we want to work inside the form field. For example, a phone number input that segments the +parts of the number into their own inputs. (Note: this is not intended to be a robust component, +just a starting point for us to learn.) + +```ts +class MyTel { + constructor(public area: string, public exchange: string, public subscriber: string) {} +} + +@Component({ + selector: 'my-tel-input', + template: ` +
+ + + + + +
+ `, + styles: [` + div { + display: flex; + } + input { + border: none; + background: none; + padding: 0; + outline: none; + font: inherit; + text-align: center; + } + `], +}) +class MyTelInput { + parts: FormGroup; + + @Input() + get value(): MyTel | null { + let n = this.parts.value; + if (n.area.length == 3 && n.exchange.length == 3 && n.subscriber.length == 4) { + return new MyTel(n.area, n.exchange, n.subscriber); + } + return null; + } + set value(tel: MyTel | null) { + tel = tel || new MyTel('', '', ''); + this.parts.setValue({area: tel.area, exchange: tel.exchange, subscriber: tel.subscriber}); + } + + constructor(fb: FormBuilder) { + this.parts = fb.group({ + 'area': '', + 'exchange': '', + 'subscriber': '', + }); + } +} +``` + +### Providing our component as a MatFormFieldControl + +The first step is to provide our new component as an implementation of the `MatFormFieldControl` +interface that the `` knows how to work with. To do this, we will have our class +implement `MatFormFieldControl`. Since this is a generic interface, we'll need to include a type +parameter indicating the type of data our control will work with, in this case `MyTel`. We then add +a provider to our component so that the form field will be able to inject it as a +`MatFormFieldControl`. + +```ts +@Component({ + ... + providers: [{provide: MatFormFieldControl, useExisting: MyTelInput}], +}) +class MyTelInput implements MatFormFieldControl { + ... +} +``` + +This sets up our component so it can work with ``, but now we need to implement the +various methods and properties declared by the interface we just implemented. To learn more about +the `MatFormFieldControl` interface, see the +[form field API documentation](https://material.angular.io/components/form-field/api). + +### Implementing the methods and properties of MatFormFieldControl + +#### `value` + +This property allows someone to set or get the value of our control. Its type should be the same +type we used for the type parameter when we implemented `MatFormFieldControl`. Since our component +already has a value property, we don't need to do anything for this one. + +#### `stateChanges` + +Because the `` uses the `OnPush` change detection strategy, we need to let it know +when something happens in the form field control that may require the form field to run change +detection. We do this via the `stateChanges` property. So far the only thing the form field needs to +know about is when the value changes. We'll need to emit on the stateChanges stream when that +happens, and as we continue flushing out these properties we'll likely find more places we need to +emit. We should also make sure to complete `stateChanges` when our component is destroyed. + +```ts +stateChanges = new Subject(); + +set value(tel: MyTel | null) { + ... + this.stateChanges.next(); +} + +ngOnDestroy() { + this.stateChanges.complete(); +} +``` + +#### `id` + +This property should return the ID of an element in the component's template that we want the +`` to associate all of its labels and hints with. In this case, we'll use the host +element and just generate a unique ID for it. + +```ts +static nextId = 0; + +@HostBinding() id = `my-tel-input-${MyTelInput.nextId++}`; +``` + +#### `placeholder` + +This property allows us to tell the `` what to use as a placeholder. In this +example, we'll do the same thing as `matInput` and `` and allow the user to specify it +via an `@Input()`. Since the value of the placeholder may change over time, we need to make sure to +trigger change detection in the parent form field by emitting on the `stateChanges` stream when the +placeholder changes. + +```ts +@Input() +get placeholder() { + return this._placeholder; +} +set placeholder(plh) { + this._placeholder = plh; + this.stateChanges.next(); +} +private _placeholder: string; +``` + +#### `ngControl` + +This property allows the form field control to specify the `@angular/forms` control that is bound to +this component. Since we haven't set up our component to act as a `ControlValueAccessor`, we'll just +set this to `null` in our component. In any real component, you would probably want to implement +`ControlValueAccessor` so that your component can work with `formControl` and `ngModel`. + +```ts +ngControl = null; +``` + +If you did implement `ControlValueAccessor`, you could simply inject the `NgControl` and make it +publicly available. (For additional information about `ControlValueAccessor` see the +[API docs](https://angular.io/api/forms/ControlValueAccessor).) + +```ts +constructor(..., @Optional() @Self() public ngControl: NgControl) { ... } +``` + +#### `focused` + +This property indicates whether or not the form field control should be considered to be in a +focused state. When it is in a focused state, the form field is displayed with a solid color +underline. For the purposes of our component, we want to consider it focused if any of the part +inputs are focused. We can use the `FocusMonitor` from `@angular/cdk` to easily check this. We also +need to remember to emit on the `stateChanges` stream so change detection can happen. + +```ts +focused = false; + +constructor(fb: FormBuilder, private fm: FocusMonitor, private elRef: ElementRef) { + ... + fm.monitor(elRef.nativeElement, true).subscribe(origin => { + this.focused = !!origin; + this.stateChanges.next(); + }); +} + +ngOnDestroy() { + ... + this.fm.stopMonitoring(this.elRef.nativeElement); +} +``` + +#### `empty` + +This property indicates whether the form field control is empty. For our control, we'll consider it +empty if all of the parts are empty. + +```ts +get empty() { + let n = this.parts.value; + return !n.area && !n.exchange && !n.subscriber; +} +``` + +#### `shouldLabelFloat` + +This property is used to indicate whether the label should be in the floating position. We'll +use the same logic as `matInput` and float the placeholder when the input is focused or non-empty. +Since the placeholder will be overlapping our control when when it's not floating, we should hide +the `–` characters when it's not floating. + +```ts +@HostBinding('class.floating') +get shouldLabelFloat() { + return this.focused || !this.empty; +} +``` +```css +span { + opacity: 0; + transition: opacity 200ms; +} +:host.floating span { + opacity: 1; +} +``` + +#### `required` + +This property is used to indicate whether the input is required. `` uses this +information to add a required indicator to the placeholder. Again, we'll want to make sure we run +change detection if the required state changes. + +```ts +@Input() +get required() { + return this._required; +} +set required(req) { + this._required = coerceBooleanProperty(req); + this.stateChanges.next(); +} +private _required = false; +``` + +#### `disabled` + +This property tells the form field when it should be in the disabled state. In addition to reporting +the right state to the form field, we need to set the disabled state on the individual inputs that +make up our component. + +```ts +@Input() +get disabled() { + return this._disabled; +} +set disabled(dis) { + this._disabled = coerceBooleanProperty(dis); + this.stateChanges.next(); +} +private _disabled = false; +``` +```html + + + + + +``` + +#### `errorState` + +This property indicates whether the associated `NgControl` is in an error state. Since we're not +using an `NgControl` in this example, we don't need to do anything other than just set it to `false`. + +```ts +errorState = false; +``` + +#### `controlType` + +This property allows us to specify a unique string for the type of control in form field. The +`` will add an additional class based on this type that can be used to easily apply +special styles to a `` that contains a specific type of control. In this example +we'll use `my-tel-input` as our control type which will result in the form field adding the class +`mat-form-field-my-tel-input`. + +```ts +controlType = 'my-tel-input'; +``` + +#### `setAriaDescribedByIds(ids: string[])` + +This method is used by the `` to specify the IDs that should be used for the +`aria-describedby` attribute of your component. The method has one parameter, the list of IDs, we +just need to apply the given IDs to our host element. + +```ts +@HostBinding('attr.aria-describedby') describedBy = ''; + +setDescribedByIds(ids: string[]) { + this.describedBy = ids.join(' '); +} +``` + +#### `onContainerClick(event: MouseEvent)` + +This method will be called when the form field is clicked on. It allows your component to hook in +and handle that click however it wants. The method has one parameter, the `MouseEvent` for the +click. In our case we'll just focus the first `` if the user isn't about to click an +`` anyways. + +```ts +onContainerClick(event: MouseEvent) { + if ((event.target as Element).tagName.toLowerCase() != 'input') { + this.elRef.nativeElement.querySelector('input').focus(); + } +} +``` + +### Trying it out + +Now that we've fully implemented the interface, we're ready to try our component out! All we need to +do is place it inside of a `` + +```html + + + +``` + +We also get all of the features that come with `` such as floating placeholder, +prefix, suffix, hints, and errors (if we've given the form field an `NgControl` and correctly report +the error state). + +```html + + + phone + Include area code + +``` diff --git a/guides/customizing-component-styles.md b/guides/customizing-component-styles.md index a5db1a459e51..5a3a641578ac 100644 --- a/guides/customizing-component-styles.md +++ b/guides/customizing-component-styles.md @@ -30,7 +30,7 @@ You can read more about specificity and how it is calculated on the ##### Component location -Some Angular Material components, specifically overlay-based ones like MdDialog, MdSnackbar, etc., +Some Angular Material components, specifically overlay-based ones like MatDialog, MatSnackbar, etc., do not exist as children of your component. Often they are injected elsewhere in the DOM. This is important to keep in mind, since even using high specificity and shadow-piercing selectors will not target elements that are not direct children of your component. Global styles are recommended diff --git a/guides/elevation.md b/guides/elevation.md new file mode 100644 index 000000000000..270913f47c56 --- /dev/null +++ b/guides/elevation.md @@ -0,0 +1,50 @@ +Angular Material's elevation classes and mixins allow you to add separation between elements along +the z-axis. All material design elements have resting elevations. In addition, some elements may +change their elevation in response to user interaction. The +[Material Design spec](https://material.io/guidelines/material-design/elevation-shadows.html) +explains how to best use elevation. + +Angular Material provides two ways to control the elevation of elements: predefined CSS classes +and mixins. + +### Predefined CSS classes + +The easiest way to add elevation to an element is to simply add one of the predefined CSS classes +`mat-elevation-z#` where `#` is the elevation number you want, 0-24. Dynamic elevation can be +achieved by switching elevation classes: + +```html +
+``` + + + +### Mixins + +Elevations can also be added in CSS via the `mat-elevation` mixin, which takes a number 0-24 +indicating the elevation of the element. In order to use the mixin, you must import +`~@angular/material/theming`: + +```scss +@import '~@angular/material/theming'; + +.my-class { + @include mat-elevation(2); +} +``` + +For convenience, you can use the `mat-elevation-transition` mixin to add a transition when the +elevation changes: + +```scss +@import '~@angular/material/theming'; + +.my-class { + @include mat-elevation-transition; + @include mat-elevation(2); + + &:active { + @include mat-elevation(8); + } +} +``` diff --git a/guides/getting-started.md b/guides/getting-started.md index d7d28ba9b550..767adc6e89be 100644 --- a/guides/getting-started.md +++ b/guides/getting-started.md @@ -9,6 +9,8 @@ For existing apps, follow these steps to begin using Angular Material. npm install --save @angular/material @angular/cdk ``` +#### Alternative: Snapshot Build + A snapshot build with the latest changes from master is also available. Note that this snapshot build should not be considered stable and may break between releases. @@ -26,6 +28,10 @@ install the `@angular/animations` module and include the `BrowserAnimationsModul npm install --save @angular/animations ``` +**Note:** `@angular/animations` uses the WebAnimation API that isn't supported by all browsers yet. +If you want to support Material component animations in these browsers, you'll have to +[include a polyfill](https://github.com/web-animations/web-animations-js). + ```ts import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; @@ -52,34 +58,34 @@ export class PizzaPartyAppModule { } ### Step 3: Import the component modules -Import the NgModule for each component you want to use: +Import the NgModule for each component you want to use: ```ts -import {MdButtonModule, MdCheckboxModule} from '@angular/material'; +import {MatButtonModule, MatCheckboxModule} from '@angular/material'; @NgModule({ ... - imports: [MdButtonModule, MdCheckboxModule], + imports: [MatButtonModule, MatCheckboxModule], ... }) export class PizzaPartyAppModule { } ``` -Alternatively, you can create a separate NgModule that imports all of the +Alternatively, you can create a separate NgModule that imports all of the Angular Material components that you will use in your application. You can then include this module wherever you'd like to use the components. ```ts -import {MdButtonModule, MdCheckboxModule} from '@angular/material'; +import {MatButtonModule, MatCheckboxModule} from '@angular/material'; @NgModule({ - imports: [MdButtonModule, MdCheckboxModule], - exports: [MdButtonModule, MdCheckboxModule], + imports: [MatButtonModule, MatCheckboxModule], + exports: [MatButtonModule, MatCheckboxModule], }) export class MyOwnCustomMaterialModule { } ``` -Whichever approach you use, be sure to import the Angular Material modules _after_ Angular's +Whichever approach you use, be sure to import the Angular Material modules _after_ Angular's `BrowserModule`, as the import order matters for NgModules. ### Step 4: Include a theme @@ -100,7 +106,7 @@ For more information on theming and instructions on how to create a custom theme ### Step 5: Gesture Support -Some components (`md-slide-toggle`, `md-slider`, `mdTooltip`) rely on +Some components (`mat-slide-toggle`, `mat-slider`, `matTooltip`) rely on [HammerJS](http://hammerjs.github.io/) for gestures. In order to get the full feature-set of these components, HammerJS must be loaded into the application. @@ -120,7 +126,7 @@ import 'hammerjs'; ### Step 6 (Optional): Add Material Icons -If you want to use the `md-icon` component with the official +If you want to use the `mat-icon` component with the official [Material Design Icons](https://material.io/icons/), load the icon font in your `index.html`. ```html @@ -130,7 +136,7 @@ If you want to use the `md-icon` component with the official For more information on using Material Icons, check out the [Material Icons Guide](https://google.github.io/material-design-icons/). -Note that `md-icon` supports any font or svg icons; using Material Icons is one of many options. +Note that `mat-icon` supports any font or svg icons; using Material Icons is one of many options. ### Appendix: Configuring SystemJS @@ -138,10 +144,10 @@ Note that `md-icon` supports any font or svg icons; using Material Icons is one If your project is using SystemJS for module loading, you will need to add `@angular/material` and `@angular/cdk` to the SystemJS configuration. -The `@angular/cdk` package is organized of multiple entry-points. +The `@angular/cdk` package is organized of multiple entry-points. Each of these entry-points must be registered individually with SystemJS. -Here is a example configuration where `@angular/material`, `@angular/cdk/platform` and +Here is a example configuration where `@angular/material`, `@angular/cdk/platform` and `@angular/cdk/a11y` are used: @@ -151,7 +157,7 @@ System.config({ map: { // ... '@angular/material': 'npm:@angular/material/bundles/material.umd.js', - + // CDK individual packages '@angular/cdk/platform': 'npm:@angular/cdk/bundles/cdk-platform.umd.js', '@angular/cdk/a11y': 'npm:@angular/cdk/bundles/cdk-a11y.umd.js', diff --git a/guides/theming-your-components.md b/guides/theming-your-components.md index 420a03790250..e1ac8a08751b 100644 --- a/guides/theming-your-components.md +++ b/guides/theming-your-components.md @@ -4,11 +4,11 @@ In order to style your own components with Angular Material's tooling, the compo ### Using `@mixin` to automatically apply a theme #### Why using `@mixin` -The advantage of using a `@mixin` function is that when you change your theme, every file that uses it will be updated automatically. -Calling it with a different theme argument allow multiple themes within the app or component. +The advantage of using a `@mixin` function is that when you change your theme, every file that uses it will be automatically updated. +Calling the `@mixin` with a different theme argument allows multiple themes within the app or component. #### How to use `@mixin` -We can better theming our custom components adding a `@mixin` function to its theme file and then calling this function to apply a theme. +We can better theme our custom components by adding a `@mixin` function to its theme file, and then call this function to apply a theme. All you need is to create a `@mixin` function in the custom-component-theme.scss diff --git a/guides/theming.md b/guides/theming.md index 8e4ccc89b90e..9c38f2aa242e 100644 --- a/guides/theming.md +++ b/guides/theming.md @@ -46,7 +46,7 @@ The actual path will depend on your server setup. You can also concatenate the file with the rest of your application's css. -Finally, if your app's content **is not** placed inside of a `md-sidenav-container` element, you +Finally, if your app's content **is not** placed inside of a `mat-sidenav-container` element, you need to add the `mat-app-background` class to your wrapper element (for example the `body`). This ensures that the proper theme background is applied to your page. @@ -169,24 +169,21 @@ Since certain components (e.g. menu, select, dialog, etc.) are inside of a globa an additional step is required for those components to be affected by the theme's css class selector (`.unicorn-dark-theme` in the example above). -To do this, you can specify a `themeClass` on the global overlay container. For the example above, +To do this, you can add the appropriate class to the global overlay container. For the example above, this would look like: ```ts -import {OverlayContainer} from '@angular/material'; +import {OverlayContainer} from '@angular/cdk/overlay'; @NgModule({ // ... }) export class UnicornCandyAppModule { constructor(overlayContainer: OverlayContainer) { - overlayContainer.themeClass = 'unicorn-dark-theme'; + overlayContainer.getContainerElement().classList.add('unicorn-dark-theme'); } } ``` -The `themeClass` of the `OverlayContainer` can be changed at any time to change the active theme -class. - #### Theming only certain components The `angular-material-theme` mixin will output styles for [all components in the library](https://github.com/angular/material2/blob/master/src/lib/core/theming/_all-theme.scss). If you are only using a subset of the components (or if you want to change the theme for specific diff --git a/guides/typography.md b/guides/typography.md index 89ca28cacb72..fe117bf5fa50 100644 --- a/guides/typography.md +++ b/guides/typography.md @@ -16,6 +16,7 @@ at the top of the page (e.g. a hero header). * `body-2` - Bolder body text. * `caption` - Smaller body and hint text. * `button` - Buttons and anchors. +* `input` - Form input fields. The typography levels are collected into a typography config which is used to generate the CSS. @@ -58,7 +59,7 @@ creating a custom theme, you can create a custom **typography configuration**. // Define a custom typography config that overrides the font-family as well as the // `headlines` and `body-1` levels. $custom-typography: mat-typography-config( - $font-family: monospace, + $font-family: 'Roboto, monospace', $headline: mat-typography-level(32px, 48px, 700), $body-1: mat-typography-level(16px, 24px, 500) ); @@ -67,7 +68,8 @@ $custom-typography: mat-typography-config( As the above example demonstrates, a typography configuration is created by using the `mat-typography-config` function, which is given both the font-family and the set of typographic levels described earlier. Each typographic level is defined by the `mat-typography-level` function, -which requires a `font-size`, `line-height`, and `font-weight`. +which requires a `font-size`, `line-height`, and `font-weight`. **Note** that the `font-family` +has to be in quotes. Once the custom typography definition is created, it can be consumed to generate styles via diff --git a/package-lock.json b/package-lock.json index 18d9ce7ecbe6..676d2415afa8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,148 +1,328 @@ { "name": "material2-srcs", - "version": "2.0.0-beta.8", + "version": "5.0.0-rc.2", "lockfileVersion": 1, "requires": true, "dependencies": { "@angular/animations": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-4.3.6.tgz", - "integrity": "sha1-v5KD7HyMmLMvVp2E3NoQiQ/cAmI=", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-5.0.0.tgz", + "integrity": "sha1-ta0ZnGf5P3WVREd+/+ZnnhVJkfs=", + "requires": { + "tslib": "1.8.0" + } + }, + "@angular/bazel": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@angular/bazel/-/bazel-5.0.1.tgz", + "integrity": "sha1-08ua/X4PwC6KFc3igJJAGCyD1NU=", + "dev": true, "requires": { - "tslib": "1.7.1" + "@bazel/typescript": "0.2.2", + "@types/node": "6.0.84" + }, + "dependencies": { + "@bazel/typescript": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@bazel/typescript/-/typescript-0.2.2.tgz", + "integrity": "sha1-Cg0JkkUIyUbeMuas2ytcwe4yxQY=", + "dev": true, + "requires": { + "@types/node": "7.0.18", + "@types/source-map": "0.5.2", + "protobufjs": "5.0.0", + "tsickle": "0.24.1", + "typescript": "2.4.2" + }, + "dependencies": { + "@types/node": { + "version": "7.0.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.18.tgz", + "integrity": "sha1-zWfyfT3Az7dG8L3V4IbExdVb4XM=", + "dev": true + }, + "typescript": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.4.2.tgz", + "integrity": "sha1-+DlfhdRZJ2BnyYiqQYN6j4KHCEQ=", + "dev": true + } + } + }, + "@types/node": { + "version": "6.0.84", + "resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.84.tgz", + "integrity": "sha512-1SvEazClhUBRNroJM3oB3xf3u2r6xGmHDGbdigqNPHvNKLl8/BtATgO9eC04ZLuovpSh0B20BF1QJxdi+qmTlg==", + "dev": true + }, + "glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", + "dev": true, + "requires": { + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "protobufjs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-5.0.0.tgz", + "integrity": "sha1-QiMGMjPqlqwGPKK1VANSBNtST6E=", + "dev": true, + "requires": { + "ascli": "1.0.1", + "bytebuffer": "5.0.1", + "glob": "5.0.15", + "yargs": "3.32.0" + } + } } }, "@angular/common": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-4.3.6.tgz", - "integrity": "sha1-7TfpMHx1Bt2DR5fBps9nXlK1tu4=", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-5.0.0.tgz", + "integrity": "sha1-+W1mpRe5ldG6mygwnxXC41lnWCU=", "requires": { - "tslib": "1.7.1" + "tslib": "1.8.0" } }, "@angular/compiler": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-4.3.6.tgz", - "integrity": "sha1-vhcN8Ji3HoNczt8WjV+3sj5QRbg=", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-5.0.0.tgz", + "integrity": "sha1-uf+/GMijnYt9rOxHMZOpDiTMK8k=", "requires": { - "tslib": "1.7.1" + "tslib": "1.8.0" } }, "@angular/compiler-cli": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-4.3.6.tgz", - "integrity": "sha1-avpq72jdaB5hs5i+TWJw5choCxI=", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-5.0.0.tgz", + "integrity": "sha1-Dsu5N9hKT43ZTwwqR7B9LkaUyFM=", "dev": true, "requires": { - "@angular/tsc-wrapped": "4.3.6", + "chokidar": "1.7.0", "minimist": "1.2.0", - "reflect-metadata": "0.1.10" + "reflect-metadata": "0.1.10", + "tsickle": "0.24.1" } }, "@angular/core": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-4.3.6.tgz", - "integrity": "sha1-u6xj1o0Pe8s4nRKzQghlK+MofpY=", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-5.0.0.tgz", + "integrity": "sha1-T5dqIl993fNJkvLK2CTJVDpG9Mg=", "requires": { - "tslib": "1.7.1" + "tslib": "1.8.0" } }, "@angular/forms": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-4.3.6.tgz", - "integrity": "sha1-DyDEWXwWoVJ0XXzZVVmFWgpcZoc=", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-5.0.0.tgz", + "integrity": "sha1-x/3fo1OWdZrphSkgowzdqMQe0d4=", "requires": { - "tslib": "1.7.1" + "tslib": "1.8.0" } }, "@angular/http": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/@angular/http/-/http-4.3.6.tgz", - "integrity": "sha1-Vjgn0afV6J47fYa3f7vTZ7LAhZE=", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@angular/http/-/http-5.0.0.tgz", + "integrity": "sha1-Byiivgz7sHhyfF64fUyF1T/smlE=", + "dev": true, "requires": { - "tslib": "1.7.1" + "tslib": "1.8.0" } }, "@angular/platform-browser": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-4.3.6.tgz", - "integrity": "sha1-YVKx87eNAkb8XhUOL3ue1DN+O6Y=", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-5.0.0.tgz", + "integrity": "sha1-xwOPfN6AcFtiAUiXIx4YLuyXb+0=", "requires": { - "tslib": "1.7.1" + "tslib": "1.8.0" } }, "@angular/platform-browser-dynamic": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-4.3.6.tgz", - "integrity": "sha1-nqv4JvEZyY+Fwqlu3LGKsAtO+xw=", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-5.0.0.tgz", + "integrity": "sha1-iH4QbIsQOwQVz2FWpCXabYP0yJ0=", "dev": true, "requires": { - "tslib": "1.7.1" + "tslib": "1.8.0" } }, "@angular/platform-server": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/@angular/platform-server/-/platform-server-4.3.6.tgz", - "integrity": "sha1-Np1JhE8cCpoQx8upsMt4wlIHQaU=", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@angular/platform-server/-/platform-server-5.0.0.tgz", + "integrity": "sha1-h30l74FK+S//x7C1I7lxpv8iIBg=", "dev": true, "requires": { - "parse5": "3.0.2", - "tslib": "1.7.1", + "domino": "1.0.30", + "tslib": "1.8.0", "xhr2": "0.1.4" } }, "@angular/router": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-4.3.6.tgz", - "integrity": "sha1-ZAM+20/NoIoyPnUztKGCDA8o0TA=", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-5.0.0.tgz", + "integrity": "sha1-/ktSGmc4QIvOMPk6U0mRQMk6T3Y=", + "dev": true, + "requires": { + "tslib": "1.8.0" + } + }, + "@angular/upgrade": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@angular/upgrade/-/upgrade-5.0.1.tgz", + "integrity": "sha1-HEVVGJu4iBQwb5c1IIQj3b1Y6o8=", "dev": true, "requires": { - "tslib": "1.7.1" + "tslib": "1.8.0" } }, - "@angular/tsc-wrapped": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/@angular/tsc-wrapped/-/tsc-wrapped-4.3.6.tgz", - "integrity": "sha1-GqZuCrLEeZpK0UtnXhOVOqX81DY=", + "@bazel/ibazel": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@bazel/ibazel/-/ibazel-0.0.1.tgz", + "integrity": "sha1-Ja9CrcbHVNsKxM5I7K3zdCaoQeI=", + "dev": true + }, + "@bazel/typescript": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@bazel/typescript/-/typescript-0.2.2.tgz", + "integrity": "sha1-Cg0JkkUIyUbeMuas2ytcwe4yxQY=", "dev": true, "requires": { - "tsickle": "0.21.6" + "@types/node": "7.0.18", + "@types/source-map": "0.5.2", + "protobufjs": "5.0.0", + "tsickle": "0.24.1", + "typescript": "2.4.2" }, "dependencies": { - "tsickle": { - "version": "0.21.6", - "resolved": "https://registry.npmjs.org/tsickle/-/tsickle-0.21.6.tgz", - "integrity": "sha1-U7Abl5xcE/2xOvs/uVgXflmRWI0=", + "@types/node": { + "version": "7.0.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.18.tgz", + "integrity": "sha1-zWfyfT3Az7dG8L3V4IbExdVb4XM=", + "dev": true + }, + "glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", "dev": true, "requires": { - "minimist": "1.2.0", - "mkdirp": "0.5.1", - "source-map": "0.5.6", - "source-map-support": "0.4.15" + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "protobufjs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-5.0.0.tgz", + "integrity": "sha1-QiMGMjPqlqwGPKK1VANSBNtST6E=", + "dev": true, + "requires": { + "ascli": "1.0.1", + "bytebuffer": "5.0.1", + "glob": "5.0.15", + "yargs": "3.32.0" } } } }, + "@firebase/app": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.1.1.tgz", + "integrity": "sha512-LoZj/Igo1PbfSorPRCozTlBhrFvOyxZsiMo2fLyDa2w/gdPbMucpWUi0vIBronR6T/yNJRWdZyyHOMieLtnjcw==", + "dev": true, + "requires": { + "@firebase/util": "0.1.1" + } + }, + "@firebase/auth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-0.2.0.tgz", + "integrity": "sha512-02tAnE1KcorVJRjs+xf0AngsRyYRpdZ9yDVHX+J5x2Z9v4qNpp1FPkx3A8hmqznCAgqc4C0EbaxHg0BcUlQsWw==", + "dev": true + }, + "@firebase/database": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@firebase/database/-/database-0.1.2.tgz", + "integrity": "sha512-60D08yaQJ6RVegxriC/VNpHE2+/4itTtxBbo15P8WkFfnsjjJVK8AkkFiCjOxj6TccpjzoAz69Xe2/p8po2fEg==", + "dev": true, + "requires": { + "@firebase/util": "0.1.1", + "faye-websocket": "0.9.3" + } + }, + "@firebase/firestore": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-0.1.2.tgz", + "integrity": "sha512-VqPLPKiSy0EzsQfA4w0qYT93KgkiL/NAIS6Ibajuuts2wbz2CBgsZM1nPvgJnaQCN8LPAT/Vvn2th7nMozFHag==", + "dev": true, + "requires": { + "@firebase/webchannel-wrapper": "0.2.3" + } + }, + "@firebase/messaging": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.1.2.tgz", + "integrity": "sha512-fZHhO1FO2RHOVg8A0BhwF+Ay0A34AtMEyjo9csht+TQNEaG924Jkf2tUG13nqRda63tmjT5REeWnGv6qAZU4Jw==", + "dev": true, + "requires": { + "@firebase/util": "0.1.1" + } + }, + "@firebase/polyfill": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@firebase/polyfill/-/polyfill-0.1.1.tgz", + "integrity": "sha512-XvZzppOmu5NZvWkvxZuWpD5VeMhyhhlrMvyFAwlOeYrJKcBFkIbmSX46Pe5ttuXYp6EjrVia/Bh1h2h0NYgE7g==", + "dev": true, + "requires": { + "promise-polyfill": "6.0.2" + } + }, + "@firebase/storage": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.1.1.tgz", + "integrity": "sha512-ccnANSW/Sun8/pFssCdOsVQrdyDb/ReKU/P0rbDdU0LvnWkYEckegJrYSoDliHuht19qHSl5C062q82ZxReVRQ==", + "dev": true + }, + "@firebase/util": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@firebase/util/-/util-0.1.1.tgz", + "integrity": "sha512-HtJ3UEecoy2qjoTXwtp0tN4pVR+zH6+pSMtxLHSxgkAAExKsPJUp5W91O6LivjRdiakRwjMZEE9bVPFUCHDhlQ==", + "dev": true + }, + "@firebase/webchannel-wrapper": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-0.2.3.tgz", + "integrity": "sha512-EWSvoDVXmUwP1nP65ob1gEGiJi38Pv1e9dmL1DHBcupmYdST1cbYper6N2EVnCkzMIm2/EfZiGSL1nf8V4uiKg==", + "dev": true + }, "@google-cloud/common": { - "version": "0.13.4", - "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-0.13.4.tgz", - "integrity": "sha1-dbt/YJMc/J2U2gtdQIlQ0Lvw6Xk=", + "version": "0.13.6", + "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-0.13.6.tgz", + "integrity": "sha1-qdjhN7xCmkSrqWif5qDkMxeE+FM=", "dev": true, "requires": { "array-uniq": "1.0.3", "arrify": "1.0.1", "concat-stream": "1.6.0", "create-error-class": "3.0.2", - "duplexify": "3.5.0", + "duplexify": "3.5.1", "ent": "2.2.0", "extend": "3.0.1", - "google-auto-auth": "0.7.1", + "google-auto-auth": "0.7.2", "is": "3.2.1", "log-driver": "1.2.5", "methmeth": "1.1.0", "modelo": "4.2.0", - "request": "2.81.0", - "retry-request": "2.0.5", + "request": "2.83.0", + "retry-request": "3.1.0", "split-array-stream": "1.0.3", "stream-events": "1.0.2", "string-format-obj": "1.1.0", @@ -150,55 +330,54 @@ } }, "@google-cloud/functions-emulator": { - "version": "1.0.0-alpha.21", - "resolved": "https://registry.npmjs.org/@google-cloud/functions-emulator/-/functions-emulator-1.0.0-alpha.21.tgz", - "integrity": "sha512-ZLCd89mo4hV4U6O1Fdcy2h9AgaY+GGAEZck3jNcFAKP70pR3+NIMPGsA5YSS4lTrS7zJBe0zaHJs1Z59S90oPA==", + "version": "1.0.0-alpha.25", + "resolved": "https://registry.npmjs.org/@google-cloud/functions-emulator/-/functions-emulator-1.0.0-alpha.25.tgz", + "integrity": "sha512-f1rn5SxjvUYeh/jibc4ccFFWgNK6qaE+eiFafgW/MlAsuGB+8DPYYIZ56ags80qtfCdL3j/ny40vmT1unO49HA==", "dev": true, "optional": true, "requires": { - "@google-cloud/storage": "1.1.1", + "@google-cloud/storage": "1.2.1", "adm-zip": "0.4.7", - "ajv": "5.1.6", + "ajv": "5.2.2", "body-parser": "1.17.2", "cli-table2": "0.2.0", "colors": "1.1.2", - "configstore": "3.1.0", - "express": "4.15.3", - "google-proto-files": "0.12.0", - "googleapis": "19.0.0", - "got": "7.0.0", - "grpc": "1.3.8", + "configstore": "3.1.1", + "express": "4.15.4", + "google-proto-files": "0.12.1", + "googleapis": "20.1.0", + "got": "7.1.0", + "grpc": "1.4.1", "http-proxy": "1.16.2", "lodash": "4.17.4", "prompt": "1.0.0", "rimraf": "2.6.1", - "semver": "5.3.0", + "semver": "5.4.1", "serializerr": "1.0.3", - "supertest": "3.0.0", - "tmp": "0.0.31", - "uuid": "3.0.1", + "tmp": "0.0.33", + "uuid": "3.1.0", "winston": "2.3.1", "yargs": "8.0.2" }, "dependencies": { "@google-cloud/storage": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-1.1.1.tgz", - "integrity": "sha1-ZZC1zm53lVbJzHBDvWRJ1rwHgd4=", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-1.2.1.tgz", + "integrity": "sha1-oPLiCHG4YvDqZKkKxI/AiEXPlQU=", "dev": true, "optional": true, "requires": { - "@google-cloud/common": "0.13.4", + "@google-cloud/common": "0.13.6", "arrify": "1.0.1", "async": "2.5.0", "concat-stream": "1.6.0", "create-error-class": "3.0.2", - "duplexify": "3.5.0", + "duplexify": "3.5.1", "extend": "3.0.1", - "gcs-resumable-upload": "0.7.7", + "gcs-resumable-upload": "0.8.2", "hash-stream-validation": "0.2.1", "is": "3.2.1", - "mime-types": "2.1.15", + "mime-types": "2.1.17", "once": "1.4.0", "pumpify": "1.3.5", "stream-events": "1.0.2", @@ -207,13 +386,14 @@ } }, "ajv": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.1.6.tgz", - "integrity": "sha1-Sy8aGd7Ok9V6whYDfj6XkcfdFWQ=", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.2.2.tgz", + "integrity": "sha1-R8aNaehvXZUxA7AHSpQw3GPaXjk=", "dev": true, "optional": true, "requires": { "co": "4.6.0", + "fast-deep-equal": "1.0.0", "json-schema-traverse": "0.3.1", "json-stable-stringify": "1.0.1" } @@ -232,39 +412,6 @@ "dev": true, "optional": true }, - "caseless": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", - "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=", - "dev": true, - "optional": true - }, - "cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", - "dev": true, - "optional": true, - "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wrap-ansi": "2.1.0" - }, - "dependencies": { - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "optional": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - } - } - }, "colors": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", @@ -272,245 +419,87 @@ "dev": true, "optional": true }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "optional": true, - "requires": { - "locate-path": "2.0.0" - } - }, - "gcp-metadata": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-0.1.0.tgz", - "integrity": "sha1-q+IfHqMk3Qs0o/BsqBdj+x7uN9k=", - "dev": true, - "optional": true, - "requires": { - "extend": "3.0.1", - "retry-request": "1.3.2" - } - }, - "gcs-resumable-upload": { - "version": "0.7.7", - "resolved": "https://registry.npmjs.org/gcs-resumable-upload/-/gcs-resumable-upload-0.7.7.tgz", - "integrity": "sha1-2clyWvlwu8hsvwr+8kBtwizpGGQ=", - "dev": true, - "optional": true, - "requires": { - "buffer-equal": "1.0.0", - "configstore": "3.1.0", - "google-auto-auth": "0.6.1", - "pumpify": "1.3.5", - "request": "2.81.0", - "stream-events": "1.0.2", - "through2": "2.0.3" - } - }, - "google-auto-auth": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/google-auto-auth/-/google-auto-auth-0.6.1.tgz", - "integrity": "sha1-wF2CDpRUc57PKKiJLuqz0WJPLLM=", - "dev": true, - "optional": true, - "requires": { - "async": "2.5.0", - "gcp-metadata": "0.1.0", - "google-auth-library": "0.10.0", - "object-assign": "3.0.0", - "request": "2.81.0" - } - }, - "har-validator": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz", - "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=", - "dev": true, - "optional": true, - "requires": { - "chalk": "1.1.3", - "commander": "2.11.0", - "is-my-json-valid": "2.16.0", - "pinkie-promise": "2.0.1" - } - }, - "load-json-file": { + "is-fullwidth-code-point": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true, - "optional": true, - "requires": { - "graceful-fs": "4.1.11", - "parse-json": "2.2.0", - "pify": "2.3.0", - "strip-bom": "3.0.0" - } + "optional": true }, "os-locale": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.0.0.tgz", - "integrity": "sha1-FZGN7VEFIrge565aMJ1U9jn8OaQ=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", + "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", "dev": true, "optional": true, "requires": { - "execa": "0.5.1", + "execa": "0.7.0", "lcid": "1.0.0", "mem": "1.1.0" } }, - "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "optional": true, "requires": { - "pify": "2.3.0" + "is-fullwidth-code-point": "2.0.0", + "strip-ansi": "4.0.0" } }, - "qs": { - "version": "6.3.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.3.2.tgz", - "integrity": "sha1-51vV9uJoEioqDgvaYwslUMFmUCw=", - "dev": true, - "optional": true - }, - "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "optional": true, "requires": { - "load-json-file": "2.0.0", - "normalize-package-data": "2.4.0", - "path-type": "2.0.0" + "ansi-regex": "3.0.0" } }, - "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "dev": true, "optional": true, "requires": { - "find-up": "2.1.0", - "read-pkg": "2.0.0" + "os-tmpdir": "1.0.2" } }, - "retry-request": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-1.3.2.tgz", - "integrity": "sha1-Wa0k5x+K4/MS1fe0vPRnpeWle9Y=", + "winston": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/winston/-/winston-2.3.1.tgz", + "integrity": "sha1-C0hCDZeMAYBM8CMLZIhhWYIloRk=", "dev": true, "optional": true, "requires": { - "request": "2.76.0", - "through2": "2.0.3" + "async": "1.0.0", + "colors": "1.0.3", + "cycle": "1.0.3", + "eyes": "0.1.8", + "isstream": "0.1.2", + "stack-trace": "0.0.10" }, "dependencies": { - "node-uuid": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", - "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=", + "async": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", + "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=", "dev": true, "optional": true }, - "request": { - "version": "2.76.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.76.0.tgz", - "integrity": "sha1-vkRQWv73A2CgQ2lVEGvjlF2VVg4=", - "dev": true, - "optional": true, - "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.6.0", - "caseless": "0.11.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.1.4", - "har-validator": "2.0.6", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.15", - "node-uuid": "1.4.8", - "oauth-sign": "0.8.2", - "qs": "6.3.2", - "stringstream": "0.0.5", - "tough-cookie": "2.3.2", - "tunnel-agent": "0.4.3" - } - } - } - }, - "string-width": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.0.tgz", - "integrity": "sha1-AwZkVh/BRslCPsfZeP4kV0N/5tA=", - "dev": true, - "optional": true, - "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" - }, - "dependencies": { - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "colors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", "dev": true, "optional": true - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "optional": true, - "requires": { - "ansi-regex": "3.0.0" - } } } }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true, - "optional": true - }, - "tmp": { - "version": "0.0.31", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.31.tgz", - "integrity": "sha1-jzirlDjhcxXl29izZX6L+yd65Kc=", - "dev": true, - "optional": true, - "requires": { - "os-tmpdir": "1.0.2" - } - }, - "tunnel-agent": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", - "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=", - "dev": true, - "optional": true - }, - "uuid": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz", - "integrity": "sha1-ZUS7ot/ajBzxfmKaOjBeK7H+5sE=", - "dev": true, - "optional": true - }, "yargs": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-8.0.2.tgz", @@ -522,12 +511,12 @@ "cliui": "3.2.0", "decamelize": "1.2.0", "get-caller-file": "1.0.2", - "os-locale": "2.0.0", + "os-locale": "2.1.0", "read-pkg-up": "2.0.0", "require-directory": "2.1.1", "require-main-filename": "1.0.1", "set-blocking": "2.0.0", - "string-width": "2.1.0", + "string-width": "2.1.1", "which-module": "2.0.0", "y18n": "3.2.1", "yargs-parser": "7.0.0" @@ -536,24 +525,26 @@ } }, "@google-cloud/storage": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-1.2.0.tgz", - "integrity": "sha1-dO6TaJbaXkU8NBzygEQ0LBqazrc=", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-1.4.0.tgz", + "integrity": "sha512-vt1NU7D12OGYPhWfwBD1Q2qFS6Suykorlp3NLaES2W9CW6sEBWLwScxElXt8nPvonYBCFt99jP4g1AqY+0hefw==", "dev": true, "requires": { - "@google-cloud/common": "0.13.4", + "@google-cloud/common": "0.13.6", "arrify": "1.0.1", "async": "2.5.0", "concat-stream": "1.6.0", "create-error-class": "3.0.2", - "duplexify": "3.5.0", + "duplexify": "3.5.1", "extend": "3.0.1", - "gcs-resumable-upload": "0.8.0", + "gcs-resumable-upload": "0.8.2", "hash-stream-validation": "0.2.1", "is": "3.2.1", - "mime-types": "2.1.15", + "mime-types": "2.1.17", "once": "1.4.0", "pumpify": "1.3.5", + "safe-buffer": "5.1.1", + "snakeize": "0.1.0", "stream-events": "1.0.2", "string-format-obj": "1.1.0", "through2": "2.0.3" @@ -566,22 +557,22 @@ "dev": true }, "@types/fs-extra": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-3.0.3.tgz", - "integrity": "sha512-o2qkg/J2LWK+sr007+KFBBOrxzxpr9kiP0gMFC75gQJXhUn/E3pQA0kSVdxrQ3lf+rOwsRnuH0wnR5MNTotEKg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-4.0.3.tgz", + "integrity": "sha512-cUE7dc4RJsRPCk8mbrgMAaglugcJbf1Oxp7DYi/aOj4+ggCxzddDQFZwCKWnqrLv4LJ89apyNJ7Y3pN79tAPVg==", "dev": true, "requires": { - "@types/node": "7.0.34" + "@types/node": "7.0.46" } }, "@types/glob": { - "version": "5.0.30", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-5.0.30.tgz", - "integrity": "sha1-ECZAnFYlqGiQdGAoCNCCsoZ7ilE=", + "version": "5.0.33", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-5.0.33.tgz", + "integrity": "sha512-BcD4yyWz+qmCggaYMSFF0Xn7GkO6tgwm3Fh9Gxk/kQmEU3Z7flQTnVlMyKBUNvXXNTCCyjqK4XT4/2hLd1gQ2A==", "dev": true, "requires": { - "@types/minimatch": "2.0.29", - "@types/node": "7.0.34" + "@types/minimatch": "3.0.1", + "@types/node": "7.0.46" } }, "@types/gulp": { @@ -590,21 +581,21 @@ "integrity": "sha1-g8WcaBzCM9Hsf4LSaVVVZvoTMVY=", "dev": true, "requires": { - "@types/node": "7.0.34", + "@types/node": "7.0.46", "@types/orchestrator": "0.3.0", - "@types/vinyl": "2.0.0" + "@types/vinyl": "2.0.1" } }, "@types/hammerjs": { - "version": "2.0.34", - "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.34.tgz", - "integrity": "sha1-nLrE9BywOUNhQXheG+ULOrEKBKk=", + "version": "2.0.35", + "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.35.tgz", + "integrity": "sha512-4mUIMSZ2U4UOWq1b+iV7XUTE4w+Kr3x+Zb/Qz5ROO6BTZLw2c8/ftjq0aRgluguLs4KRuBnrOy/s389HVn1/zA==", "dev": true }, "@types/jasmine": { - "version": "2.5.45", - "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.5.45.tgz", - "integrity": "sha1-WJKKYh0BTOarWcWpxBBx9zKLDKk=", + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.6.3.tgz", + "integrity": "sha512-2dJf9//QxTnFBXHsCqLbB55jlMreJQie9HhgsZrSgHveOKCWVmgcXgRyvIi22Ndk/dJ/e2srLOstk2NgVbb7XA==", "dev": true }, "@types/merge2": { @@ -613,13 +604,13 @@ "integrity": "sha1-njnQT2/k82+nR3VmytH6+AsqZx8=", "dev": true, "requires": { - "@types/node": "7.0.34" + "@types/node": "7.0.46" } }, "@types/minimatch": { - "version": "2.0.29", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-2.0.29.tgz", - "integrity": "sha1-UALhT3Xi1x5WQoHfBDHIwbSio2o=", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.1.tgz", + "integrity": "sha512-rUO/jz10KRSyA9SHoCWQ8WX9BICyj5jZYu1/ucKEJKb4KzLZCKMURdYbadP157Q6Zl1x0vHsrU+Z/O0XlhYQDw==", "dev": true }, "@types/minimist": { @@ -629,9 +620,9 @@ "dev": true }, "@types/node": { - "version": "7.0.34", - "resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.34.tgz", - "integrity": "sha512-99ujivDq9tqw3b88xrWqUcHfY3XT+moVhAlMqlN+OdavTxfCRW2X1bRBFcloILRJiIoir+gG3I65jzrpNgF/3g==", + "version": "7.0.46", + "resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.46.tgz", + "integrity": "sha512-u+JAi1KtmaUoU/EHJkxoiuvzyo91FCE41Z9TZWWcOUU3P8oUdlDLdrGzCGWySPgbRMD17B0B+1aaJLYI9egQ6A==", "dev": true }, "@types/orchestrator": { @@ -640,14 +631,14 @@ "integrity": "sha1-v4ShaZyTMNT+ic2BJj6PwJ+zKXg=", "dev": true, "requires": { - "@types/node": "7.0.34", - "@types/q": "0.0.35" + "@types/node": "7.0.46", + "@types/q": "0.0.37" } }, "@types/q": { - "version": "0.0.35", - "resolved": "https://registry.npmjs.org/@types/q/-/q-0.0.35.tgz", - "integrity": "sha512-Br6c/XFnnYBSOGKadEaXruA71rpUMxMzlSrGmhpoIEWrqRMI/yQjrY+0ORJi52m/JgkxRqatdBJ32fQ/95qtbw==", + "version": "0.0.37", + "resolved": "https://registry.npmjs.org/@types/q/-/q-0.0.37.tgz", + "integrity": "sha512-vjFGX1zMTMz/kUp3xgfJcxMVLkMWVMrdlyc0RwVyve1y9jxwqNaT8wTcv6M51ylq2a/zn5lm8g7qPSoIS4uvZQ==", "dev": true }, "@types/run-sequence": { @@ -657,7 +648,7 @@ "dev": true, "requires": { "@types/gulp": "3.8.32", - "@types/node": "7.0.34" + "@types/node": "7.0.46" } }, "@types/selenium-webdriver": { @@ -666,34 +657,56 @@ "integrity": "sha1-dMt3+2BS7a/yqJhN2v2I1BnyXKw=", "dev": true }, + "@types/source-map": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@types/source-map/-/source-map-0.5.2.tgz", + "integrity": "sha512-++w4WmMbk3dS3UeHGzAG+xJOSz5Xqtjys/TBkqG3qp3SeWE7Wwezqe5eB7B51cxUyh4PW7bwVotpsLdBK0D8cw==", + "dev": true + }, "@types/vinyl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/vinyl/-/vinyl-2.0.0.tgz", - "integrity": "sha1-/SE79/QTbd4h/hiVUAsSwYb4wmg=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/vinyl/-/vinyl-2.0.1.tgz", + "integrity": "sha512-Joudabfn2ZofU2usW04y8OLmN75u7ZQkW0MCT3AnoBf5oUBp5iQ3Pgfz9+y1RdWkzhCPZo9/wBJ7FMWW2JrY0g==", "dev": true, "requires": { - "@types/node": "7.0.34" + "@types/node": "7.0.46" } }, + "JSONStream": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz", + "integrity": "sha1-cH92HgHa6eFvG8+TcDt4xwlmV5o=", + "dev": true, + "requires": { + "jsonparse": "1.3.1", + "through": "2.3.8" + } + }, + "a-sync-waterfall": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/a-sync-waterfall/-/a-sync-waterfall-1.0.0.tgz", + "integrity": "sha1-OOgxnXk3niRiiEW1O5ZyKyng5Hw=", + "dev": true + }, "abab": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/abab/-/abab-1.0.3.tgz", - "integrity": "sha1-uB3l9ydOxOdW15fNg08wNkJyTl0=", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/abab/-/abab-1.0.4.tgz", + "integrity": "sha1-X6rZwsB/YN12dw9xzwJbYqY8/U4=", "dev": true }, "abbrev": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.0.tgz", - "integrity": "sha1-0FVMIlZjbi9W58LlrRg/hZQo2B8=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true }, "accepts": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", - "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz", + "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=", "dev": true, "requires": { - "mime-types": "2.1.15", + "mime-types": "2.1.17", "negotiator": "0.6.1" } }, @@ -749,15 +762,23 @@ } }, "ajv": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", - "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.2.3.tgz", + "integrity": "sha1-wG9Zh3jETGsWGrr+NGa4GtGBTtI=", "dev": true, "requires": { "co": "4.6.0", + "fast-deep-equal": "1.0.0", + "json-schema-traverse": "0.3.1", "json-stable-stringify": "1.0.1" } }, + "ajv-keywords": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.0.tgz", + "integrity": "sha1-opbhf3v658HOT34N5T0pyzIWLfA=", + "dev": true + }, "align-text": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", @@ -809,13 +830,13 @@ "dev": true }, "anymatch": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.0.tgz", - "integrity": "sha1-o+Uvo5FoyCX/V7AkgSbOWo/5VQc=", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", + "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", "dev": true, "requires": { - "arrify": "1.0.1", - "micromatch": "2.3.11" + "micromatch": "2.3.11", + "normalize-path": "2.1.1" } }, "app-module-path": { @@ -825,9 +846,9 @@ "dev": true }, "aproba": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.1.2.tgz", - "integrity": "sha512-ZpYajIfO0j2cOFTO955KUMIKNmj6zhX8kVztMAxFsDaMwz+9Z9SV0uou2pC9HJqcfpffOsjnbrDMvkNy+9RXPw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true }, "archiver": { @@ -1073,7 +1094,6 @@ "resolved": "https://registry.npmjs.org/ascli/-/ascli-1.0.1.tgz", "integrity": "sha1-vPpZdKYvGOgcq660lzKrSoj5Brw=", "dev": true, - "optional": true, "requires": { "colour": "0.7.1", "optjs": "3.2.2" @@ -1086,9 +1106,9 @@ "dev": true }, "assert-plus": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", - "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", "dev": true }, "ast-module-types": { @@ -1131,17 +1151,17 @@ "dev": true, "requires": { "browserslist": "1.7.7", - "caniuse-db": "1.0.30000699", + "caniuse-db": "1.0.30000748", "normalize-range": "0.1.2", "num2fraction": "1.2.2", - "postcss": "5.2.17", + "postcss": "5.2.18", "postcss-value-parser": "3.3.0" } }, "aws-sign2": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", - "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", "dev": true }, "aws4": { @@ -1151,18 +1171,18 @@ "dev": true }, "axe-core": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-2.3.1.tgz", - "integrity": "sha512-7tp/4CaH1pJNANRj6ULjxND2FBK34stXC4+0WMGu1OOLVPTBj4rLreEb8dBVLh4J5c4BNzU+X3OSmDwRhDxyfg==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-2.4.2.tgz", + "integrity": "sha512-RyAyvCYPJ5y/Ov2mY67kuZG8SfxIAOAI0KuJ9nRXX/vDCL/RTcArGgtn5i2+uZjL23GC6eqMaJ6GjUEDEe11tw==", "dev": true }, "axe-webdriverjs": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/axe-webdriverjs/-/axe-webdriverjs-1.1.3.tgz", - "integrity": "sha1-lIqXLk0OJSa0HGCptEBkkyu24G8=", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/axe-webdriverjs/-/axe-webdriverjs-1.1.5.tgz", + "integrity": "sha512-Nsb/6gyIGYFmQbo3NP14MZghNQff5m+v8D4ex4baad6DvtFzg6T843WZvbdb5F9oHCUWnXy5cgw4LhEpj4ffww==", "dev": true, "requires": { - "axe-core": "2.3.1" + "axe-core": "2.4.2" } }, "babel-code-frame": { @@ -1174,12 +1194,43 @@ "chalk": "1.1.3", "esutils": "2.0.2", "js-tokens": "3.0.2" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "dev": true, + "requires": { + "core-js": "2.5.1", + "regenerator-runtime": "0.11.0" } }, "babylon": { - "version": "6.17.4", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.17.4.tgz", - "integrity": "sha512-kChlV+0SXkjE0vUn9OZ7pBMWRFd8uq3mZe8x1K6jhuNcAFAtEnjchFAqB+dYEXKyd+JpT6eppRR78QAr5gTsUw==", + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", "dev": true }, "backo2": { @@ -1219,10 +1270,13 @@ "dev": true }, "basic-auth": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-1.1.0.tgz", - "integrity": "sha1-RSIe5Cn37h5QNb4/UVM/HN/SmIQ=", - "dev": true + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.0.tgz", + "integrity": "sha1-AV2z81PgLlY3d1X5YnQuiYHnu7o=", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } }, "basic-auth-connect": { "version": "1.0.0", @@ -1272,9 +1326,9 @@ } }, "binary-extensions": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.8.0.tgz", - "integrity": "sha1-SOyNFt9Dd+rl+liEaCSAr02Vx3Q=", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.10.0.tgz", + "integrity": "sha1-muuabF6IY4qtFx4Wf1kAq+JINdA=", "dev": true }, "bl": { @@ -1311,9 +1365,9 @@ } }, "bluebird": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.0.tgz", - "integrity": "sha1-eRQg1/VR7qKJdFOop3ZT+WYG1nw=", + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", + "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==", "dev": true }, "body-parser": { @@ -1323,24 +1377,32 @@ "dev": true, "requires": { "bytes": "2.4.0", - "content-type": "1.0.2", + "content-type": "1.0.4", "debug": "2.6.7", - "depd": "1.1.0", - "http-errors": "1.6.1", + "depd": "1.1.1", + "http-errors": "1.6.2", "iconv-lite": "0.4.15", "on-finished": "2.3.0", "qs": "6.4.0", "raw-body": "2.2.0", "type-is": "1.6.15" + }, + "dependencies": { + "qs": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", + "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=", + "dev": true + } } }, "boom": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", - "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", + "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", "dev": true, "requires": { - "hoek": "2.16.3" + "hoek": "4.2.0" } }, "boxen": { @@ -1360,10 +1422,23 @@ "widest-line": "1.0.0" }, "dependencies": { - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", "dev": true } } @@ -1412,8 +1487,8 @@ "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", "dev": true, "requires": { - "caniuse-db": "1.0.30000699", - "electron-to-chromium": "1.3.15" + "caniuse-db": "1.0.30000748", + "electron-to-chromium": "1.3.27" } }, "browserstack": { @@ -1461,7 +1536,7 @@ "requires": { "cross-spawn-async": "1.0.1", "err-code": "0.1.2", - "q": "1.5.0" + "q": "1.4.1" } }, "buffers": { @@ -1490,7 +1565,6 @@ "resolved": "https://registry.npmjs.org/bytebuffer/-/bytebuffer-5.0.1.tgz", "integrity": "sha1-WC7qSxqHO20CCkjVjfhfC7ps/d0=", "dev": true, - "optional": true, "requires": { "long": "3.2.0" } @@ -1513,7 +1587,7 @@ "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", "dev": true, "requires": { - "no-case": "2.3.1", + "no-case": "2.3.2", "upper-case": "1.1.3" } }, @@ -1534,9 +1608,9 @@ } }, "caniuse-db": { - "version": "1.0.30000699", - "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000699.tgz", - "integrity": "sha1-WvSRqxx3dWGjK0P+JT1qcHHM+Xk=", + "version": "1.0.30000748", + "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000748.tgz", + "integrity": "sha1-eF2e381kW/eVxv887TPEXVgMSKA=", "dev": true }, "canonical-path": { @@ -1558,9 +1632,9 @@ "dev": true }, "catharsis": { - "version": "0.8.8", - "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.8.tgz", - "integrity": "sha1-aTR59DqsVJ2Aa9c+kkzQ2USVGgY=", + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.9.tgz", + "integrity": "sha1-mMyJDKZS3S7w5ws3klMQ/56Q/Is=", "dev": true, "requires": { "underscore-contrib": "0.3.0" @@ -1596,6 +1670,14 @@ "has-ansi": "2.0.0", "strip-ansi": "3.0.1", "supports-color": "2.0.0" + }, + "dependencies": { + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } } }, "change-case": { @@ -1612,7 +1694,7 @@ "is-upper-case": "1.1.2", "lower-case": "1.1.4", "lower-case-first": "1.0.2", - "no-case": "2.3.1", + "no-case": "2.3.2", "param-case": "2.1.1", "pascal-case": "2.0.1", "path-case": "2.1.1", @@ -1636,7 +1718,7 @@ "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", "dev": true, "requires": { - "anymatch": "1.3.0", + "anymatch": "1.3.2", "async-each": "1.0.1", "glob-parent": "2.0.0", "inherits": "2.0.3", @@ -1646,10 +1728,16 @@ "readdirp": "2.1.0" } }, + "chownr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.0.1.tgz", + "integrity": "sha1-4qdQQqlVGQi+vSW4Uj1fl2nXkYE=", + "dev": true + }, "circular-json": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.1.tgz", - "integrity": "sha1-vos2rvzN6LPKeqLWr8B6NyQsDS0=", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", + "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", "dev": true }, "cjson": { @@ -1662,12 +1750,12 @@ } }, "clean-css": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.1.6.tgz", - "integrity": "sha1-Wke+tSaZTLT3vzYYilXtO0VSjws=", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.1.9.tgz", + "integrity": "sha1-Nc7ornaHpJuYA09w3gDE7dOCYwE=", "dev": true, "requires": { - "source-map": "0.5.6" + "source-map": "0.5.7" } }, "cli-boxes": { @@ -1729,34 +1817,26 @@ } }, "cli-width": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.1.0.tgz", - "integrity": "sha1-sjTKIJsp72b8UY2bmNWEewDt8Ao=", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", "dev": true }, "cliui": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", "dev": true, "requires": { - "center-align": "0.1.3", - "right-align": "0.1.3", - "wordwrap": "0.0.2" - }, - "dependencies": { - "wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", - "dev": true - } + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wrap-ansi": "2.1.0" } }, "clone": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.2.tgz", - "integrity": "sha1-Jgt6meux7f4kdTgXX3gyQ8sZ0Uk=", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.1.tgz", + "integrity": "sha1-0hfR6WERjjrJpLi7oyhVU79kfNs=", "dev": true }, "clone-buffer": { @@ -1776,9 +1856,9 @@ } }, "clone-stats": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", - "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", "dev": true }, "cloneable-readable": { @@ -1810,7 +1890,7 @@ "integrity": "sha1-Gsz5fdc5uYO/mU1W/sj5WFNkG3o=", "dev": true, "requires": { - "color-name": "1.1.2" + "color-name": "1.1.3" } }, "color-diff": { @@ -1820,9 +1900,9 @@ "dev": true }, "color-name": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.2.tgz", - "integrity": "sha1-XIq3K2S9IhXWF66VWeuxSEdc+Y0=", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, "colorguard": { @@ -1837,17 +1917,24 @@ "object-assign": "4.1.1", "pipetteur": "2.0.3", "plur": "2.1.2", - "postcss": "5.2.17", + "postcss": "5.2.18", "postcss-reporter": "1.4.1", "text-table": "0.2.0", "yargs": "1.3.3" }, "dependencies": { - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } }, "plur": { "version": "2.1.2", @@ -1855,7 +1942,7 @@ "integrity": "sha1-dIJFLBoPUI4+NE6uwxLJHCncZVo=", "dev": true, "requires": { - "irregular-plurals": "1.3.0" + "irregular-plurals": "1.4.0" } }, "postcss-reporter": { @@ -1867,9 +1954,15 @@ "chalk": "1.1.3", "lodash": "4.17.4", "log-symbols": "1.0.2", - "postcss": "5.2.17" + "postcss": "5.2.18" } }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, "yargs": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/yargs/-/yargs-1.3.3.tgz", @@ -1888,8 +1981,7 @@ "version": "0.7.1", "resolved": "https://registry.npmjs.org/colour/-/colour-0.7.1.tgz", "integrity": "sha1-nLFpkX7F0SwHNtPoaFdG3xyt93g=", - "dev": true, - "optional": true + "dev": true }, "combine-lists": { "version": "1.0.1", @@ -1948,7 +2040,7 @@ "integrity": "sha1-fAp5onu4C2xplERfgpWCWdPQIVM=", "dev": true, "requires": { - "semver": "5.3.0" + "semver": "5.4.1" } }, "component-bind": { @@ -1958,9 +2050,9 @@ "dev": true }, "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.1.2.tgz", + "integrity": "sha1-KWWU8nU9qmOZbSrwjRWpURbJrsM=", "dev": true }, "component-inherit": { @@ -2008,39 +2100,39 @@ } }, "compressible": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.10.tgz", - "integrity": "sha1-/tocf3YXkScyspv4zyYlKiC57s0=", + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.12.tgz", + "integrity": "sha1-xZpcmdt2dn6YdlAOJx72OzSTvWY=", "dev": true, "requires": { - "mime-db": "1.27.0" + "mime-db": "1.30.0" } }, "compression": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.0.tgz", - "integrity": "sha1-AwyfGY8WQ6BX13anOOki2kNzAS0=", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.1.tgz", + "integrity": "sha1-7/JgPvwuIs+G810uuTWJ+YdTc9s=", "dev": true, "requires": { - "accepts": "1.3.3", - "bytes": "2.5.0", - "compressible": "2.0.10", - "debug": "2.6.8", + "accepts": "1.3.4", + "bytes": "3.0.0", + "compressible": "2.0.12", + "debug": "2.6.9", "on-headers": "1.0.1", "safe-buffer": "5.1.1", - "vary": "1.1.1" + "vary": "1.1.2" }, "dependencies": { "bytes": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-2.5.0.tgz", - "integrity": "sha1-TJQj6i0lLCcMQbK97+/5u2tiwGo=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=", "dev": true }, "debug": { - "version": "2.6.8", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", - "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { "ms": "2.0.0" @@ -2066,29 +2158,46 @@ } }, "configstore": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.0.tgz", - "integrity": "sha1-Rd+QcHPibfoc9LLVL1tgVF6qEdE=", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.1.tgz", + "integrity": "sha512-5oNkD/L++l0O6xGXxb1EWS7SivtjfGQlRyxJsYgE0Z495/L81e2h4/d3r969hoPXuFItzNOKMtsXgYG4c7dYvw==", "dev": true, "requires": { - "dot-prop": "4.1.1", + "dot-prop": "4.2.0", "graceful-fs": "4.1.11", "make-dir": "1.0.0", "unique-string": "1.0.0", - "write-file-atomic": "2.1.0", + "write-file-atomic": "2.3.0", "xdg-basedir": "3.0.0" } }, "connect": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/connect/-/connect-3.6.2.tgz", - "integrity": "sha1-aU6NIGgb/kkCgsiriGvpjwn0L+c=", + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.6.5.tgz", + "integrity": "sha1-+43ee6B2OHfQ7J352sC0tA5yx9o=", "dev": true, "requires": { - "debug": "2.6.7", - "finalhandler": "1.0.3", - "parseurl": "1.3.1", - "utils-merge": "1.0.0" + "debug": "2.6.9", + "finalhandler": "1.0.6", + "parseurl": "1.3.2", + "utils-merge": "1.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true + } } }, "connect-livereload": { @@ -2098,18 +2207,18 @@ "dev": true }, "connect-query": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/connect-query/-/connect-query-0.2.0.tgz", - "integrity": "sha1-Iw3knmlQmjFzi/96WzP4eF7O+jo=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/connect-query/-/connect-query-1.0.0.tgz", + "integrity": "sha1-3kT1dyCdokBNH8BGktGkEY5YIRk=", "dev": true, "requires": { - "qs": "1.1.0" + "qs": "6.4.0" }, "dependencies": { "qs": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-1.1.0.tgz", - "integrity": "sha1-KEXNnfRistsoqQNw4ULUksWkXd4=", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", + "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=", "dev": true } } @@ -2142,7 +2251,7 @@ "dev": true, "requires": { "inherits": "2.0.3", - "statuses": "1.3.1" + "statuses": "1.4.0" } }, "ms": { @@ -2177,9 +2286,9 @@ "optional": true }, "content-type": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.2.tgz", - "integrity": "sha1-t9ETrueo3Se9IRM8TcJSnfFyHu0=", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", "dev": true }, "content-type-parser": { @@ -2188,40 +2297,183 @@ "integrity": "sha1-w+VpiMU8ZRJ/tG1AMqOpACRv3JQ=", "dev": true }, - "conventional-changelog-atom": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-atom/-/conventional-changelog-atom-0.1.0.tgz", - "integrity": "sha1-Z6R8ZqQrL4kJ7xWHyZia4d5zC5I=", + "conventional-changelog": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-1.1.6.tgz", + "integrity": "sha512-AaQRALJYQVbfMs0UYJ3jf5yIAJwGnm/E7ETwzZMwF/3JDMyDaa4agLQomz94pcYiGH7zcrxFcwHApSODOYnunA==", "dev": true, "requires": { - "q": "1.5.0" + "conventional-changelog-angular": "1.5.1", + "conventional-changelog-atom": "0.1.1", + "conventional-changelog-codemirror": "0.2.0", + "conventional-changelog-core": "1.9.2", + "conventional-changelog-ember": "0.2.8", + "conventional-changelog-eslint": "0.2.0", + "conventional-changelog-express": "0.2.0", + "conventional-changelog-jquery": "0.1.0", + "conventional-changelog-jscs": "0.1.0", + "conventional-changelog-jshint": "0.2.0" } }, - "conventional-changelog-codemirror": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-codemirror/-/conventional-changelog-codemirror-0.1.0.tgz", - "integrity": "sha1-dXelkdv5tTjnoVCn7mL2WihyszQ=", + "conventional-changelog-angular": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-1.5.1.tgz", + "integrity": "sha512-AnjnPyqHp8yR2IOWsXYOCv6Ly0WC2rLRK04fgAS/5QoA3ovYLSoz9PKB5pcSG3M9lAf40IqZwU3R3G6Hy7XCSA==", "dev": true, "requires": { - "q": "1.5.0" + "compare-func": "1.3.2", + "q": "1.4.1" } }, - "conventional-changelog-eslint": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-eslint/-/conventional-changelog-eslint-0.1.0.tgz", - "integrity": "sha1-pSQR6ZngUBzlALhWsKZD0DMJB+I=", + "conventional-changelog-atom": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/conventional-changelog-atom/-/conventional-changelog-atom-0.1.1.tgz", + "integrity": "sha512-6Nlu/+MiD4gi7k3Z+N1vMJWpaPSdvFPWzPGnH4OXewHAxiAl0L/TT9CGgA01fosPxmYr4hMNtD7kyN0tkg8vIA==", "dev": true, "requires": { - "q": "1.5.0" + "q": "1.4.1" } }, - "conventional-changelog-express": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-express/-/conventional-changelog-express-0.1.0.tgz", - "integrity": "sha1-VcbIQcgRliA2wDe9vZZKVK4xD84=", + "conventional-changelog-codemirror": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-codemirror/-/conventional-changelog-codemirror-0.2.0.tgz", + "integrity": "sha512-jUbY98JoKdAOR5k3pOBiKZ+Iz9t2F84hL7x4WjSRW6x7FdeCEUOjyfml+YClE2h/h62Tf3mwur5jSO8upxxc1g==", + "dev": true, + "requires": { + "q": "1.4.1" + } + }, + "conventional-changelog-core": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-1.9.2.tgz", + "integrity": "sha512-L/boGKXaKWrlCU8bHa1QM36Pb/JopCPmekj5SFqqAuBfjya860xX2fAC5Ggelse++Bw39AZ2NrHwBnJrdwLlLw==", + "dev": true, + "requires": { + "conventional-changelog-writer": "2.0.1", + "conventional-commits-parser": "2.0.0", + "dateformat": "1.0.12", + "get-pkg-repo": "1.4.0", + "git-raw-commits": "1.2.0", + "git-remote-origin-url": "2.0.0", + "git-semver-tags": "1.2.2", + "lodash": "4.17.4", + "normalize-package-data": "2.4.0", + "q": "1.4.1", + "read-pkg": "1.1.0", + "read-pkg-up": "1.0.1", + "through2": "2.0.3" + }, + "dependencies": { + "dateformat": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", + "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", + "dev": true, + "requires": { + "get-stdin": "4.0.1", + "meow": "3.7.0" + } + }, + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "parse-json": "2.2.0", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "strip-bom": "2.0.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "2.0.1" + } + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "1.1.0", + "normalize-package-data": "2.4.0", + "path-type": "1.1.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "1.1.2", + "read-pkg": "1.1.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "0.2.1" + } + } + } + }, + "conventional-changelog-ember": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/conventional-changelog-ember/-/conventional-changelog-ember-0.2.8.tgz", + "integrity": "sha512-smsh0o/S95n22lrQZrSHYjJrxIGoFl+OFHK+q2KGHA2zRFrW7QilYM7VUjgmB+emzwqFguPjrq+D2U8iPhMNJg==", + "dev": true, + "requires": { + "q": "1.4.1" + } + }, + "conventional-changelog-eslint": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-eslint/-/conventional-changelog-eslint-0.2.0.tgz", + "integrity": "sha512-WGKnC0bGPD6BHGiRBfYqNGfy6DZDn2jGs1yxPRT8I2796wYdGqsbDF4477o4fdsxUJvckoW2OFPqkmRMQaCHSA==", + "dev": true, + "requires": { + "q": "1.4.1" + } + }, + "conventional-changelog-express": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-express/-/conventional-changelog-express-0.2.0.tgz", + "integrity": "sha512-ujSEmbWfozC1iIjH5Pl7AKtREowvAl10whs1q6c7nZLnoNZK5CmdB2PQ/V42O6rCgUzaLX+ACRW2+g0A/Htqvw==", "dev": true, "requires": { - "q": "1.5.0" + "q": "1.4.1" } }, "conventional-changelog-jquery": { @@ -2230,7 +2482,7 @@ "integrity": "sha1-Agg5cWLjhGmG5xJztsecW1+A9RA=", "dev": true, "requires": { - "q": "1.5.0" + "q": "1.4.1" } }, "conventional-changelog-jscs": { @@ -2239,35 +2491,56 @@ "integrity": "sha1-BHnrRDzH1yxYvwvPDvHURKkvDlw=", "dev": true, "requires": { - "q": "1.5.0" + "q": "1.4.1" } }, "conventional-changelog-jshint": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-jshint/-/conventional-changelog-jshint-0.1.0.tgz", - "integrity": "sha1-AMq46aMxdIer2UxNhGcTQpGNKgc=", + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/conventional-changelog-jshint/-/conventional-changelog-jshint-0.2.0.tgz", + "integrity": "sha512-uUP4c0et6F2teapl+YY2JHFAHD401U5CkgI+P8PyU0y1zS8BdBy6EnhqgZEXhFOp9fPzUdic+Wv/9alOqw3agQ==", "dev": true, "requires": { "compare-func": "1.3.2", - "q": "1.5.0" + "q": "1.4.1" } }, "conventional-changelog-writer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-1.4.1.tgz", - "integrity": "sha1-P0y00APrtWmJ0w00WJO1KkNjnI4=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-2.0.1.tgz", + "integrity": "sha512-X4qC758celQOKw0iUPAsH5sJX6fH6N5dboFc3elXb1/SIKhsYMukhhaxWmxRdtVUSqGt9rZg8giwBQG5B2GeKg==", "dev": true, "requires": { "compare-func": "1.3.2", "conventional-commits-filter": "1.0.0", "dateformat": "1.0.12", - "handlebars": "4.0.10", + "handlebars": "4.0.11", "json-stringify-safe": "5.0.1", "lodash": "4.17.4", "meow": "3.7.0", - "semver": "5.3.0", - "split": "1.0.0", + "semver": "5.4.1", + "split": "1.0.1", "through2": "2.0.3" + }, + "dependencies": { + "dateformat": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", + "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", + "dev": true, + "requires": { + "get-stdin": "4.0.1", + "meow": "3.7.0" + } + }, + "split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "dev": true, + "requires": { + "through": "2.3.8" + } + } } }, "conventional-commits-filter": { @@ -2281,16 +2554,16 @@ } }, "conventional-commits-parser": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-1.3.0.tgz", - "integrity": "sha1-4ye1MZThp61dxjR57pCZpSsCSGU=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-2.0.0.tgz", + "integrity": "sha512-8od6g684Fhi5Vpp4ABRv/RBsW1AY6wSHbJHEK6FGTv+8jvAAnlABniZu/FVmX9TcirkHepaEsa1QGkRvbg0CKw==", "dev": true, "requires": { - "is-text-path": "1.0.1", "JSONStream": "1.3.1", + "is-text-path": "1.0.1", "lodash": "4.17.4", "meow": "3.7.0", - "split2": "2.1.1", + "split2": "2.2.0", "through2": "2.0.3", "trim-off-newlines": "1.0.1" } @@ -2325,27 +2598,20 @@ "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=", "dev": true }, - "cookiejar": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.1.tgz", - "integrity": "sha1-Qa1XsbVVlR7BcUEqgZQrHoIA00o=", - "dev": true, - "optional": true - }, "copy-props": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-1.6.0.tgz", "integrity": "sha1-8DJLvumXcRAeezraES8xPDk9uO0=", "dev": true, "requires": { - "each-props": "1.3.0", - "is-plain-object": "2.0.3" + "each-props": "1.3.1", + "is-plain-object": "2.0.4" } }, "core-js": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.4.1.tgz", - "integrity": "sha1-TekR5mew6ukSTjQlS1OupvxhjT4=" + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz", + "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs=" }, "core-util-is": { "version": "1.0.2", @@ -2354,26 +2620,18 @@ "dev": true }, "cosmiconfig": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-2.1.3.tgz", - "integrity": "sha1-lSdx6w3dwcs/ovb75RpSLpOz7go=", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-2.2.2.tgz", + "integrity": "sha512-GiNXLwAFPYHy25XmTPpafYvn3CLAkJ8FLsscq78MQd1Kh0OU6Yzhn4eV2MVF4G9WEQZoWEGltatdR+ntGPMl5A==", "dev": true, "requires": { "is-directory": "0.3.1", - "js-yaml": "3.9.0", + "js-yaml": "3.10.0", "minimist": "1.2.0", "object-assign": "4.1.1", "os-homedir": "1.0.2", "parse-json": "2.2.0", "require-from-string": "1.2.1" - }, - "dependencies": { - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - } } }, "crc": { @@ -2434,7 +2692,7 @@ "dev": true, "requires": { "lru-cache": "4.1.1", - "which": "1.2.14" + "which": "1.3.0" } }, "cross-spawn-async": { @@ -2444,7 +2702,7 @@ "dev": true, "requires": { "lru-cache": "2.7.3", - "which": "1.2.14" + "which": "1.3.0" }, "dependencies": { "lru-cache": { @@ -2456,12 +2714,23 @@ } }, "cryptiles": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", - "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", + "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", "dev": true, "requires": { - "boom": "2.10.1" + "boom": "5.2.0" + }, + "dependencies": { + "boom": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", + "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", + "dev": true, + "requires": { + "hoek": "4.2.0" + } + } } }, "crypto-random-string": { @@ -2634,7 +2903,7 @@ "dev": true, "requires": { "inherits": "2.0.3", - "statuses": "1.3.1" + "statuses": "1.4.0" } } } @@ -2707,7 +2976,7 @@ "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", "dev": true, "requires": { - "es5-ext": "0.10.24" + "es5-ext": "0.10.35" } }, "dargs": { @@ -2726,25 +2995,13 @@ "dev": true, "requires": { "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - } } }, "dateformat": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", - "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", - "dev": true, - "requires": { - "get-stdin": "4.0.1", - "meow": "3.7.0" - } + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.2.0.tgz", + "integrity": "sha1-QGXiATz5+5Ft39gu+1Bq1MZ2kGI=", + "dev": true }, "debug": { "version": "2.6.7", @@ -2797,6 +3054,14 @@ "dev": true, "requires": { "clone": "1.0.2" + }, + "dependencies": { + "clone": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.2.tgz", + "integrity": "sha1-Jgt6meux7f4kdTgXX3gyQ8sZ0Uk=", + "dev": true + } } }, "del": { @@ -2812,14 +3077,6 @@ "pify": "2.3.0", "pinkie-promise": "2.0.1", "rimraf": "2.6.1" - }, - "dependencies": { - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - } } }, "delayed-stream": { @@ -2835,9 +3092,9 @@ "dev": true }, "depd": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.0.tgz", - "integrity": "sha1-4b2Cxqq2ztlluXuIsX7T5SjKGMM=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", + "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=", "dev": true }, "dependency-graph": { @@ -2847,38 +3104,15 @@ "dev": true }, "dependency-tree": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/dependency-tree/-/dependency-tree-5.8.0.tgz", - "integrity": "sha1-Cr1a7nibSb5RIaks9ml7t6EzLXA=", + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/dependency-tree/-/dependency-tree-5.11.0.tgz", + "integrity": "sha1-koRk1vknNgfT9muaV+JZ5jVmd1U=", "dev": true, "requires": { - "commander": "2.6.0", - "debug": "2.2.0", - "filing-cabinet": "1.8.0", - "precinct": "3.6.0" - }, - "dependencies": { - "commander": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.6.0.tgz", - "integrity": "sha1-nfflL7Kgyw+4kFjugMMQQiXzfh0=", - "dev": true - }, - "debug": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", - "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", - "dev": true, - "requires": { - "ms": "0.7.1" - } - }, - "ms": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", - "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", - "dev": true - } + "commander": "2.11.0", + "debug": "2.6.7", + "filing-cabinet": "1.12.0", + "precinct": "3.8.0" } }, "deprecated": { @@ -2909,9 +3143,9 @@ "dev": true, "requires": { "ast-module-types": "2.3.2", - "escodegen": "1.8.1", + "escodegen": "1.9.0", "get-amd-module-type": "2.0.5", - "node-source-walk": "3.2.1" + "node-source-walk": "3.3.0" } }, "detective-cjs": { @@ -2921,16 +3155,16 @@ "dev": true, "requires": { "ast-module-types": "2.3.2", - "node-source-walk": "3.2.1" + "node-source-walk": "3.3.0" } }, "detective-es6": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/detective-es6/-/detective-es6-1.1.6.tgz", - "integrity": "sha1-XZFAVm7v8SxU/mqzk2ZQ1DvOsKY=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/detective-es6/-/detective-es6-1.2.0.tgz", + "integrity": "sha1-a5s71Uf9jyH4lQL2JuRe0qMnb9w=", "dev": true, "requires": { - "node-source-walk": "3.2.1" + "node-source-walk": "3.3.0" } }, "detective-less": { @@ -2941,7 +3175,7 @@ "requires": { "debug": "2.2.0", "gonzales-pe": "3.4.7", - "node-source-walk": "3.2.1" + "node-source-walk": "3.3.0" }, "dependencies": { "debug": { @@ -2962,58 +3196,46 @@ } }, "detective-sass": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/detective-sass/-/detective-sass-2.0.0.tgz", - "integrity": "sha1-CvGKxjnIw26dN9sadOK/hJ8KVI4=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/detective-sass/-/detective-sass-2.0.1.tgz", + "integrity": "sha1-BWYKoblc/Yf1dGQ7+s4+iiaBEqE=", "dev": true, "requires": { - "debug": "2.2.0", + "debug": "3.1.0", "gonzales-pe": "3.4.7", - "node-source-walk": "3.2.1" + "node-source-walk": "3.3.0" }, "dependencies": { "debug": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", - "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { - "ms": "0.7.1" + "ms": "2.0.0" } - }, - "ms": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", - "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", - "dev": true } } }, "detective-scss": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/detective-scss/-/detective-scss-1.0.0.tgz", - "integrity": "sha1-owEFIV5fy8JhMuPGuVyNgpkYWJE=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/detective-scss/-/detective-scss-1.0.1.tgz", + "integrity": "sha1-dDJGoN01jZ2R/0ElQX9qd/vPJw8=", "dev": true, "requires": { - "debug": "2.2.0", + "debug": "3.1.0", "gonzales-pe": "3.4.7", - "node-source-walk": "3.2.1" + "node-source-walk": "3.3.0" }, "dependencies": { "debug": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", - "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { - "ms": "0.7.1" + "ms": "2.0.0" } - }, - "ms": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", - "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", - "dev": true } } }, @@ -3023,6 +3245,43 @@ "integrity": "sha1-UK7n24uruZA4HwEMY/q7pbWOVM0=", "dev": true }, + "detective-typescript": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/detective-typescript/-/detective-typescript-1.0.1.tgz", + "integrity": "sha512-4DZpsap+dVzQblcL/ffVXtaXtA7byrP14XQnvyAvwo8+z7/C0OrwLNQhyiC6cLZlzy2T8QuPc+mDsYsa4lAxHw==", + "dev": true, + "requires": { + "node-source-walk": "3.2.0", + "typescript": "2.0.10", + "typescript-eslint-parser": "1.0.2" + }, + "dependencies": { + "babylon": { + "version": "6.8.4", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.8.4.tgz", + "integrity": "sha1-CXMGuNq66VFZIlzymz6lWRIFMYA=", + "dev": true, + "requires": { + "babel-runtime": "6.26.0" + } + }, + "node-source-walk": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/node-source-walk/-/node-source-walk-3.2.0.tgz", + "integrity": "sha1-PGBcxTq97ktFq2XpR9+x23yQ8OM=", + "dev": true, + "requires": { + "babylon": "6.8.4" + } + }, + "typescript": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.0.10.tgz", + "integrity": "sha1-zN1O2G/VVQpAcQGggUAS4bP6w90=", + "dev": true + } + } + }, "dgeni": { "version": "0.4.9", "resolved": "https://registry.npmjs.org/dgeni/-/dgeni-0.4.9.tgz", @@ -3037,7 +3296,7 @@ "optimist": "0.6.1", "q": "1.4.1", "validate.js": "0.9.0", - "winston": "2.3.1" + "winston": "2.4.0" }, "dependencies": { "lodash": { @@ -3045,23 +3304,17 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", "dev": true - }, - "q": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", - "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=", - "dev": true } } }, "dgeni-packages": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/dgeni-packages/-/dgeni-packages-0.19.1.tgz", - "integrity": "sha1-e5ZUXQo1FRycKwolEz3ggQkMfKA=", + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/dgeni-packages/-/dgeni-packages-0.22.0.tgz", + "integrity": "sha1-ftB6+QdPZUeEclbBpltIiloXrQM=", "dev": true, "requires": { "canonical-path": "0.0.2", - "catharsis": "0.8.8", + "catharsis": "0.8.9", "change-case": "3.0.0", "dgeni": "0.4.9", "espree": "2.2.5", @@ -3074,12 +3327,21 @@ "mkdirp": "0.5.1", "mkdirp-promise": "5.0.1", "node-html-encoder": "0.0.2", - "nunjucks": "2.5.2", - "semver": "5.3.0", + "nunjucks": "3.0.1", + "semver": "5.4.1", "shelljs": "0.7.8", + "source-map-support": "0.4.18", "spdx-license-list": "2.1.0", "stringmap": "0.2.2", - "typescript": "2.2.2" + "typescript": "2.4.2" + }, + "dependencies": { + "typescript": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.4.2.tgz", + "integrity": "sha1-+DlfhdRZJ2BnyYiqQYN6j4KHCEQ=", + "dev": true + } } }, "di": { @@ -3095,9 +3357,9 @@ "dev": true }, "diff": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.0.tgz", - "integrity": "sha512-w0XZubFWn0Adlsapj9EAWX0FqWdO4tz8kc3RiYdWLh4k/V8PTb6i0SMgXt0vRM3zyKnT8tKO7mUlieRQHIjMNg==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.4.0.tgz", + "integrity": "sha512-QpVuMTEoJMF7cKzi6bvWhRulU1fZqZnvyVQgNhPaxxuTYwyjn/j1v9falseQ/uXWwPnO56RBfwtg4h/EQXmucA==", "dev": true }, "doiuse": { @@ -3107,17 +3369,17 @@ "dev": true, "requires": { "browserslist": "1.7.7", - "caniuse-db": "1.0.30000699", + "caniuse-db": "1.0.30000748", "css-rule-stream": "1.1.0", "duplexer2": "0.0.2", "jsonfilter": "1.1.2", "ldjson-stream": "1.2.1", "lodash": "4.17.4", "multimatch": "2.1.0", - "postcss": "5.2.17", + "postcss": "5.2.18", "source-map": "0.4.4", "through2": "0.6.5", - "yargs": "3.10.0" + "yargs": "3.32.0" }, "dependencies": { "duplexer2": { @@ -3233,6 +3495,12 @@ "domelementtype": "1.3.0" } }, + "domino": { + "version": "1.0.30", + "resolved": "https://registry.npmjs.org/domino/-/domino-1.0.30.tgz", + "integrity": "sha512-ikq8WiDSkICdkElud317F2Sigc6A3EDpWsxWBwIZqOl95km4p/Vc9Rj98id7qKgsjDmExj0AVM7JOd4bb647Xg==", + "dev": true + }, "domutils": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.6.2.tgz", @@ -3249,13 +3517,13 @@ "integrity": "sha1-NNzzf1Co6TwrO8qLt/uRVcfaO+4=", "dev": true, "requires": { - "no-case": "2.3.1" + "no-case": "2.3.2" } }, "dot-prop": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.1.1.tgz", - "integrity": "sha1-qEk/C3te7sglJbXHWH+n3nyoWcE=", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", + "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", "dev": true, "requires": { "is-obj": "1.0.1" @@ -3284,33 +3552,25 @@ "optional": true }, "duplexify": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.5.0.tgz", - "integrity": "sha1-GqdzAC4VeEV+nZ1KULDMquvL1gQ=", + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.5.1.tgz", + "integrity": "sha512-j5goxHTwVED1Fpe5hh3q9R93Kip0Bg2KVAt4f8CEYM3UEwYcPSvWbXaUQOzdX/HtiNomipv+gU7ASQPDbV7pGQ==", "dev": true, "requires": { - "end-of-stream": "1.0.0", + "end-of-stream": "1.4.0", "inherits": "2.0.3", "readable-stream": "2.3.3", "stream-shift": "1.0.0" } }, "each-props": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.0.tgz", - "integrity": "sha1-ftgDHJJ2iK7bSoluuRSFtEh7kOo=", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.1.tgz", + "integrity": "sha1-/BOPUeOid0KG1IWOAtbn3kYt4Vg=", "dev": true, "requires": { - "is-plain-object": "2.0.3", - "object-assign": "4.1.1" - }, - "dependencies": { - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - } + "is-plain-object": "2.0.4", + "object.defaults": "1.1.0" } }, "ecc-jsbn": { @@ -3340,9 +3600,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.3.15", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.15.tgz", - "integrity": "sha1-CDl5NIkcvPrrvRi4KpW1pIETg2k=", + "version": "1.3.27", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.27.tgz", + "integrity": "sha1-eOy4o5kGYYe7N07t412ccFZagD0=", "dev": true }, "encodeurl": { @@ -3352,23 +3612,12 @@ "dev": true }, "end-of-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.0.0.tgz", - "integrity": "sha1-1FlucCc0qT5A6a+GQxnqvZn/Lw4=", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.0.tgz", + "integrity": "sha1-epDYM+/abPpurA9JSduw+tOmMgY=", "dev": true, "requires": { - "once": "1.3.3" - }, - "dependencies": { - "once": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/once/-/once-1.3.3.tgz", - "integrity": "sha1-suJhVXzkwxTsgwTz+oJmPkKXyiA=", - "dev": true, - "requires": { - "wrappy": "1.0.2" - } - } + "once": "1.4.0" } }, "engine.io": { @@ -3385,6 +3634,16 @@ "ws": "1.1.2" }, "dependencies": { + "accepts": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.3.tgz", + "integrity": "sha1-w8p0NJOGSMPg2cHjKN1otiLChMo=", + "dev": true, + "requires": { + "mime-types": "2.1.17", + "negotiator": "0.6.1" + } + }, "debug": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", @@ -3422,6 +3681,12 @@ "yeast": "0.1.2" }, "dependencies": { + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, "debug": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", @@ -3462,15 +3727,7 @@ "graceful-fs": "4.1.11", "memory-fs": "0.4.1", "object-assign": "4.1.1", - "tapable": "0.2.6" - }, - "dependencies": { - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - } + "tapable": "0.2.8" } }, "ent": { @@ -3515,35 +3772,35 @@ "integrity": "sha1-t7cO2PNZ6duICS8tIMD4MUIK2D8=", "dev": true, "requires": { - "accepts": "1.3.3", + "accepts": "1.3.4", "escape-html": "1.0.3" } }, "es5-ext": { - "version": "0.10.24", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.24.tgz", - "integrity": "sha1-pVh3yZJLwMjZvTwsvhdJWsFwmxQ=", + "version": "0.10.35", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.35.tgz", + "integrity": "sha1-GO6FjOajxFx9eekcFfzKnsVoSU8=", "dev": true, "requires": { - "es6-iterator": "2.0.1", + "es6-iterator": "2.0.3", "es6-symbol": "3.1.1" } }, "es6-iterator": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.1.tgz", - "integrity": "sha1-jjGcnwRTv1ddN0lAplWSDlnKVRI=", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", "dev": true, "requires": { "d": "1.0.0", - "es5-ext": "0.10.24", + "es5-ext": "0.10.35", "es6-symbol": "3.1.1" } }, "es6-promise": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", - "integrity": "sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.0.2.tgz", + "integrity": "sha1-AQ1YWEI6XxGJeWZfRkhqlcbuK7Y=", "dev": true }, "es6-set": { @@ -3553,8 +3810,8 @@ "dev": true, "requires": { "d": "1.0.0", - "es5-ext": "0.10.24", - "es6-iterator": "2.0.1", + "es5-ext": "0.10.35", + "es6-iterator": "2.0.3", "es6-symbol": "3.1.1", "event-emitter": "0.3.5" } @@ -3566,7 +3823,7 @@ "dev": true, "requires": { "d": "1.0.0", - "es5-ext": "0.10.24" + "es5-ext": "0.10.35" } }, "escape-html": { @@ -3582,39 +3839,23 @@ "dev": true }, "escodegen": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", - "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.9.0.tgz", + "integrity": "sha512-v0MYvNQ32bzwoG2OSFzWAkuahDQHK92JBN0pTAALJ4RIxEZe766QJPDR8Hqy7XNUy5K3fnVL76OqYAdc4TZEIw==", "dev": true, "requires": { - "esprima": "2.7.3", - "estraverse": "1.9.3", + "esprima": "3.1.3", + "estraverse": "4.2.0", "esutils": "2.0.2", "optionator": "0.8.2", - "source-map": "0.2.0" + "source-map": "0.5.7" }, "dependencies": { "esprima": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", - "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", - "dev": true - }, - "estraverse": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", - "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", "dev": true - }, - "source-map": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", - "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=", - "dev": true, - "optional": true, - "requires": { - "amdefine": "1.0.1" - } } } }, @@ -3637,9 +3878,9 @@ "dev": true }, "etag": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.0.tgz", - "integrity": "sha1-b2Ma7zNtbEY2K1F2QETOIWvjwFE=", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", "dev": true }, "event-emitter": { @@ -3649,7 +3890,7 @@ "dev": true, "requires": { "d": "1.0.0", - "es5-ext": "0.10.24" + "es5-ext": "0.10.35" } }, "event-stream": { @@ -3665,17 +3906,6 @@ "split": "0.3.3", "stream-combiner": "0.0.4", "through": "2.3.8" - }, - "dependencies": { - "split": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", - "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", - "dev": true, - "requires": { - "through": "2.3.8" - } - } } }, "eventemitter3": { @@ -3685,14 +3915,14 @@ "dev": true }, "execa": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.5.1.tgz", - "integrity": "sha1-3j+4XLjW6RyFvLzrFkWBeFy1ezY=", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", "dev": true, "optional": true, "requires": { - "cross-spawn": "4.0.2", - "get-stream": "2.3.1", + "cross-spawn": "5.1.0", + "get-stream": "3.0.0", "is-stream": "1.1.0", "npm-run-path": "2.0.2", "p-finally": "1.0.0", @@ -3700,23 +3930,17 @@ "strip-eof": "1.0.0" }, "dependencies": { - "get-stream": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", - "integrity": "sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4=", + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "dev": true, "optional": true, "requires": { - "object-assign": "4.1.1", - "pinkie-promise": "2.0.1" + "lru-cache": "4.1.1", + "shebang-command": "1.2.0", + "which": "1.3.0" } - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true, - "optional": true } } }, @@ -3825,40 +4049,66 @@ } }, "express": { - "version": "4.15.3", - "resolved": "https://registry.npmjs.org/express/-/express-4.15.3.tgz", - "integrity": "sha1-urZdDwOqgMNYQIly/HAPkWlEtmI=", + "version": "4.15.4", + "resolved": "https://registry.npmjs.org/express/-/express-4.15.4.tgz", + "integrity": "sha1-Ay4iU0ic+PzgJma+yj0R7XotrtE=", "dev": true, "optional": true, "requires": { - "accepts": "1.3.3", + "accepts": "1.3.4", "array-flatten": "1.1.1", "content-disposition": "0.5.2", - "content-type": "1.0.2", + "content-type": "1.0.4", "cookie": "0.3.1", "cookie-signature": "1.0.6", - "debug": "2.6.7", - "depd": "1.1.0", + "debug": "2.6.8", + "depd": "1.1.1", "encodeurl": "1.0.1", "escape-html": "1.0.3", - "etag": "1.8.0", - "finalhandler": "1.0.3", + "etag": "1.8.1", + "finalhandler": "1.0.6", "fresh": "0.5.0", "merge-descriptors": "1.0.1", "methods": "1.1.2", "on-finished": "2.3.0", - "parseurl": "1.3.1", + "parseurl": "1.3.2", "path-to-regexp": "0.1.7", - "proxy-addr": "1.1.4", - "qs": "6.4.0", + "proxy-addr": "1.1.5", + "qs": "6.5.0", "range-parser": "1.2.0", - "send": "0.15.3", - "serve-static": "1.12.3", + "send": "0.15.4", + "serve-static": "1.12.4", "setprototypeof": "1.0.3", "statuses": "1.3.1", "type-is": "1.6.15", "utils-merge": "1.0.0", - "vary": "1.1.1" + "vary": "1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "dev": true, + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "qs": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.0.tgz", + "integrity": "sha512-fjVFjW9yhqMhVGwRExCXLhJKrLlkYSaxNWdyc9rmHlrVZbk35YHH312dFd7191uQeXkI3mKLZTIbSvIeFwFemg==", + "dev": true, + "optional": true + }, + "statuses": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=", + "dev": true, + "optional": true + } } }, "express-session": { @@ -3873,7 +4123,7 @@ "debug": "2.2.0", "depd": "1.0.1", "on-headers": "1.0.1", - "parseurl": "1.3.1", + "parseurl": "1.3.2", "uid-safe": "2.0.0", "utils-merge": "1.0.0" }, @@ -3932,9 +4182,9 @@ } }, "extsprintf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz", - "integrity": "sha1-4QgOBljjALBilJkMxw4VAiNf1VA=", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", "dev": true }, "eyes": { @@ -3951,8 +4201,35 @@ "requires": { "chalk": "1.1.3", "time-stamp": "1.1.0" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } } }, + "fast-deep-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", + "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=", + "dev": true + }, "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", @@ -3969,12 +4246,12 @@ } }, "faye-websocket": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", - "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.9.3.tgz", + "integrity": "sha1-SCpQWw3wrmJrlphm0710DNuWLoM=", "dev": true, "requires": { - "websocket-driver": "0.6.5" + "websocket-driver": "0.7.0" } }, "figures": { @@ -3985,14 +4262,6 @@ "requires": { "escape-string-regexp": "1.0.5", "object-assign": "4.1.1" - }, - "dependencies": { - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - } } }, "file-entry-cache": { @@ -4001,16 +4270,8 @@ "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", "dev": true, "requires": { - "flat-cache": "1.2.2", + "flat-cache": "1.3.0", "object-assign": "4.1.1" - }, - "dependencies": { - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - } } }, "file-exists": { @@ -4026,67 +4287,30 @@ "dev": true }, "filesize": { - "version": "3.5.10", - "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.5.10.tgz", - "integrity": "sha1-/I+iPdtO+eXgq24eZPZ5okpWdh8=", + "version": "3.5.11", + "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.5.11.tgz", + "integrity": "sha512-ZH7loueKBoDb7yG9esn1U+fgq7BzlzW6NRi5/rMdxIZ05dj7GFD/Xc5rq2CDt5Yq86CyfSYVyx4242QQNZbx1g==", "dev": true }, "filing-cabinet": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/filing-cabinet/-/filing-cabinet-1.8.0.tgz", - "integrity": "sha1-fQ6MObBuFlMs7hTK3eQbH6AnYmU=", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/filing-cabinet/-/filing-cabinet-1.12.0.tgz", + "integrity": "sha1-b7k/2WjoO+3xtCmPKCNqiq/ffXg=", "dev": true, "requires": { "app-module-path": "1.1.0", - "commander": "2.8.1", - "debug": "2.2.0", + "commander": "2.11.0", + "debug": "2.6.7", "enhanced-resolve": "3.0.3", - "is-relative-path": "1.0.1", + "is-relative-path": "1.0.2", "module-definition": "2.2.4", - "module-lookup-amd": "4.0.4", - "object-assign": "4.0.1", - "resolve": "1.1.7", + "module-lookup-amd": "4.0.5", + "object-assign": "4.1.1", + "resolve": "1.4.0", "resolve-dependency-path": "1.0.2", - "sass-lookup": "1.0.2", - "stylus-lookup": "1.0.1" - }, - "dependencies": { - "commander": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.8.1.tgz", - "integrity": "sha1-Br42f+v9oMMwqh4qBy09yXYkJdQ=", - "dev": true, - "requires": { - "graceful-readlink": "1.0.1" - } - }, - "debug": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", - "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", - "dev": true, - "requires": { - "ms": "0.7.1" - } - }, - "ms": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", - "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", - "dev": true - }, - "object-assign": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.0.1.tgz", - "integrity": "sha1-mVBEVsNZi1ytT8WcJuipuxB/4L0=", - "dev": true - }, - "resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true - } + "sass-lookup": "1.1.0", + "stylus-lookup": "1.0.2", + "typescript": "2.4.2" } }, "fill-range": { @@ -4109,18 +4333,35 @@ "dev": true }, "finalhandler": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.0.3.tgz", - "integrity": "sha1-70fneVDpmXgOhgIqVg4yF+DQzIk=", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.0.6.tgz", + "integrity": "sha1-AHrqM9Gk0+QgF/YkhIrVjSEvgU8=", "dev": true, "requires": { - "debug": "2.6.7", + "debug": "2.6.9", "encodeurl": "1.0.1", "escape-html": "1.0.3", "on-finished": "2.3.0", - "parseurl": "1.3.1", + "parseurl": "1.3.2", "statuses": "1.3.1", "unpipe": "1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "statuses": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=", + "dev": true + } } }, "find": { @@ -4145,13 +4386,13 @@ "dev": true }, "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "dev": true, + "optional": true, "requires": { - "path-exists": "2.1.0", - "pinkie-promise": "2.0.1" + "locate-path": "2.0.0" } }, "findup-sync": { @@ -4173,9 +4414,9 @@ "dev": true, "requires": { "expand-tilde": "2.0.2", - "is-plain-object": "2.0.3", + "is-plain-object": "2.0.4", "object.defaults": "1.1.0", - "object.pick": "1.2.0", + "object.pick": "1.3.0", "parse-filepath": "1.0.1" }, "dependencies": { @@ -4191,29 +4432,31 @@ } }, "firebase": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/firebase/-/firebase-4.1.3.tgz", - "integrity": "sha1-5dcyc2bIVNwSRhYzuov+6i9cc1g=", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/firebase/-/firebase-4.6.0.tgz", + "integrity": "sha512-lfVXLQiCNcNsmG7ZxQapQRdE/kZOjheBZVZ1MNK80GTsuK9RO7DYO5Qdf4K9Om8r7OBZ4nRp438kGkf24x7sOg==", "dev": true, "requires": { + "@firebase/app": "0.1.1", + "@firebase/auth": "0.2.0", + "@firebase/database": "0.1.2", + "@firebase/firestore": "0.1.2", + "@firebase/messaging": "0.1.2", + "@firebase/polyfill": "0.1.1", + "@firebase/storage": "0.1.1", "dom-storage": "2.0.2", - "faye-websocket": "0.9.3", - "jsonwebtoken": "7.4.1", - "promise-polyfill": "6.0.2", "xmlhttprequest": "1.8.0" }, "dependencies": { "base64url": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/base64url/-/base64url-2.0.0.tgz", - "integrity": "sha1-6sFuA+oUOO/5Qj1puqNiYu0fcLs=", - "dev": true + "integrity": "sha1-6sFuA+oUOO/5Qj1puqNiYu0fcLs=" }, "buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=", - "dev": true + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" }, "dom-storage": { "version": "2.0.2", @@ -4225,17 +4468,15 @@ "version": "1.0.9", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.9.tgz", "integrity": "sha1-S8kmJ07Dtau1AW5+HWCSGsJisqE=", - "dev": true, "requires": { "base64url": "2.0.0", - "safe-buffer": "5.1.0" + "safe-buffer": "5.1.1" } }, "faye-websocket": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.9.3.tgz", "integrity": "sha1-SCpQWw3wrmJrlphm0710DNuWLoM=", - "dev": true, "requires": { "websocket-driver": "0.6.5" } @@ -4243,20 +4484,17 @@ "hoek": { "version": "2.16.3", "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", - "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", - "dev": true + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=" }, "isemail": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/isemail/-/isemail-1.2.0.tgz", - "integrity": "sha1-vgPfjMPineTSxd9lASY/H6RZXpo=", - "dev": true + "integrity": "sha1-vgPfjMPineTSxd9lASY/H6RZXpo=" }, "joi": { "version": "6.10.1", "resolved": "https://registry.npmjs.org/joi/-/joi-6.10.1.tgz", "integrity": "sha1-TVDDGAeRIgAP5fFq8f+OGRe3fgY=", - "dev": true, "requires": { "hoek": "2.16.3", "isemail": "1.2.0", @@ -4265,10 +4503,9 @@ } }, "jsonwebtoken": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-7.4.1.tgz", - "integrity": "sha1-fKMk9SFfi+A5zTWmxFu4y3SkSPs=", - "dev": true, + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-7.4.3.tgz", + "integrity": "sha1-d/UCHeBYtgWheD+hKD6ZgS5kVjg=", "requires": { "joi": "6.10.1", "jws": "3.1.4", @@ -4281,60 +4518,52 @@ "version": "1.1.5", "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.1.5.tgz", "integrity": "sha1-oFUs4CIHQs1S4VN3SjKQXDDnVuU=", - "dev": true, "requires": { "base64url": "2.0.0", "buffer-equal-constant-time": "1.0.1", "ecdsa-sig-formatter": "1.0.9", - "safe-buffer": "5.1.0" + "safe-buffer": "5.1.1" } }, "jws": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/jws/-/jws-3.1.4.tgz", "integrity": "sha1-+ei5M46KhHJ31kRLFGT2GIDgUKI=", - "dev": true, "requires": { "base64url": "2.0.0", "jwa": "1.1.5", - "safe-buffer": "5.1.0" + "safe-buffer": "5.1.1" } }, "lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=", - "dev": true + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" }, "moment": { "version": "2.18.1", "resolved": "https://registry.npmjs.org/moment/-/moment-2.18.1.tgz", - "integrity": "sha1-w2GT3Tzhwu7SrbfIAtu8d6gbHA8=", - "dev": true + "integrity": "sha1-w2GT3Tzhwu7SrbfIAtu8d6gbHA8=" }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "promise-polyfill": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-6.0.2.tgz", - "integrity": "sha1-2chtPcTcLfkBboiUbe/Wm0m0EWI=", - "dev": true + "integrity": "sha1-2chtPcTcLfkBboiUbe/Wm0m0EWI=" }, "safe-buffer": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.0.tgz", - "integrity": "sha512-aSLEDudu6OoRr/2rU609gRmnYboRLxgDG1z9o2Q0os7236FwvcqIOO8r8U5JUEwivZOhDaKlFO4SbPTJYyBEyQ==", - "dev": true + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" }, "topo": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/topo/-/topo-1.1.0.tgz", "integrity": "sha1-6ddRYV0buH3IZdsYL6HKCl71NtU=", - "dev": true, "requires": { "hoek": "2.16.3" } @@ -4343,7 +4572,6 @@ "version": "0.6.5", "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.6.5.tgz", "integrity": "sha1-XLJVbOuF9Dc8bYI4qmkchFThOjY=", - "dev": true, "requires": { "websocket-extensions": "0.1.1" } @@ -4351,8 +4579,7 @@ "websocket-extensions": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.1.tgz", - "integrity": "sha1-domUmcGEtu91Q3fC27DNbLVdKec=", - "dev": true + "integrity": "sha1-domUmcGEtu91Q3fC27DNbLVdKec=" }, "xmlhttprequest": { "version": "1.8.0", @@ -4363,159 +4590,2455 @@ "xtend": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", - "dev": true + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=" } } }, "firebase-admin": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-5.0.0.tgz", - "integrity": "sha1-+trlbJm+T7VseBAH1nGdFT/o/Xs=", + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/firebase-admin/-/firebase-admin-5.4.2.tgz", + "integrity": "sha1-AC9YvirYkBIp5SXsVfpybRL5BOs=", "dev": true, "requires": { - "@types/jsonwebtoken": "7.2.0", + "@google-cloud/firestore": "0.8.2", + "@google-cloud/storage": "1.3.1", + "@types/google-cloud__storage": "1.1.5", + "@types/jsonwebtoken": "7.2.3", + "@types/node": "8.0.33", "faye-websocket": "0.9.3", + "google-auth-library": "0.10.0", "jsonwebtoken": "7.1.9", + "lodash": "4.17.4", "node-forge": "0.7.1" }, "dependencies": { - "@types/jsonwebtoken": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-7.2.0.tgz", - "integrity": "sha1-D+0yyFAdqArJg50tQDplyD13b/0=", + "@google-cloud/common": { + "version": "0.13.6", + "resolved": "https://registry.npmjs.org/@google-cloud/common/-/common-0.13.6.tgz", + "integrity": "sha1-qdjhN7xCmkSrqWif5qDkMxeE+FM=", "dev": true, "requires": { - "@types/node": "7.0.18" - } + "array-uniq": "1.0.3", + "arrify": "1.0.1", + "concat-stream": "1.6.0", + "create-error-class": "3.0.2", + "duplexify": "3.5.1", + "ent": "2.2.0", + "extend": "3.0.1", + "google-auto-auth": "0.7.2", + "is": "3.2.1", + "log-driver": "1.2.5", + "methmeth": "1.1.0", + "modelo": "4.2.0", + "request": "2.83.0", + "retry-request": "3.0.0", + "split-array-stream": "1.0.3", + "stream-events": "1.0.2", + "string-format-obj": "1.1.0", + "through2": "2.0.3" + } + }, + "@google-cloud/common-grpc": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@google-cloud/common-grpc/-/common-grpc-0.4.1.tgz", + "integrity": "sha1-CSZGB++4k0MJr/1jhJmG+7gPkhg=", + "dev": true, + "requires": { + "@google-cloud/common": "0.13.6", + "dot-prop": "2.4.0", + "duplexify": "3.5.1", + "extend": "3.0.1", + "grpc": "1.6.6", + "is": "3.2.1", + "modelo": "4.2.0", + "retry-request": "3.0.0", + "through2": "2.0.3" + } + }, + "@google-cloud/firestore": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/@google-cloud/firestore/-/firestore-0.8.2.tgz", + "integrity": "sha512-Z9hoiZIIn1MN7lUZE0pStvkrTdzJnSyFyxDJ4VojkzF/lL4EAUr9USyWXol6HZYcERPjXIasHSXXTYwuXx8tmA==", + "dev": true, + "requires": { + "@google-cloud/common": "0.13.6", + "@google-cloud/common-grpc": "0.4.1", + "bun": "0.0.12", + "extend": "3.0.1", + "functional-red-black-tree": "1.0.1", + "google-gax": "0.14.1", + "grpc": "1.6.6", + "is": "3.2.1", + "through2": "2.0.3" + } + }, + "@google-cloud/storage": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-1.3.1.tgz", + "integrity": "sha512-tN2YttvQ33KwXuG2tpP3lEtxkZWV1yifc84YOusMjBCDoAal5GWXDPuCeFBI7cMs5LW+V2o3I9ZusOJZwYA8ug==", + "dev": true, + "requires": { + "@google-cloud/common": "0.13.6", + "arrify": "1.0.1", + "async": "2.5.0", + "concat-stream": "1.6.0", + "create-error-class": "3.0.2", + "duplexify": "3.5.1", + "extend": "3.0.1", + "gcs-resumable-upload": "0.8.2", + "hash-stream-validation": "0.2.1", + "is": "3.2.1", + "mime-types": "2.1.17", + "once": "1.4.0", + "pumpify": "1.3.5", + "safe-buffer": "5.1.1", + "stream-events": "1.0.2", + "string-format-obj": "1.1.0", + "through2": "2.0.3" + } + }, + "@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=", + "dev": true + }, + "@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "dev": true + }, + "@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "dev": true + }, + "@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=", + "dev": true + }, + "@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=", + "dev": true, + "requires": { + "@protobufjs/aspromise": "1.1.2", + "@protobufjs/inquire": "1.1.0" + } + }, + "@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=", + "dev": true + }, + "@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=", + "dev": true + }, + "@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=", + "dev": true + }, + "@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=", + "dev": true + }, + "@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=", + "dev": true + }, + "@types/google-cloud__storage": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@types/google-cloud__storage/-/google-cloud__storage-1.1.5.tgz", + "integrity": "sha512-c82GoxSyQZASfOTpyMx8nvpIwySR+vXnkzymya9MQR7L+60ckLQVzn6yUMnOWgTDFM7EUzk79X5p8uSDx9jDVw==", + "dev": true, + "requires": { + "@types/node": "8.0.33" + } + }, + "@types/jsonwebtoken": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-7.2.3.tgz", + "integrity": "sha512-cVhxZfVCyTZd1P+2a+xXSR9to7hZTulNRLLCQMVfAevUqx2Ee+EgsiD/7pX8qvdXWP3nWgSoTjKRLMrIpdPVjQ==", + "dev": true, + "requires": { + "@types/node": "8.0.33" + } + }, + "@types/long": { + "version": "3.0.32", + "resolved": "https://registry.npmjs.org/@types/long/-/long-3.0.32.tgz", + "integrity": "sha512-ZXyOOm83p7X8p3s0IYM3VeueNmHpkk/yMlP8CLeOnEcu6hIwPH7YjZBvhQkR0ZFS2DqZAxKtJ/M5fcuv3OU5BA==", + "dev": true + }, + "@types/node": { + "version": "8.0.33", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.33.tgz", + "integrity": "sha512-vmCdO8Bm1ExT+FWfC9sd9r4jwqM7o97gGy2WBshkkXbf/2nLAJQUrZfIhw27yVOtLUev6kSZc4cav/46KbDd8A==", + "dev": true + }, + "ajv": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.2.3.tgz", + "integrity": "sha1-wG9Zh3jETGsWGrr+NGa4GtGBTtI=", + "dev": true, + "requires": { + "co": "4.6.0", + "fast-deep-equal": "1.0.0", + "json-schema-traverse": "0.3.1", + "json-stable-stringify": "1.0.1" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "arguejs": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/arguejs/-/arguejs-0.2.3.tgz", + "integrity": "sha1-tvk59f4OPNHz+T4qqSYkJL8xKvc=", + "dev": true + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "1.0.3" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "ascli": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ascli/-/ascli-1.0.1.tgz", + "integrity": "sha1-vPpZdKYvGOgcq660lzKrSoj5Brw=", + "dev": true, + "requires": { + "colour": "0.7.1", + "optjs": "3.2.2" + } + }, + "asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", + "dev": true + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "async": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/async/-/async-2.5.0.tgz", + "integrity": "sha512-e+lJAJeNWuPCNyxZKOBdaJGyLGHugXVQtrAwtuAe2vhxTYxFTKE73p8JuTmdH0qdQZtDvI4dhJwjZc5zsfIsYw==", + "dev": true, + "requires": { + "lodash": "4.17.4" + } + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true + }, + "aws4": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base64url": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-2.0.0.tgz", + "integrity": "sha1-6sFuA+oUOO/5Qj1puqNiYu0fcLs=", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "dev": true, + "optional": true, + "requires": { + "tweetnacl": "0.14.5" + } + }, + "boom": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", + "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", + "dev": true, + "requires": { + "hoek": "4.2.0" + } + }, + "brace-expansion": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "buffer-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz", + "integrity": "sha1-WWFrSYME1Var1GaWayLu2j7KX74=", + "dev": true + }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=", + "dev": true + }, + "bun": { + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/bun/-/bun-0.0.12.tgz", + "integrity": "sha512-Toms18J9DqnT+IfWkwxVTB2EaBprHvjlMWrTIsfX4xbu3ZBqVBwrERU0em1IgtRe04wT+wJxMlKHZok24hrcSQ==", + "dev": true, + "requires": { + "readable-stream": "1.0.34" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, + "bytebuffer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/bytebuffer/-/bytebuffer-5.0.1.tgz", + "integrity": "sha1-WC7qSxqHO20CCkjVjfhfC7ps/d0=", + "dev": true, + "requires": { + "long": "3.2.0" + } + }, + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true + }, + "capture-stack-trace": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz", + "integrity": "sha1-Sm+gc5nCa7pH8LJJa00PtAjFVQ0=", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "cliui": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", + "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "dev": true, + "requires": { + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wrap-ansi": "2.1.0" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "colour": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/colour/-/colour-0.7.1.tgz", + "integrity": "sha1-nLFpkX7F0SwHNtPoaFdG3xyt93g=", + "dev": true + }, + "combined-stream": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", + "dev": true, + "requires": { + "delayed-stream": "1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "concat-stream": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", + "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", + "dev": true, + "requires": { + "inherits": "2.0.3", + "readable-stream": "2.3.3", + "typedarray": "0.0.6" + } + }, + "configstore": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.1.tgz", + "integrity": "sha512-5oNkD/L++l0O6xGXxb1EWS7SivtjfGQlRyxJsYgE0Z495/L81e2h4/d3r969hoPXuFItzNOKMtsXgYG4c7dYvw==", + "dev": true, + "requires": { + "dot-prop": "4.2.0", + "graceful-fs": "4.1.11", + "make-dir": "1.0.0", + "unique-string": "1.0.0", + "write-file-atomic": "2.3.0", + "xdg-basedir": "3.0.0" + }, + "dependencies": { + "dot-prop": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", + "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", + "dev": true, + "requires": { + "is-obj": "1.0.1" + } + } + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "create-error-class": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", + "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", + "dev": true, + "requires": { + "capture-stack-trace": "1.0.0" + } + }, + "cryptiles": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", + "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", + "dev": true, + "requires": { + "boom": "5.2.0" + }, + "dependencies": { + "boom": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", + "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", + "dev": true, + "requires": { + "hoek": "4.2.0" + } + } + } + }, + "crypto-random-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", + "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=", + "dev": true + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "1.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "dot-prop": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-2.4.0.tgz", + "integrity": "sha1-hI4o9/HVB0DGdHqzywdnBGK2+Jw=", + "dev": true, + "requires": { + "is-obj": "1.0.1" + } + }, + "duplexify": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.5.1.tgz", + "integrity": "sha512-j5goxHTwVED1Fpe5hh3q9R93Kip0Bg2KVAt4f8CEYM3UEwYcPSvWbXaUQOzdX/HtiNomipv+gU7ASQPDbV7pGQ==", + "dev": true, + "requires": { + "end-of-stream": "1.4.0", + "inherits": "2.0.3", + "readable-stream": "2.3.3", + "stream-shift": "1.0.0" + } + }, + "ecc-jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "dev": true, + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, + "ecdsa-sig-formatter": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.9.tgz", + "integrity": "sha1-S8kmJ07Dtau1AW5+HWCSGsJisqE=", + "dev": true, + "requires": { + "base64url": "2.0.0", + "safe-buffer": "5.1.1" + } + }, + "end-of-stream": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.0.tgz", + "integrity": "sha1-epDYM+/abPpurA9JSduw+tOmMgY=", + "dev": true, + "requires": { + "once": "1.4.0" + } + }, + "ent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz", + "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=", + "dev": true + }, + "extend": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", + "dev": true + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "fast-deep-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", + "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=", + "dev": true + }, + "faye-websocket": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.9.3.tgz", + "integrity": "sha1-SCpQWw3wrmJrlphm0710DNuWLoM=", + "dev": true, + "requires": { + "websocket-driver": "0.7.0" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.1.tgz", + "integrity": "sha1-b7lPvXGIUwbXPRXMSX/kzE7NRL8=", + "dev": true, + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.17" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "gcp-metadata": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-0.3.1.tgz", + "integrity": "sha512-5kJPX/RXuqoLmHiOOgkSDk/LI0QaXpEvZ3pvQP4ifjGGDKZKVSOjL/GcDjXA5kLxppFCOjmmsu0Uoop9d1upaQ==", + "dev": true, + "requires": { + "extend": "3.0.1", + "retry-request": "3.0.0" + } + }, + "gcs-resumable-upload": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/gcs-resumable-upload/-/gcs-resumable-upload-0.8.2.tgz", + "integrity": "sha512-PBl1OFABYxubxfYPh000I0+JLbQzBRtNqxzgxYboIQk2tdw7BvjJ2dVukk3YH4QM6GiUwqItyNqWBuxjLH8GhA==", + "dev": true, + "requires": { + "buffer-equal": "1.0.0", + "configstore": "3.1.1", + "google-auto-auth": "0.7.2", + "pumpify": "1.3.5", + "request": "2.83.0", + "stream-events": "1.0.2", + "through2": "2.0.3" + } + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "1.0.0" + } + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "dev": true, + "requires": { + "array-union": "1.0.2", + "glob": "7.1.2", + "object-assign": "4.1.1", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" + } + }, + "google-auth-library": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-0.10.0.tgz", + "integrity": "sha1-bhW6vuhf0d0U2NEoopW2g41SE24=", + "dev": true, + "requires": { + "gtoken": "1.2.2", + "jws": "3.1.4", + "lodash.noop": "3.0.1", + "request": "2.83.0" + } + }, + "google-auto-auth": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/google-auto-auth/-/google-auto-auth-0.7.2.tgz", + "integrity": "sha512-ux2n2AE2g3+vcLXwL4dP/M12SFMRX5dzCzBfhAEkTeAB7dpyGdOIEj7nmUx0BHKaCcUQrRWg9kT63X/Mmtk1+A==", + "dev": true, + "requires": { + "async": "2.5.0", + "gcp-metadata": "0.3.1", + "google-auth-library": "0.10.0", + "request": "2.83.0" + } + }, + "google-gax": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/google-gax/-/google-gax-0.14.1.tgz", + "integrity": "sha1-/oj7nVAw0mJ3xjUIp5r3s3F54Tk=", + "dev": true, + "requires": { + "extend": "3.0.1", + "globby": "6.1.0", + "google-auto-auth": "0.5.4", + "google-proto-files": "0.13.1", + "grpc": "1.6.6", + "is-stream-ended": "0.1.3", + "lodash": "4.17.4", + "process-nextick-args": "1.0.7", + "protobufjs": "6.8.0", + "readable-stream": "2.3.3", + "through2": "2.0.3" + }, + "dependencies": { + "@types/node": { + "version": "7.0.43", + "resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.43.tgz", + "integrity": "sha512-7scYwwfHNppXvH/9JzakbVxk0o0QUILVk1Lv64GRaxwPuGpnF1QBiwdvhDpLcymb8BpomQL3KYoWKq3wUdDMhQ==", + "dev": true + }, + "google-auto-auth": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/google-auto-auth/-/google-auto-auth-0.5.4.tgz", + "integrity": "sha1-HYbHko1jPnWpwasDSlJ+/M5KQLE=", + "dev": true, + "requires": { + "async": "2.5.0", + "google-auth-library": "0.10.0", + "object-assign": "3.0.0", + "request": "2.83.0" + } + }, + "object-assign": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", + "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=", + "dev": true + }, + "protobufjs": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.0.tgz", + "integrity": "sha512-47Y49f5JN5Qsbxas2TyI2zFO8j9GpQAQm5thf54fr2O8qcP/jkIXYxmYx1hN2WQFAhESU1xpVn5NWVDBB8WFnw==", + "dev": true, + "requires": { + "@protobufjs/aspromise": "1.1.2", + "@protobufjs/base64": "1.1.2", + "@protobufjs/codegen": "2.0.4", + "@protobufjs/eventemitter": "1.1.0", + "@protobufjs/fetch": "1.1.0", + "@protobufjs/float": "1.0.2", + "@protobufjs/inquire": "1.1.0", + "@protobufjs/path": "1.1.2", + "@protobufjs/pool": "1.1.0", + "@protobufjs/utf8": "1.1.0", + "@types/long": "3.0.32", + "@types/node": "7.0.43", + "long": "3.2.0" + } + } + } + }, + "google-p12-pem": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/google-p12-pem/-/google-p12-pem-0.1.2.tgz", + "integrity": "sha1-M8RqsCGqc0+gMys5YKmj/8svMXc=", + "dev": true, + "requires": { + "node-forge": "0.7.1" + } + }, + "google-proto-files": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/google-proto-files/-/google-proto-files-0.13.1.tgz", + "integrity": "sha512-CivI3rZ85dMPTCAyxq6lq9s7vDkeWEIFxweopC1vEjjRmFMJwOX/MOmFZ90a0BGal/Dsb63vq7Ael9ryeokz0g==", + "dev": true + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "grpc": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/grpc/-/grpc-1.6.6.tgz", + "integrity": "sha1-IFF4T2vWE0aB+ixLXnXcgsbCP/o=", + "dev": true, + "requires": { + "arguejs": "0.2.3", + "lodash": "4.17.4", + "nan": "2.7.0", + "node-pre-gyp": "0.6.38", + "protobufjs": "5.0.2" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, + "ajv": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", + "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", + "dev": true, + "requires": { + "co": "4.6.0", + "json-stable-stringify": "1.0.1" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true + }, + "are-we-there-yet": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz", + "integrity": "sha1-u13KOCu5TwXhUZQ3PRb9O6HKEQ0=", + "dev": true, + "requires": { + "delegates": "1.0.0", + "readable-stream": "2.3.3" + } + }, + "asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", + "dev": true + }, + "assert-plus": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "aws-sign2": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", + "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", + "dev": true + }, + "aws4": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", + "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", + "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "dev": true, + "optional": true, + "requires": { + "tweetnacl": "0.14.5" + } + }, + "block-stream": { + "version": "0.0.9", + "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", + "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "dev": true, + "requires": { + "inherits": "2.0.3" + } + }, + "boom": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", + "dev": true, + "requires": { + "hoek": "2.16.3" + } + }, + "brace-expansion": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", + "dev": true, + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "combined-stream": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", + "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", + "dev": true, + "requires": { + "delayed-stream": "1.0.0" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "cryptiles": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", + "dev": true, + "requires": { + "boom": "2.10.1" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz", + "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8=", + "dev": true + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "dev": true + }, + "ecc-jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", + "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", + "dev": true, + "optional": true, + "requires": { + "jsbn": "0.1.1" + } + }, + "extend": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", + "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", + "dev": true + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", + "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", + "dev": true, + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.17" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fstream": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.11.tgz", + "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "inherits": "2.0.3", + "mkdirp": "0.5.1", + "rimraf": "2.6.2" + } + }, + "fstream-ignore": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/fstream-ignore/-/fstream-ignore-1.0.5.tgz", + "integrity": "sha1-nDHa40dnAY/h0kmyTa2mfQktoQU=", + "dev": true, + "requires": { + "fstream": "1.0.11", + "inherits": "2.0.3", + "minimatch": "3.0.4" + } + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "dev": true, + "requires": { + "aproba": "1.2.0", + "console-control-strings": "1.1.0", + "has-unicode": "2.0.1", + "object-assign": "4.1.1", + "signal-exit": "3.0.2", + "string-width": "1.0.2", + "strip-ansi": "3.0.1", + "wide-align": "1.1.2" + } + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "1.0.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "dev": true, + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", + "dev": true + }, + "har-schema": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", + "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=", + "dev": true + }, + "har-validator": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", + "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", + "dev": true, + "requires": { + "ajv": "4.11.8", + "har-schema": "1.0.5" + } + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "dev": true + }, + "hawk": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", + "dev": true, + "requires": { + "boom": "2.10.1", + "cryptiles": "2.0.5", + "hoek": "2.16.3", + "sntp": "1.0.9" + } + }, + "hoek": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", + "dev": true + }, + "http-signature": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", + "dev": true, + "requires": { + "assert-plus": "0.2.0", + "jsprim": "1.4.1", + "sshpk": "1.13.1" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "ini": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz", + "integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true, + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "dev": true, + "requires": { + "jsonify": "0.0.0" + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "mime-db": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", + "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=", + "dev": true + }, + "mime-types": { + "version": "2.1.17", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", + "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", + "dev": true, + "requires": { + "mime-db": "1.30.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "1.1.8" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + } + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "node-pre-gyp": { + "version": "0.6.38", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.6.38.tgz", + "integrity": "sha1-6Sog+DQWQVu0CG9tH7eLPac9ET0=", + "dev": true, + "requires": { + "hawk": "3.1.3", + "mkdirp": "0.5.1", + "nopt": "4.0.1", + "npmlog": "4.1.2", + "rc": "1.2.1", + "request": "2.81.0", + "rimraf": "2.6.2", + "semver": "5.4.1", + "tar": "2.2.1", + "tar-pack": "3.4.0" + } + }, + "nopt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", + "dev": true, + "requires": { + "abbrev": "1.1.1", + "osenv": "0.1.4" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "dev": true, + "requires": { + "are-we-there-yet": "1.1.4", + "console-control-strings": "1.1.0", + "gauge": "2.7.4", + "set-blocking": "2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "osenv": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.4.tgz", + "integrity": "sha1-Qv5tWVPfBsgGS+bxdsPQWqqjRkQ=", + "dev": true, + "requires": { + "os-homedir": "1.0.2", + "os-tmpdir": "1.0.2" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "performance-now": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", + "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=", + "dev": true + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", + "dev": true + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, + "qs": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", + "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=", + "dev": true + }, + "rc": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.1.tgz", + "integrity": "sha1-LgPo5C7kULjLPc5lvhv4l04d/ZU=", + "dev": true, + "requires": { + "deep-extend": "0.4.2", + "ini": "1.3.4", + "minimist": "1.2.0", + "strip-json-comments": "2.0.1" + } + }, + "readable-stream": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "request": { + "version": "2.81.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", + "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", + "dev": true, + "requires": { + "aws-sign2": "0.6.0", + "aws4": "1.6.0", + "caseless": "0.12.0", + "combined-stream": "1.0.5", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.1.4", + "har-validator": "4.2.1", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.17", + "oauth-sign": "0.8.2", + "performance-now": "0.2.0", + "qs": "6.4.0", + "safe-buffer": "5.1.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.3", + "tunnel-agent": "0.6.0", + "uuid": "3.1.0" + } + }, + "rimraf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", + "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", + "dev": true, + "requires": { + "glob": "7.1.2" + } + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "dev": true + }, + "semver": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "sntp": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", + "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", + "dev": true, + "requires": { + "hoek": "2.16.3" + } + }, + "sshpk": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", + "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", + "dev": true, + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "stringstream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", + "dev": true + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, + "tar": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", + "dev": true, + "requires": { + "block-stream": "0.0.9", + "fstream": "1.0.11", + "inherits": "2.0.3" + } + }, + "tar-pack": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/tar-pack/-/tar-pack-3.4.0.tgz", + "integrity": "sha1-I74tf2cagzk3bL2wuP4/3r8xeYQ=", + "dev": true, + "requires": { + "debug": "2.6.9", + "fstream": "1.0.11", + "fstream-ignore": "1.0.5", + "once": "1.4.0", + "readable-stream": "2.3.3", + "rimraf": "2.6.2", + "tar": "2.2.1", + "uid-number": "0.0.6" + } + }, + "tough-cookie": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", + "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", + "dev": true, + "requires": { + "punycode": "1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true, + "optional": true + }, + "uid-number": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/uid-number/-/uid-number-0.0.6.tgz", + "integrity": "sha1-DqEOgDXo61uOREnwbaHHMGY7qoE=", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "uuid": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", + "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==", + "dev": true + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "1.3.0" + }, + "dependencies": { + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + } + } + }, + "wide-align": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.2.tgz", + "integrity": "sha512-ijDLlyQ7s6x1JgCLur53osjm/UXUYD9+0PbYKrBsYisYXzCxN+HC3mYDNy/dWdmf3AwqwU3CXwDCvsNgGK1S0w==", + "dev": true, + "requires": { + "string-width": "1.0.2" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + } + } + }, + "gtoken": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-1.2.2.tgz", + "integrity": "sha1-Fyd2oanZasCfwioA9b6DzubeiCA=", + "dev": true, + "requires": { + "google-p12-pem": "0.1.2", + "jws": "3.1.4", + "mime": "1.4.1", + "request": "2.83.0" + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", + "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", + "dev": true, + "requires": { + "ajv": "5.2.3", + "har-schema": "2.0.0" + } + }, + "hash-stream-validation": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/hash-stream-validation/-/hash-stream-validation-0.2.1.tgz", + "integrity": "sha1-7Mm5l7IYvluzEphii7gHhptz3NE=", + "dev": true, + "requires": { + "through2": "2.0.3" + } + }, + "hawk": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", + "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", + "dev": true, + "requires": { + "boom": "4.3.1", + "cryptiles": "3.1.2", + "hoek": "4.2.0", + "sntp": "2.0.2" + } + }, + "hoek": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz", + "integrity": "sha512-v0XCLxICi9nPfYrS9RL8HbYnXi9obYAeLbSP00BmnZwCK9+Ih9WOjoZ8YoHCoav2csqn4FOz4Orldsy2dmDwmQ==", + "dev": true + }, + "http-parser-js": { + "version": "0.4.9", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.9.tgz", + "integrity": "sha1-6hoE+2St/wJC6ZdPKX3Uw8rSceE=", + "dev": true + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "jsprim": "1.4.1", + "sshpk": "1.13.1" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, + "invert-kv": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", + "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=", + "dev": true + }, + "is": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/is/-/is-3.2.1.tgz", + "integrity": "sha1-0Kwq1V63sL7JJqUmb2xmKqqD3KU=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "1.0.1" + } + }, + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "dev": true + }, + "is-stream-ended": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/is-stream-ended/-/is-stream-ended-0.1.3.tgz", + "integrity": "sha1-oEc7Jnx1ZjVIa+7cfjNE5UnRUqw=", + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isemail": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/isemail/-/isemail-1.2.0.tgz", + "integrity": "sha1-vgPfjMPineTSxd9lASY/H6RZXpo=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "joi": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/joi/-/joi-6.10.1.tgz", + "integrity": "sha1-TVDDGAeRIgAP5fFq8f+OGRe3fgY=", + "dev": true, + "requires": { + "hoek": "2.16.3", + "isemail": "1.2.0", + "moment": "2.18.1", + "topo": "1.1.0" + }, + "dependencies": { + "hoek": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", + "dev": true + } + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true, + "optional": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", + "dev": true + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "dev": true, + "requires": { + "jsonify": "0.0.0" + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", + "dev": true + }, + "jsonwebtoken": { + "version": "7.1.9", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-7.1.9.tgz", + "integrity": "sha1-hHgE5SWL7FqUmajcSl56O64I1Yo=", + "dev": true, + "requires": { + "joi": "6.10.1", + "jws": "3.1.4", + "lodash.once": "4.1.1", + "ms": "0.7.3", + "xtend": "4.0.1" + } + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "jwa": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.1.5.tgz", + "integrity": "sha1-oFUs4CIHQs1S4VN3SjKQXDDnVuU=", + "dev": true, + "requires": { + "base64url": "2.0.0", + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.9", + "safe-buffer": "5.1.1" + } + }, + "jws": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.1.4.tgz", + "integrity": "sha1-+ei5M46KhHJ31kRLFGT2GIDgUKI=", + "dev": true, + "requires": { + "base64url": "2.0.0", + "jwa": "1.1.5", + "safe-buffer": "5.1.1" + } + }, + "lcid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", + "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "dev": true, + "requires": { + "invert-kv": "1.0.0" + } + }, + "lodash": { + "version": "4.17.4", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", + "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", + "dev": true + }, + "lodash.noop": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash.noop/-/lodash.noop-3.0.1.tgz", + "integrity": "sha1-OBiPTWUKOkdCWEObluxFsyYXEzw=", + "dev": true + }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=", + "dev": true + }, + "log-driver": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.5.tgz", + "integrity": "sha1-euTsJXMC/XkNVXyxDJcQDYV7AFY=", + "dev": true + }, + "long": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", + "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=", + "dev": true + }, + "make-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.0.0.tgz", + "integrity": "sha1-l6ARdR6R3YfPre9Ygy67BJNt6Xg=", + "dev": true, + "requires": { + "pify": "2.3.0" + } + }, + "methmeth": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/methmeth/-/methmeth-1.1.0.tgz", + "integrity": "sha1-6AomYY5S9cQiKGG7dIUQvRDikIk=", + "dev": true + }, + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", + "dev": true + }, + "mime-db": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", + "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=", + "dev": true + }, + "mime-types": { + "version": "2.1.17", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", + "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", + "dev": true, + "requires": { + "mime-db": "1.30.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "1.1.8" + } + }, + "modelo": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/modelo/-/modelo-4.2.0.tgz", + "integrity": "sha1-O0tCACOmbKfjK9uhbnEJN+FNGws=", + "dev": true + }, + "moment": { + "version": "2.18.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.18.1.tgz", + "integrity": "sha1-w2GT3Tzhwu7SrbfIAtu8d6gbHA8=", + "dev": true + }, + "ms": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.3.tgz", + "integrity": "sha1-cIFVpeROM/X9D8U+gdDUCpG+H/8=", + "dev": true + }, + "nan": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.7.0.tgz", + "integrity": "sha1-2Vv3IeyHfgjbJ27T/G63j5CDrUY=", + "dev": true + }, + "node-forge": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.1.tgz", + "integrity": "sha1-naYR6giYL0uUIGs760zJZl8gwwA=", + "dev": true + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1.0.2" + } + }, + "optjs": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/optjs/-/optjs-3.2.2.tgz", + "integrity": "sha1-aabOicRCpEQDFBrS+bNwvVu29O4=", + "dev": true + }, + "os-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", + "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", + "dev": true, + "requires": { + "lcid": "1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true }, - "@types/node": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.18.tgz", - "integrity": "sha1-zWfyfT3Az7dG8L3V4IbExdVb4XM=", + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "dev": true }, - "base64url": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/base64url/-/base64url-2.0.0.tgz", - "integrity": "sha1-6sFuA+oUOO/5Qj1puqNiYu0fcLs=", + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", "dev": true }, - "buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=", + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "2.0.4" + } + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", "dev": true }, - "ecdsa-sig-formatter": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.9.tgz", - "integrity": "sha1-S8kmJ07Dtau1AW5+HWCSGsJisqE=", + "protobufjs": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-5.0.2.tgz", + "integrity": "sha1-WXSNfc8D0tsiwT2p/rAk4Wq4DJE=", "dev": true, "requires": { - "base64url": "2.0.0", - "safe-buffer": "5.0.1" + "ascli": "1.0.1", + "bytebuffer": "5.0.1", + "glob": "7.1.2", + "yargs": "3.32.0" } }, - "faye-websocket": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.9.3.tgz", - "integrity": "sha1-SCpQWw3wrmJrlphm0710DNuWLoM=", + "pump": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.2.tgz", + "integrity": "sha1-Oz7mUS+U8OV1U4wXmV+fFpkKXVE=", "dev": true, "requires": { - "websocket-driver": "0.6.5" + "end-of-stream": "1.4.0", + "once": "1.4.0" } }, - "hoek": { - "version": "2.16.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", - "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", + "pumpify": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.3.5.tgz", + "integrity": "sha1-G2ccYZlAq8rqwK0OOjwWS+dgmTs=", + "dev": true, + "requires": { + "duplexify": "3.5.1", + "inherits": "2.0.3", + "pump": "1.0.2" + } + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", "dev": true }, - "isemail": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/isemail/-/isemail-1.2.0.tgz", - "integrity": "sha1-vgPfjMPineTSxd9lASY/H6RZXpo=", + "qs": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", "dev": true }, - "joi": { - "version": "6.10.1", - "resolved": "https://registry.npmjs.org/joi/-/joi-6.10.1.tgz", - "integrity": "sha1-TVDDGAeRIgAP5fFq8f+OGRe3fgY=", + "readable-stream": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", "dev": true, "requires": { - "hoek": "2.16.3", - "isemail": "1.2.0", - "moment": "2.18.1", - "topo": "1.1.0" + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" } }, - "jsonwebtoken": { - "version": "7.1.9", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-7.1.9.tgz", - "integrity": "sha1-hHgE5SWL7FqUmajcSl56O64I1Yo=", + "request": { + "version": "2.83.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz", + "integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==", "dev": true, "requires": { - "joi": "6.10.1", - "jws": "3.1.4", - "lodash.once": "4.1.1", - "ms": "0.7.3", - "xtend": "4.0.1" + "aws-sign2": "0.7.0", + "aws4": "1.6.0", + "caseless": "0.12.0", + "combined-stream": "1.0.5", + "extend": "3.0.1", + "forever-agent": "0.6.1", + "form-data": "2.3.1", + "har-validator": "5.0.3", + "hawk": "6.0.2", + "http-signature": "1.2.0", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.17", + "oauth-sign": "0.8.2", + "performance-now": "2.1.0", + "qs": "6.5.1", + "safe-buffer": "5.1.1", + "stringstream": "0.0.5", + "tough-cookie": "2.3.3", + "tunnel-agent": "0.6.0", + "uuid": "3.1.0" } }, - "jwa": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.1.5.tgz", - "integrity": "sha1-oFUs4CIHQs1S4VN3SjKQXDDnVuU=", + "retry-request": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-3.0.0.tgz", + "integrity": "sha1-i60rHc8Ek4uyEeLO2GLlkbgvGRc=", "dev": true, "requires": { - "base64url": "2.0.0", - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.9", - "safe-buffer": "5.0.1" + "request": "2.83.0", + "through2": "2.0.3" } }, - "jws": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.1.4.tgz", - "integrity": "sha1-+ei5M46KhHJ31kRLFGT2GIDgUKI=", + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "sntp": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.0.2.tgz", + "integrity": "sha1-UGQRDwr4X3z9t9a2ekACjOUrSys=", "dev": true, "requires": { - "base64url": "2.0.0", - "jwa": "1.1.5", - "safe-buffer": "5.0.1" + "hoek": "4.2.0" } }, - "lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=", - "dev": true + "split-array-stream": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/split-array-stream/-/split-array-stream-1.0.3.tgz", + "integrity": "sha1-0rdajl4Ngk1S/eyLgiWDncLjXfo=", + "dev": true, + "requires": { + "async": "2.5.0", + "is-stream-ended": "0.1.3" + } }, - "moment": { - "version": "2.18.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.18.1.tgz", - "integrity": "sha1-w2GT3Tzhwu7SrbfIAtu8d6gbHA8=", + "sshpk": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", + "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", + "dev": true, + "requires": { + "asn1": "0.2.3", + "assert-plus": "1.0.0", + "bcrypt-pbkdf": "1.0.1", + "dashdash": "1.14.1", + "ecc-jsbn": "0.1.1", + "getpass": "0.1.7", + "jsbn": "0.1.1", + "tweetnacl": "0.14.5" + } + }, + "stream-events": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.2.tgz", + "integrity": "sha1-q/OfZsCJCk63lbyNXoWbJhW1kLI=", + "dev": true, + "requires": { + "stubs": "3.0.0" + } + }, + "stream-shift": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz", + "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", "dev": true }, - "ms": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.3.tgz", - "integrity": "sha1-cIFVpeROM/X9D8U+gdDUCpG+H/8=", + "string-format-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/string-format-obj/-/string-format-obj-1.1.0.tgz", + "integrity": "sha1-djVhCx7zlwE+hHi+mKFw4EmD0Gg=", "dev": true }, - "node-forge": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.1.tgz", - "integrity": "sha1-naYR6giYL0uUIGs760zJZl8gwwA=", + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "1.1.0", + "is-fullwidth-code-point": "1.0.0", + "strip-ansi": "3.0.1" + } + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "stringstream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", + "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", "dev": true }, - "safe-buffer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz", - "integrity": "sha1-0mPKVGls2KMGtcplUekt5XkY++c=", + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "stubs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz", + "integrity": "sha1-6NK6H6nJBXAwPAMLaQD31fiavls=", "dev": true }, + "through2": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "dev": true, + "requires": { + "readable-stream": "2.3.3", + "xtend": "4.0.1" + } + }, "topo": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/topo/-/topo-1.1.0.tgz", @@ -4523,21 +7046,132 @@ "dev": true, "requires": { "hoek": "2.16.3" + }, + "dependencies": { + "hoek": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", + "dev": true + } + } + }, + "tough-cookie": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", + "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", + "dev": true, + "requires": { + "punycode": "1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true, + "optional": true + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", + "dev": true + }, + "unique-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", + "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", + "dev": true, + "requires": { + "crypto-random-string": "1.0.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, + "uuid": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", + "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==", + "dev": true + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "1.3.0" } }, "websocket-driver": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.6.5.tgz", - "integrity": "sha1-XLJVbOuF9Dc8bYI4qmkchFThOjY=", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", + "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", "dev": true, "requires": { - "websocket-extensions": "0.1.1" + "http-parser-js": "0.4.9", + "websocket-extensions": "0.1.2" } }, "websocket-extensions": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.1.tgz", - "integrity": "sha1-domUmcGEtu91Q3fC27DNbLVdKec=", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.2.tgz", + "integrity": "sha1-Dhh4HeYpoYMIzhSBZQ9n/6JpOl0=", + "dev": true + }, + "window-size": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", + "integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY=", + "dev": true + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "1.0.2", + "strip-ansi": "3.0.1" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write-file-atomic": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.3.0.tgz", + "integrity": "sha512-xuPeK4OdjWqtfi59ylvVL0Yn35SF3zgcAcv7rBPFHVaEapaDr4GdGgm3j7ckTwH9wHL7fGmgfAnb0+THrHb8tA==", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "imurmurhash": "0.1.4", + "signal-exit": "3.0.2" + } + }, + "xdg-basedir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", + "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=", "dev": true }, "xtend": { @@ -4545,16 +7179,38 @@ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", "dev": true + }, + "y18n": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", + "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=", + "dev": true + }, + "yargs": { + "version": "3.32.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", + "integrity": "sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=", + "dev": true, + "requires": { + "camelcase": "2.1.1", + "cliui": "3.2.0", + "decamelize": "1.2.0", + "os-locale": "1.4.0", + "string-width": "1.0.2", + "window-size": "0.1.4", + "y18n": "3.2.1" + } } } }, "firebase-tools": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/firebase-tools/-/firebase-tools-3.9.1.tgz", - "integrity": "sha1-tivitFZBile/Goz4sBprj3j2wbA=", + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/firebase-tools/-/firebase-tools-3.13.1.tgz", + "integrity": "sha1-LKgqXN1eiHxf6WHNgBnkqYdiX1o=", "dev": true, "requires": { - "@google-cloud/functions-emulator": "1.0.0-alpha.21", + "@google-cloud/functions-emulator": "1.0.0-alpha.25", + "JSONStream": "1.3.1", "archiver": "0.16.0", "chalk": "1.1.3", "cjson": "0.3.3", @@ -4566,24 +7222,25 @@ "didyoumean": "1.2.1", "es6-set": "0.1.5", "exit-code": "1.0.2", - "filesize": "3.5.10", + "filesize": "3.5.11", "firebase": "2.4.2", "fs-extra": "0.23.1", "fstream-ignore": "1.0.5", + "glob": "7.1.2", + "google-auto-auth": "0.7.2", "inquirer": "0.12.0", - "jsonschema": "1.1.1", - "JSONStream": "1.3.1", - "jsonwebtoken": "5.7.0", + "jsonschema": "1.2.0", + "jsonwebtoken": "7.4.3", "lodash": "4.17.4", "open": "0.0.5", "ora": "0.2.3", "portfinder": "0.4.0", - "progress": "1.1.8", - "request": "2.81.0", - "rsvp": "3.6.1", - "semver": "5.3.0", - "superstatic": "4.1.0", - "tar": "2.2.1", + "progress": "2.0.0", + "request": "2.83.0", + "rsvp": "3.6.2", + "semver": "5.4.1", + "superstatic": "5.0.1", + "tar": "3.2.1", "tmp": "0.0.27", "universal-analytics": "0.3.11", "update-notifier": "0.5.0", @@ -4598,6 +7255,19 @@ "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=", "dev": true }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, "configstore": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/configstore/-/configstore-1.4.0.tgz", @@ -4673,35 +7343,18 @@ "rimraf": "2.6.1" } }, - "jsonwebtoken": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-5.7.0.tgz", - "integrity": "sha1-HJD5qGzlt0j1+XnBK3BAK0r83bQ=", - "dev": true, - "requires": { - "jws": "3.1.4", - "ms": "0.7.3", - "xtend": "4.0.1" - } - }, - "ms": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.3.tgz", - "integrity": "sha1-cIFVpeROM/X9D8U+gdDUCpG+H/8=", - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, "pkginfo": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz", "integrity": "sha1-Wyn2qB9wcXFC4J52W76rl7T4HiE=", "dev": true }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, "winston": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/winston/-/winston-1.1.2.tgz", @@ -4800,12 +7453,12 @@ } }, "flat-cache": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.2.2.tgz", - "integrity": "sha1-+oZxTnLCHbiGAXYezy9VXRq8a5Y=", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz", + "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=", "dev": true, "requires": { - "circular-json": "0.3.1", + "circular-json": "0.3.3", "del": "2.2.2", "graceful-fs": "4.1.11", "write": "0.2.1" @@ -4845,27 +7498,20 @@ "dev": true }, "form-data": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", - "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.1.tgz", + "integrity": "sha1-b7lPvXGIUwbXPRXMSX/kzE7NRL8=", "dev": true, "requires": { "asynckit": "0.4.0", "combined-stream": "1.0.5", - "mime-types": "2.1.15" + "mime-types": "2.1.17" } }, - "formidable": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.1.1.tgz", - "integrity": "sha1-lriIb3w8NQi5Mta9cMTTqI818ak=", - "dev": true, - "optional": true - }, "forwarded": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.0.tgz", - "integrity": "sha1-Ge+YdMSuHCl7zweP3mOgm2aoQ2M=", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=", "dev": true, "optional": true }, @@ -4904,7 +7550,7 @@ "requires": { "graceful-fs": "4.1.11", "jsonfile": "3.0.1", - "universalify": "0.1.0" + "universalify": "0.1.1" }, "dependencies": { "jsonfile": { @@ -4959,7 +7605,7 @@ "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "dev": true, "requires": { - "aproba": "1.1.2", + "aproba": "1.2.0", "console-control-strings": "1.1.0", "has-unicode": "2.0.1", "object-assign": "4.1.1", @@ -4967,14 +7613,6 @@ "string-width": "1.0.2", "strip-ansi": "3.0.1", "wide-align": "1.1.2" - }, - "dependencies": { - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - } } }, "gaze": { @@ -4987,129 +7625,28 @@ } }, "gcp-metadata": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-0.2.0.tgz", - "integrity": "sha1-Ytr8pl86YxvIzi7Dt3Zh9fk4ego=", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-0.3.1.tgz", + "integrity": "sha512-5kJPX/RXuqoLmHiOOgkSDk/LI0QaXpEvZ3pvQP4ifjGGDKZKVSOjL/GcDjXA5kLxppFCOjmmsu0Uoop9d1upaQ==", "dev": true, "requires": { "extend": "3.0.1", - "retry-request": "2.0.5" + "retry-request": "3.1.0" } }, "gcs-resumable-upload": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/gcs-resumable-upload/-/gcs-resumable-upload-0.8.0.tgz", - "integrity": "sha1-023swVIzRGCC249J4hAndE80+CQ=", + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/gcs-resumable-upload/-/gcs-resumable-upload-0.8.2.tgz", + "integrity": "sha512-PBl1OFABYxubxfYPh000I0+JLbQzBRtNqxzgxYboIQk2tdw7BvjJ2dVukk3YH4QM6GiUwqItyNqWBuxjLH8GhA==", "dev": true, "requires": { "buffer-equal": "1.0.0", - "configstore": "3.1.0", - "google-auto-auth": "0.6.1", + "configstore": "3.1.1", + "google-auto-auth": "0.7.2", "pumpify": "1.3.5", - "request": "2.81.0", + "request": "2.83.0", "stream-events": "1.0.2", "through2": "2.0.3" - }, - "dependencies": { - "caseless": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", - "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=", - "dev": true - }, - "gcp-metadata": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-0.1.0.tgz", - "integrity": "sha1-q+IfHqMk3Qs0o/BsqBdj+x7uN9k=", - "dev": true, - "requires": { - "extend": "3.0.1", - "retry-request": "1.3.2" - } - }, - "google-auto-auth": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/google-auto-auth/-/google-auto-auth-0.6.1.tgz", - "integrity": "sha1-wF2CDpRUc57PKKiJLuqz0WJPLLM=", - "dev": true, - "requires": { - "async": "2.5.0", - "gcp-metadata": "0.1.0", - "google-auth-library": "0.10.0", - "object-assign": "3.0.0", - "request": "2.81.0" - } - }, - "har-validator": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz", - "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=", - "dev": true, - "requires": { - "chalk": "1.1.3", - "commander": "2.11.0", - "is-my-json-valid": "2.16.0", - "pinkie-promise": "2.0.1" - } - }, - "node-uuid": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", - "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=", - "dev": true - }, - "qs": { - "version": "6.3.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.3.2.tgz", - "integrity": "sha1-51vV9uJoEioqDgvaYwslUMFmUCw=", - "dev": true - }, - "retry-request": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-1.3.2.tgz", - "integrity": "sha1-Wa0k5x+K4/MS1fe0vPRnpeWle9Y=", - "dev": true, - "requires": { - "request": "2.76.0", - "through2": "2.0.3" - }, - "dependencies": { - "request": { - "version": "2.76.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.76.0.tgz", - "integrity": "sha1-vkRQWv73A2CgQ2lVEGvjlF2VVg4=", - "dev": true, - "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.6.0", - "caseless": "0.11.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.1.4", - "har-validator": "2.0.6", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.15", - "node-uuid": "1.4.8", - "oauth-sign": "0.8.2", - "qs": "6.3.2", - "stringstream": "0.0.5", - "tough-cookie": "2.3.2", - "tunnel-agent": "0.4.3" - } - } - } - }, - "tunnel-agent": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", - "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=", - "dev": true - } } }, "generate-function": { @@ -5134,7 +7671,7 @@ "dev": true, "requires": { "ast-module-types": "2.3.2", - "node-source-walk": "3.2.1" + "node-source-walk": "3.3.0" } }, "get-caller-file": { @@ -5152,7 +7689,7 @@ "hosted-git-info": "2.5.0", "meow": "3.7.0", "normalize-package-data": "2.4.0", - "parse-github-repo-url": "1.4.0", + "parse-github-repo-url": "1.4.1", "through2": "2.0.3" } }, @@ -5166,8 +7703,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true, - "optional": true + "dev": true }, "getpass": { "version": "0.1.7", @@ -5176,14 +7712,6 @@ "dev": true, "requires": { "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - } } }, "git-raw-commits": { @@ -5195,8 +7723,29 @@ "dargs": "4.1.0", "lodash.template": "4.4.0", "meow": "3.7.0", - "split2": "2.1.1", + "split2": "2.2.0", "through2": "2.0.3" + }, + "dependencies": { + "lodash.template": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.4.0.tgz", + "integrity": "sha1-5zoDhcg1VZF0bgILmWecaQ5o+6A=", + "dev": true, + "requires": { + "lodash._reinterpolate": "3.0.0", + "lodash.templatesettings": "4.1.0" + } + }, + "lodash.templatesettings": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.1.0.tgz", + "integrity": "sha1-K01OlbpEDZFf8IvImeRVNmZxMxY=", + "dev": true, + "requires": { + "lodash._reinterpolate": "3.0.0" + } + } } }, "git-remote-origin-url": { @@ -5210,13 +7759,13 @@ } }, "git-semver-tags": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-1.2.0.tgz", - "integrity": "sha1-sx/QLIq1eL1sm1ysyl4cZMEXesE=", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-1.2.2.tgz", + "integrity": "sha512-fhINopzKBQ8m6YlQt7gPf6T6hFnTF84O7U+8kYJmfjjKk7gbmKGj+BLcKNWi+japPbBwCeXXnfKwThpJpR9ZnQ==", "dev": true, "requires": { "meow": "3.7.0", - "semver": "5.3.0" + "semver": "5.4.1" } }, "gitconfiglocal": { @@ -5228,12 +7777,6 @@ "ini": "1.3.4" } }, - "github-url-from-git": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/github-url-from-git/-/github-url-from-git-1.5.0.tgz", - "integrity": "sha1-+YX+3MCpqledyI16/waNVcxiUaA=", - "dev": true - }, "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", @@ -5392,7 +7935,7 @@ "homedir-polyfill": "1.0.1", "ini": "1.3.4", "is-windows": "0.2.0", - "which": "1.2.14" + "which": "1.3.0" } }, "globby": { @@ -5407,14 +7950,6 @@ "object-assign": "4.1.1", "pify": "2.3.0", "pinkie-promise": "2.0.1" - }, - "dependencies": { - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - } } }, "globjoin": { @@ -5553,19 +8088,19 @@ "gtoken": "1.2.2", "jws": "3.1.4", "lodash.noop": "3.0.1", - "request": "2.81.0" + "request": "2.83.0" } }, "google-auto-auth": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/google-auto-auth/-/google-auto-auth-0.7.1.tgz", - "integrity": "sha1-yCYERJEt2M7szYOHYdVvRik3vQI=", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/google-auto-auth/-/google-auto-auth-0.7.2.tgz", + "integrity": "sha512-ux2n2AE2g3+vcLXwL4dP/M12SFMRX5dzCzBfhAEkTeAB7dpyGdOIEj7nmUx0BHKaCcUQrRWg9kT63X/Mmtk1+A==", "dev": true, "requires": { "async": "2.5.0", - "gcp-metadata": "0.2.0", + "gcp-metadata": "0.3.1", "google-auth-library": "0.10.0", - "request": "2.81.0" + "request": "2.83.0" } }, "google-closure-compiler": { @@ -5577,6 +8112,27 @@ "chalk": "1.1.3", "vinyl": "2.1.0", "vinyl-sourcemaps-apply": "0.2.1" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } } }, "google-p12-pem": { @@ -5589,16 +8145,16 @@ } }, "google-proto-files": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/google-proto-files/-/google-proto-files-0.12.0.tgz", - "integrity": "sha1-pJB1wfzCvYpIAYaDHCl2Bbl3Sys=", + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/google-proto-files/-/google-proto-files-0.12.1.tgz", + "integrity": "sha512-d+BiTMpYP4/pN+Kgi0lWE+2/GaVN4jy8LCabjo2Wd16Ykm5oRO5bI40bo8u2U4rN/GpZkMyfxZAjWVPRjGe3nQ==", "dev": true, "optional": true }, "googleapis": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-19.0.0.tgz", - "integrity": "sha1-hwDrFClHd+DBlhUgUf1jZwYd3oU=", + "version": "20.1.0", + "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-20.1.0.tgz", + "integrity": "sha512-UZYpUKPcwt28tZIvC+sT7yHtl56bMxnePNJBtZ3tG0OrQ1KegukirKRRuIxPavNCOcVC/ka5j/RRDhEIrYf5UA==", "dev": true, "optional": true, "requires": { @@ -5620,9 +8176,9 @@ } }, "got": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/got/-/got-7.0.0.tgz", - "integrity": "sha1-gtQ59nY82xyIIbejquJ4TIjDuNM=", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/got/-/got-7.1.0.tgz", + "integrity": "sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw==", "dev": true, "optional": true, "requires": { @@ -5634,11 +8190,12 @@ "is-stream": "1.1.0", "isurl": "1.0.0", "lowercase-keys": "1.0.0", - "p-cancelable": "0.2.0", + "p-cancelable": "0.3.0", "p-timeout": "1.2.0", "safe-buffer": "5.1.1", "timed-out": "4.0.1", - "url-parse-lax": "1.0.0" + "url-parse-lax": "1.0.0", + "url-to-options": "1.0.1" } }, "graceful-fs": { @@ -5663,15 +8220,15 @@ } }, "grpc": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/grpc/-/grpc-1.3.8.tgz", - "integrity": "sha1-N9ETwaxAKtFO3KfTjc+QYkwueeQ=", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/grpc/-/grpc-1.4.1.tgz", + "integrity": "sha1-PuSoNGphPygjkoyfj5kIG2No7Hw=", "dev": true, "optional": true, "requires": { "arguejs": "0.2.3", "lodash": "4.17.4", - "nan": "2.6.2", + "nan": "2.7.0", "node-pre-gyp": "0.6.36", "protobufjs": "5.0.2" }, @@ -5685,7 +8242,7 @@ "requires": { "mkdirp": "0.5.1", "nopt": "4.0.1", - "npmlog": "4.1.0", + "npmlog": "4.1.2", "rc": "1.2.1", "request": "2.81.0", "rimraf": "2.6.1", @@ -5759,9 +8316,9 @@ } }, "npmlog": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.0.tgz", - "integrity": "sha1-3Fm+6F9k8A7UJO+yrweD3yXRwLU=", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha1-CKfyqL9zRgR3mp76StXMcXq7lUs=", "dev": true, "optional": true, "requires": { @@ -5779,7 +8336,7 @@ "optional": true, "requires": { "delegates": "1.0.0", - "readable-stream": "2.2.10" + "readable-stream": "2.3.2" }, "dependencies": { "delegates": { @@ -5790,9 +8347,9 @@ "optional": true }, "readable-stream": { - "version": "2.2.10", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.10.tgz", - "integrity": "sha1-7/5yu3yITA3TNeI3nVJhltnQEe4=", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.2.tgz", + "integrity": "sha1-WgTfBeT1f+Pw3Gj90R3FyXx+b00=", "dev": true, "optional": true, "requires": { @@ -5800,8 +8357,8 @@ "inherits": "2.0.3", "isarray": "1.0.0", "process-nextick-args": "1.0.7", - "safe-buffer": "5.1.0", - "string_decoder": "1.0.1", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", "util-deprecate": "1.0.2" }, "dependencies": { @@ -5834,19 +8391,19 @@ "optional": true }, "safe-buffer": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.0.tgz", - "integrity": "sha1-/kyEYDl/nqqqWOc75GJzQIpF4iM=", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM=", "dev": true }, "string_decoder": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.1.tgz", - "integrity": "sha1-YuIA8DmVWmgQ2N8KM//A8BNmLZg=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=", "dev": true, "optional": true, "requires": { - "safe-buffer": "5.1.0" + "safe-buffer": "5.1.1" } }, "util-deprecate": { @@ -6052,11 +8609,11 @@ "oauth-sign": "0.8.2", "performance-now": "0.2.0", "qs": "6.4.0", - "safe-buffer": "5.1.0", + "safe-buffer": "5.1.1", "stringstream": "0.0.5", "tough-cookie": "2.3.2", "tunnel-agent": "0.6.0", - "uuid": "3.0.1" + "uuid": "3.1.0" }, "dependencies": { "aws-sign2": { @@ -6250,7 +8807,7 @@ "requires": { "assert-plus": "0.2.0", "jsprim": "1.4.0", - "sshpk": "1.13.0" + "sshpk": "1.13.1" }, "dependencies": { "assert-plus": { @@ -6306,9 +8863,9 @@ } }, "sshpk": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.0.tgz", - "integrity": "sha1-/yo+T9BEl1Vf7Zezmg/YL6+zozw=", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", + "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", "dev": true, "optional": true, "requires": { @@ -6318,7 +8875,6 @@ "dashdash": "1.14.1", "ecc-jsbn": "0.1.1", "getpass": "0.1.7", - "jodid25519": "1.0.2", "jsbn": "0.1.1", "tweetnacl": "0.14.5" }, @@ -6376,16 +8932,6 @@ "assert-plus": "1.0.0" } }, - "jodid25519": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz", - "integrity": "sha1-BtSRIlUJNBlHfUJWM2BuDpB4KWc=", - "dev": true, - "optional": true, - "requires": { - "jsbn": "0.1.1" - } - }, "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", @@ -6464,9 +9010,9 @@ "optional": true }, "safe-buffer": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.0.tgz", - "integrity": "sha1-/kyEYDl/nqqqWOc75GJzQIpF4iM=", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM=", "dev": true }, "stringstream": { @@ -6502,13 +9048,13 @@ "dev": true, "optional": true, "requires": { - "safe-buffer": "5.1.0" + "safe-buffer": "5.1.1" } }, "uuid": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.1.tgz", - "integrity": "sha1-ZUS7ot/ajBzxfmKaOjBeK7H+5sE=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", + "integrity": "sha1-PdPT55Crwk17DToDT/q6vijrvAQ=", "dev": true, "optional": true } @@ -6573,23 +9119,23 @@ "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", "dev": true, "requires": { - "brace-expansion": "1.1.7" + "brace-expansion": "1.1.8" }, "dependencies": { "brace-expansion": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz", - "integrity": "sha1-Pv/DxQ4ABTH7cg6v+A8K6O8jz1k=", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", "dev": true, "requires": { - "balanced-match": "0.4.2", + "balanced-match": "1.0.0", "concat-map": "0.0.1" }, "dependencies": { "balanced-match": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", - "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, "concat-map": { @@ -6695,7 +9241,7 @@ "fstream": "1.0.11", "fstream-ignore": "1.0.5", "once": "1.4.0", - "readable-stream": "2.2.10", + "readable-stream": "2.3.2", "rimraf": "2.6.1", "tar": "2.2.1", "uid-number": "0.0.6" @@ -6772,24 +9318,24 @@ "dev": true, "optional": true, "requires": { - "brace-expansion": "1.1.7" + "brace-expansion": "1.1.8" }, "dependencies": { "brace-expansion": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz", - "integrity": "sha1-Pv/DxQ4ABTH7cg6v+A8K6O8jz1k=", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", + "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", "dev": true, "optional": true, "requires": { - "balanced-match": "0.4.2", + "balanced-match": "1.0.0", "concat-map": "0.0.1" }, "dependencies": { "balanced-match": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", - "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true, "optional": true }, @@ -6826,9 +9372,9 @@ } }, "readable-stream": { - "version": "2.2.10", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.10.tgz", - "integrity": "sha1-7/5yu3yITA3TNeI3nVJhltnQEe4=", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.2.tgz", + "integrity": "sha1-WgTfBeT1f+Pw3Gj90R3FyXx+b00=", "dev": true, "optional": true, "requires": { @@ -6836,8 +9382,8 @@ "inherits": "2.0.3", "isarray": "1.0.0", "process-nextick-args": "1.0.7", - "safe-buffer": "5.1.0", - "string_decoder": "1.0.1", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", "util-deprecate": "1.0.2" }, "dependencies": { @@ -6870,19 +9416,19 @@ "optional": true }, "safe-buffer": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.0.tgz", - "integrity": "sha1-/kyEYDl/nqqqWOc75GJzQIpF4iM=", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha1-iTMSr2myEj3vcfV4iQAWce6yyFM=", "dev": true }, "string_decoder": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.1.tgz", - "integrity": "sha1-YuIA8DmVWmgQ2N8KM//A8BNmLZg=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha1-D8Z9fBQYJd6UKC3VNr7GubzoYKs=", "dev": true, "optional": true, "requires": { - "safe-buffer": "5.1.0" + "safe-buffer": "5.1.1" } }, "util-deprecate": { @@ -6915,8 +9461,8 @@ "requires": { "google-p12-pem": "0.1.2", "jws": "3.1.4", - "mime": "1.3.6", - "request": "2.81.0" + "mime": "1.4.1", + "request": "2.83.0" } }, "gulp": { @@ -6929,7 +9475,7 @@ "chalk": "1.1.3", "deprecated": "0.0.1", "gulp-util": "3.0.8", - "interpret": "1.0.3", + "interpret": "1.0.4", "liftoff": "2.3.0", "minimist": "1.2.0", "orchestrator": "0.3.8", @@ -6940,11 +9486,30 @@ "vinyl-fs": "0.3.14" }, "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, "semver": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz", "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=", "dev": true + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true } } }, @@ -6984,6 +9549,22 @@ "supports-color": "0.2.0" } }, + "clone-stats": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", + "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=", + "dev": true + }, + "dateformat": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", + "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", + "dev": true, + "requires": { + "get-stdin": "4.0.1", + "meow": "3.7.0" + } + }, "gulp-util": { "version": "2.2.20", "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-2.2.20.tgz", @@ -7147,21 +9728,21 @@ } }, "gulp-clean-css": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/gulp-clean-css/-/gulp-clean-css-3.6.0.tgz", - "integrity": "sha1-Q08rgMVapFJUzaMCB+GhFm8NHlI=", + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/gulp-clean-css/-/gulp-clean-css-3.9.0.tgz", + "integrity": "sha512-CsqaSO2ZTMQI/WwbWloZWBudhsRMKgxBthzxt4bbcbWrjOY4pRFziyK9IH6YbTpaWAPKEwWpopPkpiAEoDofxw==", "dev": true, "requires": { - "clean-css": "4.1.6", + "clean-css": "4.1.9", "gulp-util": "3.0.8", "through2": "2.0.3", "vinyl-sourcemaps-apply": "0.2.1" } }, "gulp-cli": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-1.3.0.tgz", - "integrity": "sha1-pr+7i+NTQb4pCuRc0+QBBxIW7dQ=", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-1.4.0.tgz", + "integrity": "sha1-b1u+LNC9tISdEs+eEkalhh+LT4g=", "dev": true, "requires": { "archy": "1.0.0", @@ -7169,7 +9750,7 @@ "copy-props": "1.6.0", "fancy-log": "1.3.0", "gulplog": "1.0.0", - "interpret": "1.0.3", + "interpret": "1.0.4", "liftoff": "2.3.0", "lodash.isfunction": "3.0.8", "lodash.isplainobject": "4.0.6", @@ -7177,44 +9758,31 @@ "matchdep": "1.0.1", "mute-stdout": "1.0.0", "pretty-hrtime": "1.0.3", - "semver-greatest-satisfied-range": "1.0.0", + "semver-greatest-satisfied-range": "1.1.0", "tildify": "1.2.0", - "v8flags": "2.1.1", - "wreck": "6.3.0", - "yargs": "3.32.0" - }, - "dependencies": { - "cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", - "dev": true, - "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wrap-ansi": "2.1.0" - } - }, - "window-size": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", - "integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY=", - "dev": true - }, - "yargs": { - "version": "3.32.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", - "integrity": "sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=", + "v8flags": "2.1.1", + "wreck": "6.3.0", + "yargs": "3.32.0" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "camelcase": "2.1.1", - "cliui": "3.2.0", - "decamelize": "1.2.0", - "os-locale": "1.4.0", - "string-width": "1.0.2", - "window-size": "0.1.4", - "y18n": "3.2.1" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true } } }, @@ -7237,7 +9805,7 @@ "integrity": "sha1-5fHzkoxtlf2WVYw27D2dDeSm7Oo=", "dev": true, "requires": { - "mime-types": "2.1.15", + "mime-types": "2.1.17", "negotiator": "0.5.3" } }, @@ -7254,7 +9822,7 @@ "dev": true, "requires": { "bytes": "2.1.0", - "content-type": "1.0.2", + "content-type": "1.0.4", "debug": "2.2.0", "depd": "1.0.1", "http-errors": "1.3.1", @@ -7279,7 +9847,7 @@ "requires": { "accepts": "1.2.13", "bytes": "2.1.0", - "compressible": "2.0.10", + "compressible": "2.0.12", "debug": "2.2.0", "on-headers": "1.0.1", "vary": "1.0.1" @@ -7296,7 +9864,7 @@ "bytes": "2.1.0", "compression": "1.5.2", "connect-timeout": "1.6.2", - "content-type": "1.0.2", + "content-type": "1.0.4", "cookie": "0.1.3", "cookie-parser": "1.3.5", "cookie-signature": "1.0.6", @@ -7308,11 +9876,11 @@ "finalhandler": "0.4.0", "fresh": "0.3.0", "http-errors": "1.3.1", - "method-override": "2.3.9", + "method-override": "2.3.10", "morgan": "1.6.1", "multiparty": "3.3.2", "on-headers": "1.0.1", - "parseurl": "1.3.1", + "parseurl": "1.3.2", "pause": "0.1.0", "qs": "4.0.0", "response-time": "2.3.2", @@ -7382,7 +9950,7 @@ "dev": true, "requires": { "inherits": "2.0.3", - "statuses": "1.3.1" + "statuses": "1.4.0" } }, "iconv-lite": { @@ -7466,7 +10034,7 @@ "dev": true, "requires": { "debug": "2.2.0", - "depd": "1.1.0", + "depd": "1.1.1", "destroy": "1.0.4", "escape-html": "1.0.3", "etag": "1.7.0", @@ -7480,9 +10048,9 @@ }, "dependencies": { "depd": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.0.tgz", - "integrity": "sha1-4b2Cxqq2ztlluXuIsX7T5SjKGMM=", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.1.tgz", + "integrity": "sha1-V4O04cRZ8G+lyif5kfPQbnoxA1k=", "dev": true }, "escape-html": { @@ -7506,7 +10074,7 @@ "dev": true, "requires": { "escape-html": "1.0.3", - "parseurl": "1.3.1", + "parseurl": "1.3.2", "send": "0.13.2" }, "dependencies": { @@ -7527,208 +10095,17 @@ } }, "gulp-conventional-changelog": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/gulp-conventional-changelog/-/gulp-conventional-changelog-1.1.4.tgz", - "integrity": "sha512-r400pFwUNhuginHAlbaZT4f0LrPtyzdpXzb5vkEwHQIub+SJD0lMFhJFFgqSZa0580skZJcb86ZvDA4W08/cGA==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/gulp-conventional-changelog/-/gulp-conventional-changelog-1.1.6.tgz", + "integrity": "sha512-zJ3ZfEukUqzLmFTWA5r8k9D4UAsymq+zsgINJmTRhw+H6Q1O9iCgREeye0gxicmusm6z6jI0fz5wLvlS66cmFg==", "dev": true, "requires": { "add-stream": "1.0.0", "concat-stream": "1.6.0", - "conventional-changelog": "1.1.4", + "conventional-changelog": "1.1.6", "gulp-util": "3.0.8", "object-assign": "4.1.1", "through2": "2.0.3" - }, - "dependencies": { - "conventional-changelog": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-1.1.4.tgz", - "integrity": "sha1-EIvHUMKjF+IA4vm0E8qqH4x++js=", - "dev": true, - "requires": { - "conventional-changelog-angular": "1.4.0", - "conventional-changelog-atom": "0.1.0", - "conventional-changelog-codemirror": "0.1.0", - "conventional-changelog-core": "1.9.0", - "conventional-changelog-ember": "0.2.6", - "conventional-changelog-eslint": "0.1.0", - "conventional-changelog-express": "0.1.0", - "conventional-changelog-jquery": "0.1.0", - "conventional-changelog-jscs": "0.1.0", - "conventional-changelog-jshint": "0.1.0" - } - }, - "conventional-changelog-angular": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-1.4.0.tgz", - "integrity": "sha512-ukKX22lJl9ewogze1hKbBuff/dGMG2uyGpOhhw0ehhlv6GtdeCxj51YfGOZ5qC89WwsHT7SDXFzBKidwH3pwmQ==", - "dev": true, - "requires": { - "compare-func": "1.3.2", - "github-url-from-git": "1.5.0", - "q": "1.5.0", - "read-pkg-up": "2.0.0" - } - }, - "conventional-changelog-core": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-1.9.0.tgz", - "integrity": "sha1-3l37wJGEdlZQjUo4njXJobxJ5/Q=", - "dev": true, - "requires": { - "conventional-changelog-writer": "1.4.1", - "conventional-commits-parser": "1.3.0", - "dateformat": "1.0.12", - "get-pkg-repo": "1.4.0", - "git-raw-commits": "1.2.0", - "git-remote-origin-url": "2.0.0", - "git-semver-tags": "1.2.0", - "lodash": "4.17.4", - "normalize-package-data": "2.4.0", - "q": "1.5.0", - "read-pkg": "1.1.0", - "read-pkg-up": "1.0.1", - "through2": "2.0.3" - }, - "dependencies": { - "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "dev": true, - "requires": { - "path-exists": "2.1.0", - "pinkie-promise": "2.0.1" - } - }, - "load-json-file": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "parse-json": "2.2.0", - "pify": "2.3.0", - "pinkie-promise": "2.0.1", - "strip-bom": "2.0.0" - } - }, - "path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "pify": "2.3.0", - "pinkie-promise": "2.0.1" - } - }, - "read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", - "dev": true, - "requires": { - "load-json-file": "1.1.0", - "normalize-package-data": "2.4.0", - "path-type": "1.1.0" - } - }, - "read-pkg-up": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", - "dev": true, - "requires": { - "find-up": "1.1.2", - "read-pkg": "1.1.0" - } - }, - "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "dev": true, - "requires": { - "is-utf8": "0.2.1" - } - } - } - }, - "conventional-changelog-ember": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/conventional-changelog-ember/-/conventional-changelog-ember-0.2.6.tgz", - "integrity": "sha1-i3NVQZ9RJ0k8TFYkc6svx5LxwrY=", - "dev": true, - "requires": { - "q": "1.5.0" - } - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "2.0.0" - } - }, - "load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "parse-json": "2.2.0", - "pify": "2.3.0", - "strip-bom": "3.0.0" - } - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, - "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "dev": true, - "requires": { - "pify": "2.3.0" - } - }, - "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "dev": true, - "requires": { - "load-json-file": "2.0.0", - "normalize-package-data": "2.4.0", - "path-type": "2.0.0" - } - }, - "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "dev": true, - "requires": { - "find-up": "2.1.0", - "read-pkg": "2.0.0" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - } } }, "gulp-dom": { @@ -7742,6 +10119,41 @@ "through2": "2.0.1" }, "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "clone": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.2.tgz", + "integrity": "sha1-Jgt6meux7f4kdTgXX3gyQ8sZ0Uk=", + "dev": true + }, + "clone-stats": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", + "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=", + "dev": true + }, + "dateformat": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", + "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", + "dev": true, + "requires": { + "get-stdin": "4.0.1", + "meow": "3.7.0" + } + }, "gulp-util": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.7.tgz", @@ -7768,43 +10180,11 @@ "vinyl": "0.5.3" } }, - "lodash.keys": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", - "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", - "dev": true, - "requires": { - "lodash._getnative": "3.9.1", - "lodash.isarguments": "3.1.0", - "lodash.isarray": "3.0.4" - } - }, - "lodash.template": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz", - "integrity": "sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8=", - "dev": true, - "requires": { - "lodash._basecopy": "3.0.1", - "lodash._basetostring": "3.0.1", - "lodash._basevalues": "3.0.0", - "lodash._isiterateecall": "3.0.9", - "lodash._reinterpolate": "3.0.0", - "lodash.escape": "3.2.0", - "lodash.keys": "3.1.2", - "lodash.restparam": "3.6.1", - "lodash.templatesettings": "3.1.1" - } - }, - "lodash.templatesettings": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz", - "integrity": "sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU=", - "dev": true, - "requires": { - "lodash._reinterpolate": "3.0.0", - "lodash.escape": "3.2.0" - } + "object-assign": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", + "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=", + "dev": true }, "readable-stream": { "version": "2.0.6", @@ -7820,12 +10200,24 @@ "util-deprecate": "1.0.2" } }, + "replace-ext": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", + "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=", + "dev": true + }, "string_decoder": { "version": "0.10.31", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", "dev": true }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, "through2": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.1.tgz", @@ -7860,9 +10252,9 @@ } }, "gulp-highlight-files": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/gulp-highlight-files/-/gulp-highlight-files-0.0.4.tgz", - "integrity": "sha1-jP1kIJ5TysUs17j2qLXZ6nl5UXI=", + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/gulp-highlight-files/-/gulp-highlight-files-0.0.5.tgz", + "integrity": "sha512-lGLApyg6ycB45MWsLx9WbsAv0iTPxGTUH+Lq8evAXUXCFBPITG2JmLxQ3OYWA1/AOItDeAX5yza1HdLlaIXIPA==", "dev": true, "requires": { "gulp-util": "3.0.8", @@ -7878,18 +10270,10 @@ "requires": { "bufferstreams": "1.1.1", "gulp-util": "3.0.8", - "html-minifier": "3.5.2", + "html-minifier": "3.5.6", "object-assign": "4.1.1", "readable-stream": "2.3.3", "tryit": "1.0.3" - }, - "dependencies": { - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - } } }, "gulp-if": { @@ -7962,7 +10346,7 @@ "array-uniq": "1.0.3", "beeper": "1.1.1", "chalk": "1.1.3", - "dateformat": "2.0.0", + "dateformat": "2.2.0", "fancy-log": "1.3.0", "gulplog": "1.0.0", "has-gulplog": "0.1.0", @@ -7978,49 +10362,48 @@ "vinyl": "0.5.3" }, "dependencies": { - "dateformat": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.0.0.tgz", - "integrity": "sha1-J0Pjq7XD/CRi5SfcpEXgTp9N7hc=", - "dev": true - }, - "lodash.keys": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", - "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "lodash._getnative": "3.9.1", - "lodash.isarguments": "3.1.0", - "lodash.isarray": "3.0.4" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" } }, - "lodash.template": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz", - "integrity": "sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8=", - "dev": true, - "requires": { - "lodash._basecopy": "3.0.1", - "lodash._basetostring": "3.0.1", - "lodash._basevalues": "3.0.0", - "lodash._isiterateecall": "3.0.9", - "lodash._reinterpolate": "3.0.0", - "lodash.escape": "3.2.0", - "lodash.keys": "3.1.2", - "lodash.restparam": "3.6.1", - "lodash.templatesettings": "3.1.1" - } + "clone": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.2.tgz", + "integrity": "sha1-Jgt6meux7f4kdTgXX3gyQ8sZ0Uk=", + "dev": true }, - "lodash.templatesettings": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz", - "integrity": "sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU=", - "dev": true, - "requires": { - "lodash._reinterpolate": "3.0.0", - "lodash.escape": "3.2.0" - } + "clone-stats": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", + "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=", + "dev": true + }, + "object-assign": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", + "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=", + "dev": true + }, + "replace-ext": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", + "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=", + "dev": true + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true }, "vinyl": { "version": "0.5.3", @@ -8051,9 +10434,9 @@ "dev": true }, "handlebars": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.10.tgz", - "integrity": "sha1-PTDHGLCaPZbyPqTMH0A8TTup/08=", + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.0.11.tgz", + "integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=", "dev": true, "requires": { "async": "1.5.2", @@ -8080,19 +10463,19 @@ } }, "har-schema": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", - "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", "dev": true }, "har-validator": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", - "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", + "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", "dev": true, "requires": { - "ajv": "4.11.8", - "har-schema": "1.0.5" + "ajv": "5.2.3", + "har-schema": "2.0.0" } }, "has-ansi": { @@ -8143,20 +10526,20 @@ } }, "has-symbol-support-x": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.0.tgz", - "integrity": "sha512-F1NtLDtW9NyUrS3faUcI1yVFHCTXyzPb1jfrZBQi5NHxFPlXxZnFLFGzfA2DsdmgCxv2MZ0+bfcgC4EZTmk4SQ==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.1.tgz", + "integrity": "sha512-JkaetveU7hFbqnAC1EV1sF4rlojU2D4Usc5CmS69l6NfmPDnpnFUegzFg33eDkkpNCxZ0mQp65HwUDrNFS/8MA==", "dev": true, "optional": true }, "has-to-string-tag-x": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.0.tgz", - "integrity": "sha512-R3OdOP9j6AH5hS1yXeu9wAS+iKSZQx/CC6aMdN6WiaqPlBoA2S+47MtoMsZgKr2m0eAJ+73WWGX0RaFFE5XWKA==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", + "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", "dev": true, "optional": true, "requires": { - "has-symbol-support-x": "1.4.0" + "has-symbol-support-x": "1.4.1" } }, "has-unicode": { @@ -8175,15 +10558,15 @@ } }, "hawk": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", - "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", + "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", "dev": true, "requires": { - "boom": "2.10.1", - "cryptiles": "2.0.5", - "hoek": "2.16.3", - "sntp": "1.0.9" + "boom": "4.3.1", + "cryptiles": "3.1.2", + "hoek": "4.2.0", + "sntp": "2.0.2" } }, "he": { @@ -8198,7 +10581,7 @@ "integrity": "sha1-lTWXMZfBRLCWE81l0xfvGZY70C0=", "dev": true, "requires": { - "no-case": "2.3.1", + "no-case": "2.3.2", "upper-case": "1.1.3" } }, @@ -8209,9 +10592,9 @@ "dev": true }, "hoek": { - "version": "2.16.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", - "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz", + "integrity": "sha512-v0XCLxICi9nPfYrS9RL8HbYnXi9obYAeLbSP00BmnZwCK9+Ih9WOjoZ8YoHCoav2csqn4FOz4Orldsy2dmDwmQ==", "dev": true }, "home-dir": { @@ -8245,38 +10628,35 @@ } }, "html-minifier": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.2.tgz", - "integrity": "sha1-1zvD/0SJQkCIGM5gm/P7DqfvTrc=", + "version": "3.5.6", + "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.6.tgz", + "integrity": "sha512-88FjtKrlak2XjczhxrBomgzV4jmGzM3UnHRBScRkJcmcRum0kb+IwhVAETJ8AVp7j0p3xugjSaw9L+RmI5/QOA==", "dev": true, "requires": { "camel-case": "3.0.0", - "clean-css": "4.1.6", - "commander": "2.9.0", + "clean-css": "4.1.9", + "commander": "2.11.0", "he": "1.1.1", "ncname": "1.0.0", "param-case": "2.1.1", "relateurl": "0.2.7", - "uglify-js": "3.0.24" - }, - "dependencies": { - "commander": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", - "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", - "dev": true, - "requires": { - "graceful-readlink": "1.0.1" - } + "uglify-js": "3.1.4" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true }, "uglify-js": { - "version": "3.0.24", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.0.24.tgz", - "integrity": "sha512-IZ7l7MU2j7LIuz6IAFWBOk1dbuQ0QVQsKLffpNPKXuL8NYcFBBQ5QkvMAtfL1+oaBW16344DY4sA26GI9cXzlA==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.1.4.tgz", + "integrity": "sha512-DcbkPg11Lw2lAWpwCmQDX+qoR4JiII6ypsQmF6tscZtlxGPFAmSRUGuMoVT3/0EHqypVik/TpkH4ITiMJeQqQA==", "dev": true, "requires": { - "commander": "2.9.0", - "source-map": "0.5.6" + "commander": "2.11.0", + "source-map": "0.6.1" } } } @@ -8302,17 +10682,23 @@ } }, "http-errors": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.1.tgz", - "integrity": "sha1-X4uO2YrKVFZWv1cplzh/kEpyIlc=", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.2.tgz", + "integrity": "sha1-CgAsyFcHGSp+eUbO7cERVfYOxzY=", "dev": true, "requires": { - "depd": "1.1.0", + "depd": "1.1.1", "inherits": "2.0.3", "setprototypeof": "1.0.3", - "statuses": "1.3.1" + "statuses": "1.4.0" } }, + "http-parser-js": { + "version": "0.4.9", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.9.tgz", + "integrity": "sha1-6hoE+2St/wJC6ZdPKX3Uw8rSceE=", + "dev": true + }, "http-proxy": { "version": "1.16.2", "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.16.2.tgz", @@ -8330,13 +10716,13 @@ "dev": true }, "http-signature": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", - "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", "dev": true, "requires": { - "assert-plus": "0.2.0", - "jsprim": "1.4.0", + "assert-plus": "1.0.0", + "jsprim": "1.4.1", "sshpk": "1.13.1" } }, @@ -8352,9 +10738,9 @@ } }, "i": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/i/-/i-0.3.5.tgz", - "integrity": "sha1-HSuFQVjsgWkRPGy39raAHpniEdU=", + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/i/-/i-0.3.6.tgz", + "integrity": "sha1-2WyScyB28HJxG2sQ/X1PZa2O4j0=", "dev": true, "optional": true }, @@ -8365,9 +10751,9 @@ "dev": true }, "ignore": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.3.tgz", - "integrity": "sha1-QyNS5XrM2HqzEQ6C0/6g5HgSFW0=", + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.5.tgz", + "integrity": "sha512-JLH93mL8amZQhh/p6mfQgVBH3M6epNq3DfsXsTSuSrInVjwyYlFE1nv2AgfRCC8PoOhM0jwQ5v8s9LgbK7yGDw==", "dev": true }, "image-diff": { @@ -8413,6 +10799,12 @@ } } }, + "immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=", + "dev": true + }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -8484,7 +10876,7 @@ "ansi-regex": "2.1.1", "chalk": "1.1.3", "cli-cursor": "1.0.2", - "cli-width": "2.1.0", + "cli-width": "2.2.0", "figures": "1.7.0", "lodash": "4.17.4", "readline2": "1.0.1", @@ -8493,12 +10885,33 @@ "string-width": "1.0.2", "strip-ansi": "3.0.1", "through": "2.3.8" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } } }, "interpret": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.0.3.tgz", - "integrity": "sha1-y8NcYu7uc/Gat7EKgBURQBr8D5A=", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.0.4.tgz", + "integrity": "sha1-ggzdWIuGj/sZGoCVBtbJyPISsbA=", "dev": true }, "invert-kv": { @@ -8508,16 +10921,16 @@ "dev": true }, "ipaddr.js": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.3.0.tgz", - "integrity": "sha1-HgOlL9rYOou7KyXL9JmLTP/NPew=", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.4.0.tgz", + "integrity": "sha1-KWrKh4qCGBbluF0KKFqZvP9FgvA=", "dev": true, "optional": true }, "irregular-plurals": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-1.3.0.tgz", - "integrity": "sha512-njf5A+Mxb3kojuHd1DzISjjIl+XhyzovXEOyPPSzdQozq/Lf2tN27mOrAAsxEPZxpn6I4MGzs1oo9TxXxPFpaA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/irregular-plurals/-/irregular-plurals-1.4.0.tgz", + "integrity": "sha1-LKmwM2UREYVUEvFr5dd8YqRYp2Y=", "dev": true }, "is": { @@ -8548,7 +10961,7 @@ "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", "dev": true, "requires": { - "binary-extensions": "1.8.0" + "binary-extensions": "1.10.0" } }, "is-buffer": { @@ -8642,9 +11055,9 @@ "dev": true }, "is-my-json-valid": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz", - "integrity": "sha1-8Hndm/2uZe4gOKrorLyGqxCeNpM=", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.1.tgz", + "integrity": "sha512-ochPsqWS1WXj8ZnMIV0vnNXooaMhp7cyL4FMSIPKTtnV0Ha/T19G2b9kkhcNsabV9bxYkze7/aLZJb/bYuFduQ==", "dev": true, "requires": { "generate-function": "2.0.0", @@ -8713,9 +11126,9 @@ "optional": true }, "is-plain-object": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.3.tgz", - "integrity": "sha1-wVvz5LZrYtcu+vKSWEhmPsvGGbY=", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, "requires": { "isobject": "3.0.1" @@ -8769,9 +11182,9 @@ } }, "is-relative-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-relative-path/-/is-relative-path-1.0.1.tgz", - "integrity": "sha1-rHJ5Oi1gwEnlBnbgSiSo2PJj3CY=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-relative-path/-/is-relative-path-1.0.2.tgz", + "integrity": "sha1-CRtGoNZ8HtD+hfH4z93gBrslHUY=", "dev": true }, "is-retry-allowed": { @@ -8810,7 +11223,7 @@ "integrity": "sha1-Thqg+1G/vLPpJogAE5cgLBd1tm4=", "dev": true, "requires": { - "text-extensions": "1.5.0" + "text-extensions": "1.7.0" } }, "is-typedarray": { @@ -8905,14 +11318,14 @@ "escodegen": "1.8.1", "esprima": "2.7.3", "glob": "5.0.15", - "handlebars": "4.0.10", - "js-yaml": "3.9.0", + "handlebars": "4.0.11", + "js-yaml": "3.10.0", "mkdirp": "0.5.1", "nopt": "3.0.6", "once": "1.4.0", "resolve": "1.1.7", "supports-color": "3.2.3", - "which": "1.2.14", + "which": "1.3.0", "wordwrap": "1.0.0" }, "dependencies": { @@ -8928,12 +11341,31 @@ "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true }, + "escodegen": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", + "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", + "dev": true, + "requires": { + "esprima": "2.7.3", + "estraverse": "1.9.3", + "esutils": "2.0.2", + "optionator": "0.8.2", + "source-map": "0.2.0" + } + }, "esprima": { "version": "2.7.3", "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", "dev": true }, + "estraverse": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", + "integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=", + "dev": true + }, "glob": { "version": "5.0.15", "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", @@ -8953,13 +11385,14 @@ "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", "dev": true }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "source-map": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", + "integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=", "dev": true, + "optional": true, "requires": { - "has-flag": "1.0.0" + "amdefine": "1.0.1" } }, "wordwrap": { @@ -8977,31 +11410,31 @@ "dev": true, "optional": true, "requires": { - "has-to-string-tag-x": "1.4.0", + "has-to-string-tag-x": "1.4.1", "is-object": "1.0.1" } }, "jasmine": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-2.6.0.tgz", - "integrity": "sha1-ayLnCIPo5YnUVjRhU7TSBt2+IX8=", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-2.8.0.tgz", + "integrity": "sha1-awicChFXax8W3xG4AUbZHU6Lij4=", "dev": true, "requires": { "exit": "0.1.2", "glob": "7.1.2", - "jasmine-core": "2.6.4" + "jasmine-core": "2.8.0" } }, "jasmine-core": { - "version": "2.6.4", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.6.4.tgz", - "integrity": "sha1-3skmzQqfoof7bbXHVfpIfnTOysU=", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz", + "integrity": "sha1-vMl5rh+f0FcB5F5S5l06XWPxok4=", "dev": true }, "jasminewd2": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/jasminewd2/-/jasminewd2-2.1.0.tgz", - "integrity": "sha1-2llSddGuYx3nNqwKfH2Fyfc+9lI=", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/jasminewd2/-/jasminewd2-2.2.0.tgz", + "integrity": "sha1-43zwsX8ZnM4jvqcbIDk5Uka07E4=", "dev": true }, "jju": { @@ -9018,8 +11451,16 @@ "requires": { "hoek": "2.16.3", "isemail": "1.2.0", - "moment": "2.18.1", + "moment": "2.19.1", "topo": "1.1.0" + }, + "dependencies": { + "hoek": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", + "dev": true + } } }, "join-path": { @@ -9034,9 +11475,9 @@ } }, "js-base64": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.1.9.tgz", - "integrity": "sha1-8OgK4DmkvWVLXygfyT8EqRSn/M4=", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.3.2.tgz", + "integrity": "sha512-Y2/+DnfJJXT1/FCwUebUhLWb3QihxiSC42+ctHLGogmW2jPY6LCapMdFZXRvVP2z6qyKW7s6qncE/9gSqZiArw==", "dev": true }, "js-tokens": { @@ -9046,9 +11487,9 @@ "dev": true }, "js-yaml": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.9.0.tgz", - "integrity": "sha512-0LoUNELX4S+iofCT8f4uEHIiRBR+c2AINyC8qRWfC6QNruLtxVZRJaPcu/xwMgFIgDxF25tGHaDjvxzJCNE9yw==", + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.10.0.tgz", + "integrity": "sha512-O2v52ffjLa9VeM43J4XocZE//WT9N0IiwDa3KSHH7Tu8CtH+1qM8SIZvnsTh6v+4yFy5KUY3BHUVwjpfAWsjIA==", "dev": true, "requires": { "argparse": "1.0.9", @@ -9076,22 +11517,22 @@ "integrity": "sha1-/eKcEJwyoRMeC2xlkU5kGY+Xw3A=", "dev": true, "requires": { - "abab": "1.0.3", + "abab": "1.0.4", "acorn": "2.7.0", "acorn-globals": "1.0.9", "array-equal": "1.0.0", "content-type-parser": "1.0.1", "cssom": "0.3.2", "cssstyle": "0.2.37", - "escodegen": "1.8.1", + "escodegen": "1.9.0", "html-encoding-sniffer": "1.0.1", "iconv-lite": "0.4.15", - "nwmatcher": "1.4.1", + "nwmatcher": "1.4.3", "parse5": "1.5.1", - "request": "2.81.0", + "request": "2.83.0", "sax": "1.2.4", "symbol-tree": "3.2.2", - "tough-cookie": "2.3.2", + "tough-cookie": "2.3.3", "webidl-conversions": "3.0.1", "whatwg-encoding": "1.0.1", "whatwg-url": "3.1.0", @@ -9125,8 +11566,7 @@ "version": "0.3.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", - "dev": true, - "optional": true + "dev": true }, "json-stable-stringify": { "version": "1.0.1", @@ -9170,6 +11610,16 @@ "through2": "0.6.5" }, "dependencies": { + "JSONStream": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-0.8.4.tgz", + "integrity": "sha1-kWV9/m/4V0gwZhMrRhi2Lo9Ih70=", + "dev": true, + "requires": { + "jsonparse": "0.0.5", + "through": "2.3.8" + } + }, "isarray": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", @@ -9182,16 +11632,6 @@ "integrity": "sha1-MwVCrT8KZUZlt3jz6y2an6UHrGQ=", "dev": true }, - "JSONStream": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-0.8.4.tgz", - "integrity": "sha1-kWV9/m/4V0gwZhMrRhi2Lo9Ih70=", - "dev": true, - "requires": { - "jsonparse": "0.0.5", - "through": "2.3.8" - } - }, "readable-stream": { "version": "1.0.34", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", @@ -9251,25 +11691,15 @@ "dev": true }, "jsonschema": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.1.1.tgz", - "integrity": "sha1-PO3o4+QR03eHLu+8n98mODy8Ptk=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.2.0.tgz", + "integrity": "sha512-XDJApzBauMg0TinJNP4iVcJl99PQ4JbWKK7nwzpOIkAOVveDKgh/2xm41T3x7Spu4PWMhnnQpNJmUSIUgl6sKg==", "dev": true }, - "JSONStream": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz", - "integrity": "sha1-cH92HgHa6eFvG8+TcDt4xwlmV5o=", - "dev": true, - "requires": { - "jsonparse": "1.3.1", - "through": "2.3.8" - } - }, "jsonwebtoken": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-7.4.1.tgz", - "integrity": "sha1-fKMk9SFfi+A5zTWmxFu4y3SkSPs=", + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-7.4.3.tgz", + "integrity": "sha1-d/UCHeBYtgWheD+hKD6ZgS5kVjg=", "dev": true, "requires": { "joi": "6.10.1", @@ -9280,21 +11710,54 @@ } }, "jsprim": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.0.tgz", - "integrity": "sha1-o7h+QCmNjDgFUtjMdiigu5WiKRg=", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", "dev": true, "requires": { "assert-plus": "1.0.0", - "extsprintf": "1.0.2", + "extsprintf": "1.3.0", "json-schema": "0.2.3", - "verror": "1.3.6" + "verror": "1.10.0" + } + }, + "jszip": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.1.4.tgz", + "integrity": "sha512-z6w8iYIxZ/fcgul0j/OerkYnkomH8BZigvzbxVmr2h5HkZUrPtk2kjYtLkqR9wwQxEP6ecKNoKLsbhd18jfnGA==", + "dev": true, + "requires": { + "core-js": "2.3.0", + "es6-promise": "3.0.2", + "lie": "3.1.1", + "pako": "1.0.6", + "readable-stream": "2.0.6" }, "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "core-js": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.3.0.tgz", + "integrity": "sha1-+rg/uwstjchfpjbEudNMdUIMbWU=", + "dev": true + }, + "readable-stream": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "string_decoder": "0.10.31", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", "dev": true } } @@ -9323,18 +11786,18 @@ } }, "karma": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/karma/-/karma-1.7.0.tgz", - "integrity": "sha1-b3oaQGRG+i4YfslTmGmPTO5HYmk=", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/karma/-/karma-1.7.1.tgz", + "integrity": "sha512-k5pBjHDhmkdaUccnC7gE3mBzZjcxyxYsYVaqiL2G5AqlfLyBO5nw2VdNK+O16cveEPd/gIOWULH7gkiYYwVNHg==", "dev": true, "requires": { - "bluebird": "3.5.0", + "bluebird": "3.5.1", "body-parser": "1.17.2", "chokidar": "1.7.0", "colors": "1.1.2", "combine-lists": "1.0.1", - "connect": "3.6.2", - "core-js": "2.4.1", + "connect": "3.6.5", + "core-js": "2.5.1", "di": "0.0.1", "dom-serialize": "2.2.1", "expand-braces": "0.1.2", @@ -9344,7 +11807,7 @@ "isbinaryfile": "3.0.2", "lodash": "3.10.1", "log4js": "0.6.38", - "mime": "1.3.6", + "mime": "1.4.1", "minimatch": "3.0.4", "optimist": "0.6.1", "qjobs": "1.1.5", @@ -9352,9 +11815,9 @@ "rimraf": "2.6.1", "safe-buffer": "5.1.1", "socket.io": "1.7.3", - "source-map": "0.5.6", + "source-map": "0.5.7", "tmp": "0.0.31", - "useragent": "2.2.0" + "useragent": "2.2.1" }, "dependencies": { "colors": { @@ -9388,7 +11851,15 @@ "requires": { "browserstack": "1.5.0", "browserstacktunnel-wrapper": "2.0.1", - "q": "1.5.0" + "q": "1.5.1" + }, + "dependencies": { + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", + "dev": true + } } }, "karma-chrome-launcher": { @@ -9398,7 +11869,7 @@ "dev": true, "requires": { "fs-access": "1.0.1", - "which": "1.2.14" + "which": "1.3.0" } }, "karma-coverage": { @@ -9411,9 +11882,19 @@ "istanbul": "0.4.5", "lodash": "3.10.1", "minimatch": "3.0.4", - "source-map": "0.5.6" + "source-map": "0.5.7" }, "dependencies": { + "dateformat": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.12.tgz", + "integrity": "sha1-nxJLZ1lMk3/3BpMuSmQsyo27/uk=", + "dev": true, + "requires": { + "get-stdin": "4.0.1", + "meow": "3.7.0" + } + }, "lodash": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", @@ -9440,10 +11921,18 @@ "integrity": "sha512-lEhtGRGS+3Yw6JSx/vJY9iQyHNtTjcojrSwNzqNUOaDceKDu9dPZqA/kr69bUO9G2T6GKbu8AZgXqy94qo31Jg==", "dev": true, "requires": { - "q": "1.5.0", + "q": "1.5.1", "sauce-connect-launcher": "1.2.2", "saucelabs": "1.4.0", - "wd": "1.4.0" + "wd": "1.4.1" + }, + "dependencies": { + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=", + "dev": true + } } }, "karma-sourcemap-loader": { @@ -9609,6 +12098,15 @@ "type-check": "0.3.2" } }, + "lie": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz", + "integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=", + "dev": true, + "requires": { + "immediate": "3.0.6" + } + }, "liftoff": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-2.3.0.tgz", @@ -9623,7 +12121,7 @@ "lodash.isstring": "4.0.1", "lodash.mapvalues": "4.6.0", "rechoir": "0.6.2", - "resolve": "1.3.3" + "resolve": "1.4.0" } }, "livereload-js": { @@ -9633,16 +12131,16 @@ "dev": true }, "load-json-file": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", "dev": true, + "optional": true, "requires": { "graceful-fs": "4.1.11", "parse-json": "2.2.0", "pify": "2.3.0", - "pinkie-promise": "2.0.1", - "strip-bom": "2.0.0" + "strip-bom": "3.0.0" } }, "locate-path": { @@ -9650,17 +12148,10 @@ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", "dev": true, + "optional": true, "requires": { "p-locate": "2.0.0", "path-exists": "3.0.0" - }, - "dependencies": { - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - } } }, "lodash": { @@ -9893,22 +12384,58 @@ "dev": true }, "lodash.template": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.4.0.tgz", - "integrity": "sha1-5zoDhcg1VZF0bgILmWecaQ5o+6A=", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz", + "integrity": "sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8=", "dev": true, "requires": { + "lodash._basecopy": "3.0.1", + "lodash._basetostring": "3.0.1", + "lodash._basevalues": "3.0.0", + "lodash._isiterateecall": "3.0.9", "lodash._reinterpolate": "3.0.0", - "lodash.templatesettings": "4.1.0" + "lodash.escape": "3.2.0", + "lodash.keys": "3.1.2", + "lodash.restparam": "3.6.1", + "lodash.templatesettings": "3.1.1" + }, + "dependencies": { + "lodash.keys": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "dev": true, + "requires": { + "lodash._getnative": "3.9.1", + "lodash.isarguments": "3.1.0", + "lodash.isarray": "3.0.4" + } + } } }, "lodash.templatesettings": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.1.0.tgz", - "integrity": "sha1-K01OlbpEDZFf8IvImeRVNmZxMxY=", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz", + "integrity": "sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU=", + "dev": true, + "requires": { + "lodash._reinterpolate": "3.0.0", + "lodash.escape": "3.2.0" + } + }, + "lodash.tostring": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/lodash.tostring/-/lodash.tostring-4.1.4.tgz", + "integrity": "sha1-Vgwn0fjq3eA8LM4Zj+9cAx2CmPs=", + "dev": true + }, + "lodash.unescape": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.0.tgz", + "integrity": "sha1-Nt6/xJK4FHhHHvl0zTeD4gLrbO8=", "dev": true, "requires": { - "lodash._reinterpolate": "3.0.0" + "lodash.tostring": "4.1.4" } }, "lodash.values": { @@ -9933,6 +12460,27 @@ "dev": true, "requires": { "chalk": "1.1.3" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } } }, "log4js": { @@ -9981,8 +12529,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=", - "dev": true, - "optional": true + "dev": true }, "longest": { "version": "1.0.1", @@ -10032,22 +12579,22 @@ } }, "madge": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/madge/-/madge-1.6.0.tgz", - "integrity": "sha1-9dCkgCe+4uuSRbk0I/l0H4iK62U=", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/madge/-/madge-2.2.0.tgz", + "integrity": "sha512-5UoX9n8od0UmKPcZo4KydgQch+oKvlywsee+60ochES6Uo1UDaHnt6yx+SOIZMt007R0F/ZNq03vhxlaJPep+g==", "dev": true, "requires": { "chalk": "1.1.3", - "commander": "2.11.0", + "commander": "2.9.0", "commondir": "1.0.1", - "debug": "2.6.7", - "dependency-tree": "5.8.0", + "debug": "2.2.0", + "dependency-tree": "5.11.0", "graphviz": "0.0.8", - "mz": "2.6.0", - "ora": "1.1.0", - "pluralize": "3.1.0", + "mz": "2.4.0", + "ora": "1.2.0", + "pluralize": "4.0.0", "pretty-ms": "2.1.0", - "rc": "1.2.1", + "rc": "1.1.6", "walkdir": "0.0.11" }, "dependencies": { @@ -10057,15 +12604,50 @@ "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", "dev": true, "requires": { - "restore-cursor": "2.0.0" + "restore-cursor": "2.0.0" + } + }, + "cli-spinners": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-1.1.0.tgz", + "integrity": "sha1-8YR7FohE2RemceudFH499JfJDQY=", + "dev": true + }, + "commander": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", + "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", + "dev": true, + "requires": { + "graceful-readlink": "1.0.1" + } + }, + "debug": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", + "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "dev": true, + "requires": { + "ms": "0.7.1" + } + }, + "ms": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", + "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", + "dev": true + }, + "mz": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.4.0.tgz", + "integrity": "sha1-mHupYk2JOVOIw3y0dB4sr03ROxo=", + "dev": true, + "requires": { + "any-promise": "1.3.0", + "object-assign": "4.1.1", + "thenify-all": "1.6.0" } }, - "cli-spinners": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-1.0.0.tgz", - "integrity": "sha1-75h+09SDkaw9q5GAtAanQhgNbmo=", - "dev": true - }, "onetime": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", @@ -10076,17 +12658,29 @@ } }, "ora": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ora/-/ora-1.1.0.tgz", - "integrity": "sha1-aaqkogljDkOxQsX3/0GCDah+L68=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-1.2.0.tgz", + "integrity": "sha1-Mvsxg1AO/oP16okQF4Xw7mBg/sk=", "dev": true, "requires": { "chalk": "1.1.3", "cli-cursor": "2.1.0", - "cli-spinners": "1.0.0", + "cli-spinners": "1.1.0", "log-symbols": "1.0.2" } }, + "rc": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.1.6.tgz", + "integrity": "sha1-Q2UbdrauU7XIAvEVH6P8OwWZack=", + "dev": true, + "requires": { + "deep-extend": "0.4.2", + "ini": "1.3.4", + "minimist": "1.2.0", + "strip-json-comments": "1.0.4" + } + }, "restore-cursor": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", @@ -10096,16 +12690,22 @@ "onetime": "2.0.1", "signal-exit": "3.0.2" } + }, + "strip-json-comments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz", + "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=", + "dev": true } } }, "magic-string": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.21.3.tgz", - "integrity": "sha1-h+IBAJ6/3m9G3FdXMFpwr3HjFiQ=", + "version": "0.22.4", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.22.4.tgz", + "integrity": "sha512-kxBL06p6iO2qPBHsqGK2b3cRwiRGpnmSuVWNhwHcMX7qJOUr1HvricYP1LZOCdkQBUp0jiWg2d6WJwR3vYgByw==", "dev": true, "requires": { - "vlq": "0.2.2" + "vlq": "0.2.3" } }, "make-dir": { @@ -10281,11 +12881,78 @@ "trim-newlines": "1.0.0" }, "dependencies": { - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "parse-json": "2.2.0", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "strip-bom": "2.0.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "2.0.1" + } + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "1.1.0", + "normalize-package-data": "2.4.0", + "path-type": "1.1.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "1.1.2", + "read-pkg": "1.1.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "0.2.1" + } } } }, @@ -10312,21 +12979,21 @@ "dev": true }, "method-override": { - "version": "2.3.9", - "resolved": "https://registry.npmjs.org/method-override/-/method-override-2.3.9.tgz", - "integrity": "sha1-vRUfLONM8Bp2ykAKuVwBKxAtj3E=", + "version": "2.3.10", + "resolved": "https://registry.npmjs.org/method-override/-/method-override-2.3.10.tgz", + "integrity": "sha1-49r41d7hDdLc59SuiNYrvud0drQ=", "dev": true, "requires": { - "debug": "2.6.8", + "debug": "2.6.9", "methods": "1.1.2", - "parseurl": "1.3.1", - "vary": "1.1.1" + "parseurl": "1.3.2", + "vary": "1.1.2" }, "dependencies": { "debug": { - "version": "2.6.8", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", - "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { "ms": "2.0.0" @@ -10358,28 +13025,28 @@ "normalize-path": "2.1.1", "object.omit": "2.0.1", "parse-glob": "3.0.4", - "regex-cache": "0.4.3" + "regex-cache": "0.4.4" } }, "mime": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.6.tgz", - "integrity": "sha1-WR2E02U6awtKO5343lqoEI5y5eA=", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", "dev": true }, "mime-db": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.27.0.tgz", - "integrity": "sha1-gg9XIpa70g7CXtVeW13oaeVDbrE=", + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", + "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=", "dev": true }, "mime-types": { - "version": "2.1.15", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.15.tgz", - "integrity": "sha1-pOv1BkCUVpI3uM9wBGd20J/JKu0=", + "version": "2.1.17", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", + "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", "dev": true, "requires": { - "mime-db": "1.27.0" + "mime-db": "1.30.0" } }, "mimic-fn": { @@ -10409,6 +13076,32 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" }, + "minipass": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.2.1.tgz", + "integrity": "sha512-u1aUllxPJUI07cOqzR7reGmQxmCqlH88uIIsf6XZFEWgw7gXKpJdR+5R9Y3KEDmWYkdIz9wXZs3C0jOPxejk/Q==", + "dev": true, + "requires": { + "yallist": "3.0.2" + }, + "dependencies": { + "yallist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz", + "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=", + "dev": true + } + } + }, + "minizlib": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.0.4.tgz", + "integrity": "sha512-sN4U9tIJtBRwKbwgFh9qJfrPIQ/GGTRr1MGqkgOeMTLy8/lM0FcWU//FqlnZ3Vb7gJ+Mxh3FOg1EklibdajbaQ==", + "dev": true, + "requires": { + "minipass": "2.2.1" + } + }, "mkdirp": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", @@ -10452,17 +13145,17 @@ "dev": true, "requires": { "ast-module-types": "2.3.2", - "node-source-walk": "3.2.1" + "node-source-walk": "3.3.0" } }, "module-lookup-amd": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/module-lookup-amd/-/module-lookup-amd-4.0.4.tgz", - "integrity": "sha1-aSldvumGFjLGULY25qSQzHigp5k=", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/module-lookup-amd/-/module-lookup-amd-4.0.5.tgz", + "integrity": "sha1-WONT+dwB7OwFexzN0A7QWUhKzKU=", "dev": true, "requires": { "commander": "2.11.0", - "debug": "2.2.0", + "debug": "3.1.0", "file-exists": "1.0.0", "find": "0.2.6", "requirejs": "2.2.0", @@ -10470,45 +13163,39 @@ }, "dependencies": { "debug": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", - "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { - "ms": "0.7.1" + "ms": "2.0.0" } - }, - "ms": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", - "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", - "dev": true } } }, "moment": { - "version": "2.18.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.18.1.tgz", - "integrity": "sha1-w2GT3Tzhwu7SrbfIAtu8d6gbHA8=", + "version": "2.19.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.19.1.tgz", + "integrity": "sha1-VtoaLRy/AdOLfhr8McELz6GSkWc=", "dev": true }, "morgan": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.8.2.tgz", - "integrity": "sha1-eErHc05KRTqcbm6GgKkyknXItoc=", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.0.tgz", + "integrity": "sha1-0B+mxlhZt2/PMbPLU6OCGjEdgFE=", "dev": true, "requires": { - "basic-auth": "1.1.0", - "debug": "2.6.8", - "depd": "1.1.0", + "basic-auth": "2.0.0", + "debug": "2.6.9", + "depd": "1.1.1", "on-finished": "2.3.0", "on-headers": "1.0.1" }, "dependencies": { "debug": { - "version": "2.6.8", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", - "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { "ms": "2.0.0" @@ -10628,28 +13315,20 @@ "optional": true }, "mz": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.6.0.tgz", - "integrity": "sha1-yLhSHZWN8KTydoAl22nHGe5O8c4=", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", "dev": true, "requires": { "any-promise": "1.3.0", "object-assign": "4.1.1", "thenify-all": "1.6.0" - }, - "dependencies": { - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - } } }, "nan": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.6.2.tgz", - "integrity": "sha1-5P805slf37WuzAjeZZb0NgWn20U=", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.7.0.tgz", + "integrity": "sha1-2Vv3IeyHfgjbJ27T/G63j5CDrUY=", "dev": true }, "nash": { @@ -10716,9 +13395,9 @@ } }, "no-case": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.1.tgz", - "integrity": "sha1-euuhxzpSGEJlVUt9wDuvcg34AIE=", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", + "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", "dev": true, "requires": { "lower-case": "1.1.4" @@ -10744,11 +13423,30 @@ "nopt": "3.0.6", "npmlog": "4.1.2", "osenv": "0.1.4", - "request": "2.81.0", + "request": "2.83.0", "rimraf": "2.6.1", "semver": "5.3.0", "tar": "2.2.1", - "which": "1.2.14" + "which": "1.3.0" + }, + "dependencies": { + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", + "dev": true + }, + "tar": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", + "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", + "dev": true, + "requires": { + "block-stream": "0.0.9", + "fstream": "1.0.11", + "inherits": "2.0.3" + } + } } }, "node-html-encoder": { @@ -10781,14 +13479,27 @@ "lodash.mergewith": "4.6.0", "meow": "3.7.0", "mkdirp": "0.5.1", - "nan": "2.6.2", + "nan": "2.7.0", "node-gyp": "3.6.2", "npmlog": "4.1.2", - "request": "2.81.0", + "request": "2.83.0", "sass-graph": "2.2.4", "stdout-stream": "1.4.0" }, "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, "cross-spawn": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", @@ -10796,7 +13507,7 @@ "dev": true, "requires": { "lru-cache": "4.1.1", - "which": "1.2.14" + "which": "1.3.0" } }, "gaze": { @@ -10818,16 +13529,22 @@ "lodash": "4.17.4", "minimatch": "3.0.4" } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true } } }, "node-source-walk": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/node-source-walk/-/node-source-walk-3.2.1.tgz", - "integrity": "sha1-Ersbmj+xhz94XEp1R7YDT5U/Zp8=", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/node-source-walk/-/node-source-walk-3.3.0.tgz", + "integrity": "sha1-rRjjW/2z0Lb34OSv8eePhGo7iHM=", "dev": true, "requires": { - "babylon": "6.17.4" + "babylon": "6.18.0" } }, "node-status-codes": { @@ -10842,7 +13559,7 @@ "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", "dev": true, "requires": { - "abbrev": "1.1.0" + "abbrev": "1.1.1" } }, "normalize-package-data": { @@ -10853,7 +13570,7 @@ "requires": { "hosted-git-info": "2.5.0", "is-builtin-module": "1.0.0", - "semver": "5.3.0", + "semver": "5.4.1", "validate-npm-package-license": "3.0.1" } }, @@ -10863,7 +13580,7 @@ "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", "dev": true, "requires": { - "remove-trailing-separator": "1.0.2" + "remove-trailing-separator": "1.1.0" } }, "normalize-range": { @@ -10919,54 +13636,21 @@ "dev": true }, "nunjucks": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-2.5.2.tgz", - "integrity": "sha1-6n00bnhbikh0Zmw8yp4YxXf7oiw=", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.0.1.tgz", + "integrity": "sha1-TedKPlULr2+jNwMj89HHwqhr3E0=", "dev": true, "requires": { + "a-sync-waterfall": "1.0.0", "asap": "2.0.6", "chokidar": "1.7.0", "yargs": "3.32.0" - }, - "dependencies": { - "cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", - "dev": true, - "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wrap-ansi": "2.1.0" - } - }, - "window-size": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", - "integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY=", - "dev": true - }, - "yargs": { - "version": "3.32.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", - "integrity": "sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=", - "dev": true, - "requires": { - "camelcase": "2.1.1", - "cliui": "3.2.0", - "decamelize": "1.2.0", - "os-locale": "1.4.0", - "string-width": "1.0.2", - "window-size": "0.1.4", - "y18n": "3.2.1" - } - } } }, "nwmatcher": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/nwmatcher/-/nwmatcher-1.4.1.tgz", - "integrity": "sha1-eumwew6oBNt+JfBctf5Al9TklJ8=", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/nwmatcher/-/nwmatcher-1.4.3.tgz", + "integrity": "sha512-IKdSTiDWCarf2JTS5e9e2+5tPZGdkRJ79XjYV0pzK8Q9BpsFyBq1RGKxzs7Q8UBushGw7m6TzVKz6fcY99iSWw==", "dev": true }, "oauth-sign": { @@ -10976,9 +13660,9 @@ "dev": true }, "object-assign": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", - "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true }, "object-component": { @@ -11033,12 +13717,20 @@ } }, "object.pick": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.2.0.tgz", - "integrity": "sha1-tTkr7peC2m2ft9avr1OXefEjTCs=", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", "dev": true, "requires": { - "isobject": "2.1.0" + "isobject": "3.0.1" + }, + "dependencies": { + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + } } }, "objectdiff": { @@ -11139,8 +13831,7 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/optjs/-/optjs-3.2.2.tgz", "integrity": "sha1-aabOicRCpEQDFBrS+bNwvVu29O4=", - "dev": true, - "optional": true + "dev": true }, "ora": { "version": "0.2.3", @@ -11154,10 +13845,23 @@ "object-assign": "4.1.1" }, "dependencies": { - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", "dev": true } } @@ -11237,9 +13941,9 @@ "dev": true }, "p-cancelable": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.2.0.tgz", - "integrity": "sha1-MVL08wvnYGtg6/6LuTs/32kIXkY=", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz", + "integrity": "sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw==", "dev": true, "optional": true }, @@ -11253,13 +13957,15 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.1.0.tgz", "integrity": "sha1-sH/y2aXYi+yAYDWJWiurZqJ5iLw=", - "dev": true + "dev": true, + "optional": true }, "p-locate": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", "dev": true, + "optional": true, "requires": { "p-limit": "1.1.0" } @@ -11283,7 +13989,7 @@ "got": "5.7.1", "registry-auth-token": "3.3.1", "registry-url": "3.1.0", - "semver": "5.3.0" + "semver": "5.4.1" }, "dependencies": { "got": { @@ -11309,12 +14015,6 @@ "url-parse-lax": "1.0.0" } }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, "timed-out": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-3.1.3.tgz", @@ -11323,13 +14023,19 @@ } } }, + "pako": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", + "integrity": "sha512-lQe48YPsMJAig+yngZ87Lus+NF+3mtu7DVOBu6b/gHO1YpKwIj5AWjZ/TOS7i46HD/UixzWb1zeWDZfGZ3iYcg==", + "dev": true + }, "param-case": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", "dev": true, "requires": { - "no-case": "2.3.1" + "no-case": "2.3.2" } }, "parse-filepath": { @@ -11344,9 +14050,9 @@ } }, "parse-github-repo-url": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/parse-github-repo-url/-/parse-github-repo-url-1.4.0.tgz", - "integrity": "sha1-KGxT4smWLgZBZJ7jrJUI/KTdlZw=", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/parse-github-repo-url/-/parse-github-repo-url-1.4.1.tgz", + "integrity": "sha1-nn2LslKmy2ukJZUGC3v23z28H1A=", "dev": true }, "parse-glob": { @@ -11382,23 +14088,6 @@ "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", "dev": true }, - "parse5": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.2.tgz", - "integrity": "sha1-Be/1fw70V3+xRKefi5qWemzERRA=", - "dev": true, - "requires": { - "@types/node": "6.0.88" - }, - "dependencies": { - "@types/node": { - "version": "6.0.88", - "resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.88.tgz", - "integrity": "sha512-bYDPZTX0/s1aihdjLuAgogUAT5M+TpoWChEMea2p0yOcfn5bu3k6cJb9cp6nw268XeSNIGGr+4+/8V5K6BGzLQ==", - "dev": true - } - } - }, "parsejson": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/parsejson/-/parsejson-0.0.3.tgz", @@ -11427,9 +14116,9 @@ } }, "parseurl": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz", - "integrity": "sha1-yKuMkiO6NIiKpkopeyiFO+wY2lY=", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", + "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=", "dev": true }, "pascal-case": { @@ -11448,18 +14137,16 @@ "integrity": "sha1-lLgDfDctP+KQbkZbtF4l0ibo7qU=", "dev": true, "requires": { - "no-case": "2.3.1" - } - }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "dev": true, - "requires": { - "pinkie-promise": "2.0.1" + "no-case": "2.3.2" } }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true, + "optional": true + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -11507,14 +14194,13 @@ "dev": true }, "path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", "dev": true, + "optional": true, "requires": { - "graceful-fs": "4.1.11", - "pify": "2.3.0", - "pinkie-promise": "2.0.1" + "pify": "2.3.0" } }, "pause": { @@ -11533,9 +14219,9 @@ } }, "performance-now": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", - "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", "dev": true }, "pify": { @@ -11570,9 +14256,9 @@ } }, "pkginfo": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.4.0.tgz", - "integrity": "sha1-NJ27f/04CB/K3AhT32h/DHdEzWU=", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.4.1.tgz", + "integrity": "sha1-tUGO8EOd5UJfxJlQQtztFPsqhP8=", "dev": true, "optional": true }, @@ -11583,9 +14269,9 @@ "dev": true }, "pluralize": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-3.1.0.tgz", - "integrity": "sha1-hCE9ChI1YGnaqEBgxVkkJjMWE2g=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-4.0.0.tgz", + "integrity": "sha1-WbcIwcAZCi9pLxx2GMRGsFL9F2I=", "dev": true }, "portfinder": { @@ -11607,24 +14293,36 @@ } }, "postcss": { - "version": "5.2.17", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.17.tgz", - "integrity": "sha1-z09Ze4ZNZcikkrLqvp1wbIecOIs=", + "version": "5.2.18", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", + "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", "dev": true, "requires": { "chalk": "1.1.3", - "js-base64": "2.1.9", - "source-map": "0.5.6", + "js-base64": "2.3.2", + "source-map": "0.5.7", "supports-color": "3.2.3" }, "dependencies": { - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "has-flag": "1.0.0" + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + }, + "dependencies": { + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } } } } @@ -11635,7 +14333,7 @@ "integrity": "sha1-xjGwicbM5CK5oQ86lY0r7dOBkyQ=", "dev": true, "requires": { - "postcss": "5.2.17" + "postcss": "5.2.18" } }, "postcss-media-query-parser": { @@ -11653,7 +14351,7 @@ "chalk": "1.1.3", "lodash": "4.17.4", "log-symbols": "1.0.2", - "postcss": "5.2.17" + "postcss": "5.2.18" } }, "postcss-resolve-nested-selector": { @@ -11668,7 +14366,7 @@ "integrity": "sha1-rXcbgfD3L19IRdCKpg+TVXZT1Uw=", "dev": true, "requires": { - "postcss": "5.2.17" + "postcss": "5.2.18" } }, "postcss-selector-parser": { @@ -11689,22 +14387,34 @@ "dev": true }, "precinct": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/precinct/-/precinct-3.6.0.tgz", - "integrity": "sha1-fY+Ffmc3UzMy9CnndgwOKKsQY/Y=", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/precinct/-/precinct-3.8.0.tgz", + "integrity": "sha1-JZqUkKhUd6HyaYn73y+ybETyR1o=", "dev": true, "requires": { "commander": "2.11.0", - "debug": "2.6.7", + "debug": "3.1.0", "detective-amd": "2.4.0", "detective-cjs": "2.0.0", - "detective-es6": "1.1.6", + "detective-es6": "1.2.0", "detective-less": "1.0.0", - "detective-sass": "2.0.0", - "detective-scss": "1.0.0", + "detective-sass": "2.0.1", + "detective-scss": "1.0.1", "detective-stylus": "1.0.0", + "detective-typescript": "1.0.1", "module-definition": "2.2.4", - "node-source-walk": "3.2.1" + "node-source-walk": "3.3.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } } }, "prelude-ls": { @@ -11755,9 +14465,9 @@ "dev": true }, "progress": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", - "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.0.tgz", + "integrity": "sha1-ihvjZr+Pwj2yvSPxDG/pILQ4nR8=", "dev": true }, "promise": { @@ -11769,6 +14479,12 @@ "asap": "2.0.6" } }, + "promise-polyfill": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/promise-polyfill/-/promise-polyfill-6.0.2.tgz", + "integrity": "sha1-2chtPcTcLfkBboiUbe/Wm0m0EWI=", + "dev": true + }, "prompt": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/prompt/-/prompt-1.0.0.tgz", @@ -11777,7 +14493,7 @@ "optional": true, "requires": { "colors": "1.1.2", - "pkginfo": "0.4.0", + "pkginfo": "0.4.1", "read": "1.0.7", "revalidator": "0.1.8", "utile": "0.3.0", @@ -11842,7 +14558,7 @@ "ascli": "1.0.1", "bytebuffer": "5.0.1", "glob": "7.1.2", - "yargs": "3.10.0" + "yargs": "3.32.0" } }, "protochain": { @@ -11853,32 +14569,32 @@ "optional": true }, "protractor": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/protractor/-/protractor-5.1.2.tgz", - "integrity": "sha1-myIXQXCaTGLVzVPGqt1UpxE36V8=", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/protractor/-/protractor-5.2.0.tgz", + "integrity": "sha1-0/ObGV6F81Oa2djLZWCp0rYyl8Q=", "dev": true, "requires": { - "@types/node": "6.0.80", + "@types/node": "6.0.90", "@types/q": "0.0.32", "@types/selenium-webdriver": "2.53.42", "blocking-proxy": "0.0.5", "chalk": "1.1.3", "glob": "7.1.2", - "jasmine": "2.6.0", - "jasminewd2": "2.1.0", + "jasmine": "2.8.0", + "jasminewd2": "2.2.0", "optimist": "0.6.1", "q": "1.4.1", "saucelabs": "1.3.0", - "selenium-webdriver": "3.0.1", - "source-map-support": "0.4.15", + "selenium-webdriver": "3.6.0", + "source-map-support": "0.4.18", "webdriver-js-extender": "1.0.0", "webdriver-manager": "12.0.6" }, "dependencies": { "@types/node": { - "version": "6.0.80", - "resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.80.tgz", - "integrity": "sha512-FJedmtuVj9Jb2AbI3cKYlAczj+3Lv3I8g2wjricLSRBCW0Oj7kzG4D6gUmgDc2Ptm0A1lat2AuheqK5kdYfswg==", + "version": "6.0.90", + "resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.90.tgz", + "integrity": "sha512-tXoGRVdi7wZX7P1VWoV9Wfk0uYDOAHdEYXAttuWgSrN76Q32wQlSrMX0Rgyv3RTEaQY2ZLQrzYHVM2e8rfo8sA==", "dev": true }, "@types/q": { @@ -11887,11 +14603,18 @@ "integrity": "sha1-vShOV8hPEyXacCur/IKlMoGQwMU=", "dev": true }, - "q": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", - "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=", - "dev": true + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } }, "saucelabs": { "version": "1.3.0", @@ -11902,26 +14625,11 @@ "https-proxy-agent": "1.0.0" } }, - "selenium-webdriver": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.0.1.tgz", - "integrity": "sha1-ot6l2kqX9mcuiefKcnbO+jZRR6c=", - "dev": true, - "requires": { - "adm-zip": "0.4.7", - "rimraf": "2.6.1", - "tmp": "0.0.30", - "xml2js": "0.4.17" - } - }, - "tmp": { - "version": "0.0.30", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz", - "integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=", - "dev": true, - "requires": { - "os-tmpdir": "1.0.2" - } + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true }, "webdriver-manager": { "version": "12.0.6", @@ -11936,23 +14644,23 @@ "ini": "1.3.4", "minimist": "1.2.0", "q": "1.4.1", - "request": "2.81.0", + "request": "2.83.0", "rimraf": "2.6.1", - "semver": "5.3.0", - "xml2js": "0.4.17" + "semver": "5.4.1", + "xml2js": "0.4.19" } } } }, "proxy-addr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.1.4.tgz", - "integrity": "sha1-J+VF9pYKRKYn2bREZ+NcG2tM4vM=", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.1.5.tgz", + "integrity": "sha1-ccDuOxAt4/IC87ZPYI0XP8uhqRg=", "dev": true, "optional": true, "requires": { - "forwarded": "0.1.0", - "ipaddr.js": "1.3.0" + "forwarded": "0.1.2", + "ipaddr.js": "1.4.0" } }, "prr": { @@ -12013,17 +14721,6 @@ "requires": { "end-of-stream": "1.4.0", "once": "1.4.0" - }, - "dependencies": { - "end-of-stream": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.0.tgz", - "integrity": "sha1-epDYM+/abPpurA9JSduw+tOmMgY=", - "dev": true, - "requires": { - "once": "1.4.0" - } - } } }, "pumpify": { @@ -12032,7 +14729,7 @@ "integrity": "sha1-G2ccYZlAq8rqwK0OOjwWS+dgmTs=", "dev": true, "requires": { - "duplexify": "3.5.0", + "duplexify": "3.5.1", "inherits": "2.0.3", "pump": "1.0.2" } @@ -12044,9 +14741,9 @@ "dev": true }, "q": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.0.tgz", - "integrity": "sha1-3QG6ydBtMObyGa7LglPunr3DCPE=", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", + "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=", "dev": true }, "qjobs": { @@ -12056,9 +14753,9 @@ "dev": true }, "qs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", - "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", + "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==", "dev": true }, "random-bytes": { @@ -12126,9 +14823,9 @@ } }, "rc": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.1.tgz", - "integrity": "sha1-LgPo5C7kULjLPc5lvhv4l04d/ZU=", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.2.tgz", + "integrity": "sha1-2M6ctX6NZNnHut2YdsfDTL48cHc=", "dev": true, "requires": { "deep-extend": "0.4.2", @@ -12167,24 +14864,26 @@ } }, "read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", "dev": true, + "optional": true, "requires": { - "load-json-file": "1.1.0", + "load-json-file": "2.0.0", "normalize-package-data": "2.4.0", - "path-type": "1.1.0" + "path-type": "2.0.0" } }, "read-pkg-up": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", "dev": true, + "optional": true, "requires": { - "find-up": "1.1.2", - "read-pkg": "1.1.0" + "find-up": "2.1.0", + "read-pkg": "2.0.0" } }, "readable-stream": { @@ -12239,7 +14938,7 @@ "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", "dev": true, "requires": { - "resolve": "1.3.3" + "resolve": "1.4.0" } }, "redent": { @@ -12258,14 +14957,19 @@ "integrity": "sha1-tPg3BEFqytiZiMmxVjXUfgO5NEo=", "dev": true }, + "regenerator-runtime": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz", + "integrity": "sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A==", + "dev": true + }, "regex-cache": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.3.tgz", - "integrity": "sha1-mxpsNdTQ3871cRrmUejp09cRQUU=", + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", + "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", "dev": true, "requires": { - "is-equal-shallow": "0.1.3", - "is-primitive": "2.0.0" + "is-equal-shallow": "0.1.3" } }, "registry-auth-token": { @@ -12274,7 +14978,7 @@ "integrity": "sha1-+w0yie4Nmtosu1KvXf5mywcNMAY=", "dev": true, "requires": { - "rc": "1.2.1", + "rc": "1.2.2", "safe-buffer": "5.1.1" } }, @@ -12284,7 +14988,7 @@ "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", "dev": true, "requires": { - "rc": "1.2.1" + "rc": "1.2.2" } }, "relateurl": { @@ -12294,9 +14998,9 @@ "dev": true }, "remove-trailing-separator": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.0.2.tgz", - "integrity": "sha1-abBi2XhyetFNxrVrpKt3L9jXBRE=", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", "dev": true }, "repeat-element": { @@ -12321,39 +15025,50 @@ } }, "replace-ext": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", - "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", "dev": true }, "request": { - "version": "2.81.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", - "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", + "version": "2.83.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz", + "integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==", "dev": true, "requires": { - "aws-sign2": "0.6.0", + "aws-sign2": "0.7.0", "aws4": "1.6.0", "caseless": "0.12.0", "combined-stream": "1.0.5", "extend": "3.0.1", "forever-agent": "0.6.1", - "form-data": "2.1.4", - "har-validator": "4.2.1", - "hawk": "3.1.3", - "http-signature": "1.1.1", + "form-data": "2.3.1", + "har-validator": "5.0.3", + "hawk": "6.0.2", + "http-signature": "1.2.0", "is-typedarray": "1.0.0", "isstream": "0.1.2", "json-stringify-safe": "5.0.1", - "mime-types": "2.1.15", + "mime-types": "2.1.17", "oauth-sign": "0.8.2", - "performance-now": "0.2.0", - "qs": "6.4.0", + "performance-now": "2.1.0", + "qs": "6.5.1", "safe-buffer": "5.1.1", "stringstream": "0.0.5", - "tough-cookie": "2.3.2", + "tough-cookie": "2.3.3", "tunnel-agent": "0.6.0", "uuid": "3.1.0" + }, + "dependencies": { + "tough-cookie": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", + "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", + "dev": true, + "requires": { + "punycode": "1.4.1" + } + } } }, "require-directory": { @@ -12442,9 +15157,9 @@ "dev": true }, "resolve": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.3.3.tgz", - "integrity": "sha1-ZVkHw0aahoDcLeOidaj91paR8OU=", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.4.0.tgz", + "integrity": "sha512-aW7sVKPufyHqOmyyLzg/J+8606v5nevBgaliIlV7nUpVMsDnoBGV/cbSLNjZAg9q0Cfd/+easKVKQ8vOu8fn1Q==", "dev": true, "requires": { "path-parse": "1.0.5" @@ -12487,7 +15202,7 @@ "integrity": "sha1-/6cbq5UtYvfB1Jt0NDVfvGjf/Fo=", "dev": true, "requires": { - "depd": "1.1.0", + "depd": "1.1.1", "on-headers": "1.0.1" } }, @@ -12502,12 +15217,12 @@ } }, "retry-request": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-2.0.5.tgz", - "integrity": "sha1-0ImhShXbntYGhbhgK0D03MDT+zw=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/retry-request/-/retry-request-3.1.0.tgz", + "integrity": "sha512-jOwZQlWR/boHhbAfzfOoUn28EDDotW2A7YxV2o5mfBb07H0k/zZAgbxRcckW08GKl/aT0JtPk1NViuk2BfHqVg==", "dev": true, "requires": { - "request": "2.81.0", + "request": "2.83.0", "through2": "2.0.3" } }, @@ -12548,7 +15263,16 @@ "integrity": "sha1-4NBUl4d6OYwQTYFtJzOnGKepTio=", "dev": true, "requires": { - "source-map-support": "0.4.15" + "source-map-support": "0.4.18" + } + }, + "rollup-plugin-alias": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-alias/-/rollup-plugin-alias-1.4.0.tgz", + "integrity": "sha512-lB094zdi19FS+1bVarVp9kBN0Zk41PdTGoCk0z8xesKO7RGjOo18cp1hUzEqrOQ4bM9+KLD9nbnu/XUxQm9pbg==", + "dev": true, + "requires": { + "slash": "1.0.0" } }, "rollup-plugin-node-resolve": { @@ -12560,22 +15284,22 @@ "browser-resolve": "1.11.2", "builtin-modules": "1.1.1", "is-module": "1.0.0", - "resolve": "1.3.3" + "resolve": "1.4.0" } }, "router": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/router/-/router-1.3.1.tgz", - "integrity": "sha1-5Z72T6/CIZShlphoNNiHBY12r0c=", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/router/-/router-1.3.2.tgz", + "integrity": "sha1-v6oWiIpSg9XuQNmZ2nqfoVKWpgw=", "dev": true, "requires": { "array-flatten": "2.1.1", - "debug": "2.6.8", + "debug": "2.6.9", "methods": "1.1.2", - "parseurl": "1.3.1", + "parseurl": "1.3.2", "path-to-regexp": "0.1.7", - "setprototypeof": "1.0.3", - "utils-merge": "1.0.0" + "setprototypeof": "1.1.0", + "utils-merge": "1.0.1" }, "dependencies": { "array-flatten": { @@ -12585,20 +15309,32 @@ "dev": true }, "debug": { - "version": "2.6.8", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", - "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "dev": true, "requires": { "ms": "2.0.0" } + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", + "dev": true + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=", + "dev": true } } }, "rsvp": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-3.6.1.tgz", - "integrity": "sha1-NPSnrChZ97rMj0l4nFYE8eJq5wI=", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-3.6.2.tgz", + "integrity": "sha512-OfWGQTb9vnwRjwtA2QwpG2ICclHC3pgXZO5xt8H2EfgDquO0qVdSb5T88L4qJVAEugbS56pAuV4XZM58UX8ulw==", "dev": true }, "run-async": { @@ -12627,9 +15363,9 @@ "dev": true }, "rxjs": { - "version": "5.4.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.4.2.tgz", - "integrity": "sha1-KjI2/L8D31e64G/Wly/ZnlwI/Pc=", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.0.tgz", + "integrity": "sha512-vmvP5y/oJIJmXKHY36PIjVeI/46Sny6BMBa7/ou2zsNz1PiqU/Gtcz1GujnHz5Qlxncv+J9VlWmttnshqFj3Kg==", "requires": { "symbol-observable": "1.0.4" } @@ -12650,6 +15386,14 @@ "graceful-fs": "4.1.11", "mkdirp": "0.5.1", "rimraf": "2.6.1" + }, + "dependencies": { + "es6-promise": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", + "integrity": "sha1-oIzd6EzNvzTQJ6FFG8kdS80ophM=", + "dev": true + } } }, "sass-graph": { @@ -12670,15 +15414,77 @@ "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", "dev": true }, - "cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wrap-ansi": "2.1.0" + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "parse-json": "2.2.0", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "strip-bom": "2.0.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "2.0.1" + } + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "1.1.0", + "normalize-package-data": "2.4.0", + "path-type": "1.1.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "1.1.2", + "read-pkg": "1.1.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "0.2.1" } }, "which-module": { @@ -12720,13 +15526,13 @@ } }, "sass-lookup": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/sass-lookup/-/sass-lookup-1.0.2.tgz", - "integrity": "sha1-wVxPcmHcKtsRs9WRLe1rCKFLIcg=", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/sass-lookup/-/sass-lookup-1.1.0.tgz", + "integrity": "sha1-2kSiG+6llV8U7/24G97idRttFeI=", "dev": true, "requires": { "commander": "2.8.1", - "is-relative-path": "1.0.1" + "is-relative-path": "1.0.2" }, "dependencies": { "commander": { @@ -12769,15 +15575,15 @@ "dev": true }, "scss-bundle": { - "version": "2.0.1-beta.7", - "resolved": "https://registry.npmjs.org/scss-bundle/-/scss-bundle-2.0.1-beta.7.tgz", - "integrity": "sha1-PquvktNm4kUFDWLbWDkKHbwl6tQ=", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/scss-bundle/-/scss-bundle-2.1.0.tgz", + "integrity": "sha512-8TJaz8f5FMyyH2Se0mxfQY6CKaFb8gxyIE0qPHxk4Dfx7x8IhnvIIXyZwh0O5XK+uD0L4/gQkkRVT5zW+GcJcw==", "dev": true, "requires": { "archy": "1.0.0", "globs": "0.1.3", "mkdirp": "0.5.1", - "mz": "2.6.0", + "mz": "2.7.0", "node-sass": "4.5.3", "pretty-bytes": "4.0.2", "promise": "7.3.1", @@ -12790,15 +15596,77 @@ "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=", "dev": true }, - "cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wrap-ansi": "2.1.0" + "path-exists": "2.1.0", + "pinkie-promise": "2.0.1" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "parse-json": "2.2.0", + "pify": "2.3.0", + "pinkie-promise": "2.0.1", + "strip-bom": "2.0.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "2.0.1" + } + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "4.1.11", + "pify": "2.3.0", + "pinkie-promise": "2.0.1" + } + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "1.1.0", + "normalize-package-data": "2.4.0", + "path-type": "1.1.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "1.1.2", + "read-pkg": "1.1.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "0.2.1" } }, "which-module": { @@ -12845,7 +15713,7 @@ "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=", "dev": true, "requires": { - "js-base64": "2.1.9", + "js-base64": "2.3.2", "source-map": "0.4.4" }, "dependencies": { @@ -12861,15 +15729,15 @@ } }, "selenium-webdriver": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.4.0.tgz", - "integrity": "sha1-FR90RSlNpqZsScwwB0eioX5TxSo=", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz", + "integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==", "dev": true, "requires": { - "adm-zip": "0.4.7", + "jszip": "3.1.4", "rimraf": "2.6.1", "tmp": "0.0.30", - "xml2js": "0.4.17" + "xml2js": "0.4.19" }, "dependencies": { "tmp": { @@ -12884,9 +15752,9 @@ } }, "semver": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", - "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==", "dev": true }, "semver-diff": { @@ -12895,47 +15763,32 @@ "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", "dev": true, "requires": { - "semver": "5.3.0" + "semver": "5.4.1" } }, "semver-greatest-satisfied-range": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.0.0.tgz", - "integrity": "sha1-T7RB4qjSbEC1mDJ1VzGN4nKlWKA=", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz", + "integrity": "sha1-E+jCZYq5aRywzXEJMkAoDTb3els=", "dev": true, "requires": { - "semver": "4.3.6", - "semver-regex": "1.0.0" - }, - "dependencies": { - "semver": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz", - "integrity": "sha1-MAvG4OhjdPe6YQaLWx7NV/xlMto=", - "dev": true - } + "sver-compat": "1.5.0" } }, - "semver-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-1.0.0.tgz", - "integrity": "sha1-kqSWkGX5xwxpR1PVUkj8aPj2Usk=", - "dev": true - }, "send": { - "version": "0.15.3", - "resolved": "https://registry.npmjs.org/send/-/send-0.15.3.tgz", - "integrity": "sha1-UBP5+ZAj31DRvZiSwZ4979HVMwk=", + "version": "0.15.4", + "resolved": "https://registry.npmjs.org/send/-/send-0.15.4.tgz", + "integrity": "sha1-mF+qPihLAnPHkzZKNcZze9k5Bbk=", "dev": true, "requires": { - "debug": "2.6.7", - "depd": "1.1.0", + "debug": "2.6.8", + "depd": "1.1.1", "destroy": "1.0.4", "encodeurl": "1.0.1", "escape-html": "1.0.3", - "etag": "1.8.0", + "etag": "1.8.1", "fresh": "0.5.0", - "http-errors": "1.6.1", + "http-errors": "1.6.2", "mime": "1.3.4", "ms": "2.0.0", "on-finished": "2.3.0", @@ -12943,11 +15796,26 @@ "statuses": "1.3.1" }, "dependencies": { + "debug": { + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", + "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, "mime": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz", "integrity": "sha1-EV+eO2s9rylZmDyzjxSaLUDrXVM=", "dev": true + }, + "statuses": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=", + "dev": true } } }, @@ -12957,7 +15825,7 @@ "integrity": "sha1-H24t2jnBaL+S0T+G1KkYkz9mftQ=", "dev": true, "requires": { - "no-case": "2.3.1", + "no-case": "2.3.2", "upper-case-first": "1.1.2" } }, @@ -12986,7 +15854,7 @@ "etag": "1.7.0", "fresh": "0.3.0", "ms": "0.7.2", - "parseurl": "1.3.1" + "parseurl": "1.3.2" }, "dependencies": { "etag": { @@ -13020,8 +15888,8 @@ "debug": "2.2.0", "escape-html": "1.0.3", "http-errors": "1.3.1", - "mime-types": "2.1.15", - "parseurl": "1.3.1" + "mime-types": "2.1.17", + "parseurl": "1.3.2" }, "dependencies": { "accepts": { @@ -13030,7 +15898,7 @@ "integrity": "sha1-5fHzkoxtlf2WVYw27D2dDeSm7Oo=", "dev": true, "requires": { - "mime-types": "2.1.15", + "mime-types": "2.1.17", "negotiator": "0.5.3" } }, @@ -13050,7 +15918,7 @@ "dev": true, "requires": { "inherits": "2.0.3", - "statuses": "1.3.1" + "statuses": "1.4.0" } }, "ms": { @@ -13068,16 +15936,16 @@ } }, "serve-static": { - "version": "1.12.3", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.12.3.tgz", - "integrity": "sha1-n0uhni8wMMVH+K+ZEHg47DjVseI=", + "version": "1.12.4", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.12.4.tgz", + "integrity": "sha1-m2qpjutyU8Tu3Ewfb9vKYJkBqWE=", "dev": true, "optional": true, "requires": { "encodeurl": "1.0.1", "escape-html": "1.0.3", - "parseurl": "1.3.1", - "send": "0.15.3" + "parseurl": "1.3.2", + "send": "0.15.4" } }, "set-blocking": { @@ -13104,6 +15972,23 @@ "integrity": "sha1-ZlZ+NwQ+608E2RvWWMDL77VbjgQ=", "dev": true }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "optional": true, + "requires": { + "shebang-regex": "1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true, + "optional": true + }, "shelljs": { "version": "0.7.8", "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.8.tgz", @@ -13111,7 +15996,7 @@ "dev": true, "requires": { "glob": "7.1.2", - "interpret": "1.0.3", + "interpret": "1.0.4", "rechoir": "0.6.2" } }, @@ -13127,12 +16012,29 @@ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true }, - "slice-ansi": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", - "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", "dev": true }, + "slice-ansi": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", + "integrity": "sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "2.0.0" + }, + "dependencies": { + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + } + } + }, "slice-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/slice-stream/-/slice-stream-1.0.0.tgz", @@ -13180,16 +16082,22 @@ "integrity": "sha1-Qb2xtz8w7GagTU4srRt2OH1NbZ8=", "dev": true, "requires": { - "no-case": "2.3.1" + "no-case": "2.3.2" } }, + "snakeize": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/snakeize/-/snakeize-0.1.0.tgz", + "integrity": "sha1-EMCI2LWOsHazIpu1oE4jLOEmQi0=", + "dev": true + }, "sntp": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", - "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.0.2.tgz", + "integrity": "sha1-UGQRDwr4X3z9t9a2ekACjOUrSys=", "dev": true, "requires": { - "hoek": "2.16.3" + "hoek": "4.2.0" } }, "socket.io": { @@ -13276,6 +16184,12 @@ "to-array": "0.1.4" }, "dependencies": { + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, "debug": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/debug/-/debug-2.3.3.tgz", @@ -13305,12 +16219,6 @@ "json3": "3.3.2" }, "dependencies": { - "component-emitter": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.1.2.tgz", - "integrity": "sha1-KWWU8nU9qmOZbSrwjRWpURbJrsM=", - "dev": true - }, "debug": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", @@ -13347,16 +16255,16 @@ } }, "source-map": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", - "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=" + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" }, "source-map-support": { - "version": "0.4.15", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.15.tgz", - "integrity": "sha1-AyAt9lwG0r2MfsI2KhkwVv7407E=", + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", + "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", "requires": { - "source-map": "0.5.6" + "source-map": "0.5.7" } }, "sourcemap-codec": { @@ -13365,7 +16273,7 @@ "integrity": "sha1-mtb5vb1pGTEBbjCTnbyGhnMyMUY=", "dev": true, "requires": { - "vlq": "0.2.2" + "vlq": "0.2.3" } }, "sparkles": { @@ -13402,15 +16310,15 @@ "dev": true }, "specificity": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/specificity/-/specificity-0.3.1.tgz", - "integrity": "sha1-8bBoQkzjF64HR42V3jwhz4Xo1Wc=", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/specificity/-/specificity-0.3.2.tgz", + "integrity": "sha512-Nc/QN/A425Qog7j9aHmwOrlwX2e7pNI47ciwxwy4jOlvbbMHkNNJchit+FX+UjF3IAdiaaV5BKeWuDUnws6G1A==", "dev": true }, "split": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/split/-/split-1.0.0.tgz", - "integrity": "sha1-xDlc5oOrzSVLwo/h2rtuXCfc/64=", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=", "dev": true, "requires": { "through": "2.3.8" @@ -13427,9 +16335,9 @@ } }, "split2": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/split2/-/split2-2.1.1.tgz", - "integrity": "sha1-eh9VHhdqkOzTNF9yRqDP4XXvT9A=", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-2.2.0.tgz", + "integrity": "sha512-RAb22TG39LhI31MbreBgIuKiIKhVsawfTgEGqKHTK87aG+ul/PB8Sqoi3I7kVdRWiCfrKxK3uo4/YUkpNvhPbw==", "dev": true, "requires": { "through2": "2.0.3" @@ -13455,14 +16363,6 @@ "getpass": "0.1.7", "jsbn": "0.1.1", "tweetnacl": "0.14.5" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - } } }, "stack-trace": { @@ -13472,9 +16372,9 @@ "dev": true }, "statuses": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", - "integrity": "sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", "dev": true }, "stdout-stream": { @@ -13551,15 +16451,6 @@ "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", "dev": true }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, "string-format-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/string-format-obj/-/string-format-obj-1.1.0.tgz", @@ -13593,6 +16484,15 @@ "strip-ansi": "3.0.1" } }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, "stringify-object": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-0.1.8.tgz", @@ -13621,13 +16521,10 @@ } }, "strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", - "dev": true, - "requires": { - "is-utf8": "0.2.1" - } + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true }, "strip-eof": { "version": "1.0.0", @@ -13674,7 +16571,7 @@ "log-symbols": "1.0.2", "minimist": "1.2.0", "plur": "2.1.2", - "postcss": "5.2.17", + "postcss": "5.2.18", "postcss-reporter": "1.4.1", "postcss-selector-parser": "2.2.3", "read-file-stdin": "0.2.1", @@ -13682,13 +16579,26 @@ "write-file-stdout": "0.0.2" }, "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, "plur": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/plur/-/plur-2.1.2.tgz", "integrity": "sha1-dIJFLBoPUI4+NE6uwxLJHCncZVo=", "dev": true, "requires": { - "irregular-plurals": "1.3.0" + "irregular-plurals": "1.4.0" } }, "postcss-reporter": { @@ -13700,8 +16610,14 @@ "chalk": "1.1.3", "lodash": "4.17.4", "log-symbols": "1.0.2", - "postcss": "5.2.17" + "postcss": "5.2.18" } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true } } }, @@ -13713,9 +16629,9 @@ "requires": { "autoprefixer": "6.7.7", "balanced-match": "0.4.2", - "chalk": "2.0.1", + "chalk": "2.2.0", "colorguard": "1.2.0", - "cosmiconfig": "2.1.3", + "cosmiconfig": "2.2.2", "debug": "2.6.7", "doiuse": "2.6.0", "execall": "1.0.0", @@ -13724,7 +16640,7 @@ "globby": "6.1.0", "globjoin": "0.1.4", "html-tags": "2.0.0", - "ignore": "3.3.3", + "ignore": "3.3.5", "imurmurhash": "0.1.4", "known-css-properties": "0.2.0", "lodash": "4.17.4", @@ -13734,7 +16650,7 @@ "micromatch": "2.3.11", "normalize-selector": "0.2.0", "pify": "2.3.0", - "postcss": "5.2.17", + "postcss": "5.2.18", "postcss-less": "0.14.0", "postcss-media-query-parser": "0.2.3", "postcss-reporter": "3.0.0", @@ -13743,13 +16659,13 @@ "postcss-selector-parser": "2.2.3", "postcss-value-parser": "3.3.0", "resolve-from": "3.0.0", - "specificity": "0.3.1", - "string-width": "2.1.0", + "specificity": "0.3.2", + "string-width": "2.1.1", "style-search": "0.1.0", "stylehacks": "2.3.2", "sugarss": "0.2.0", "svg-tags": "1.0.0", - "table": "4.0.1" + "table": "4.0.2" }, "dependencies": { "ansi-regex": { @@ -13759,9 +16675,9 @@ "dev": true }, "ansi-styles": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.1.0.tgz", - "integrity": "sha1-CcIC1ckX7CMYjKpcnLkXnNlUd1A=", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", + "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", "dev": true, "requires": { "color-convert": "1.9.0" @@ -13774,14 +16690,14 @@ "dev": true }, "chalk": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.0.1.tgz", - "integrity": "sha512-Mp+FXEI+FrwY/XYV45b2YD3E8i3HwnEAoFcM0qlZzq/RZ9RwWitt2Y/c7cqRAz70U7hfekqx6qNYthuKFO6K0g==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.2.0.tgz", + "integrity": "sha512-0BMM/2hG3ZaoPfR6F+h/oWpZtsh3b/s62TjSM6MGCJWEbJDN1acqCXvyhhZsDSVFklpebUoQ5O1kKC7lOzrn9g==", "dev": true, "requires": { - "ansi-styles": "3.1.0", + "ansi-styles": "3.2.0", "escape-string-regexp": "1.0.5", - "supports-color": "4.2.0" + "supports-color": "4.5.0" } }, "get-stdin": { @@ -13815,16 +16731,10 @@ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, "string-width": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.0.tgz", - "integrity": "sha1-AwZkVh/BRslCPsfZeP4kV0N/5tA=", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { "is-fullwidth-code-point": "2.0.0", @@ -13841,9 +16751,9 @@ } }, "supports-color": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.2.0.tgz", - "integrity": "sha512-Ts0Mu/A1S1aZxEJNG88I4Oc9rcZSBFNac5e27yh4j2mqbhZSSzR1Ah79EYwSn9Zuh7lrlGD2cVGzw1RKGzyLSg==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", + "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", "dev": true, "requires": { "has-flag": "2.0.0" @@ -13852,14 +16762,14 @@ } }, "stylus-lookup": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/stylus-lookup/-/stylus-lookup-1.0.1.tgz", - "integrity": "sha1-qMvC4NLYcVdMN/Dim0axjcmfHWk=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stylus-lookup/-/stylus-lookup-1.0.2.tgz", + "integrity": "sha1-eVm+rAu1V+vROvO8Osvu/7J2YNQ=", "dev": true, "requires": { "commander": "2.8.1", - "debug": "2.2.0", - "is-relative-path": "1.0.1" + "debug": "3.1.0", + "is-relative-path": "1.0.2" }, "dependencies": { "commander": { @@ -13872,19 +16782,13 @@ } }, "debug": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", - "integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { - "ms": "0.7.1" + "ms": "2.0.0" } - }, - "ms": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz", - "integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=", - "dev": true } } }, @@ -13894,32 +16798,13 @@ "integrity": "sha1-rDQjdWMyfG/4l7ZHQr9q7BkK054=", "dev": true, "requires": { - "postcss": "5.2.17" - } - }, - "superagent": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.5.2.tgz", - "integrity": "sha1-M2GjlxVnUEw1EGOr6q4PqiPb8/g=", - "dev": true, - "optional": true, - "requires": { - "component-emitter": "1.2.1", - "cookiejar": "2.1.1", - "debug": "2.6.7", - "extend": "3.0.1", - "form-data": "2.1.4", - "formidable": "1.1.1", - "methods": "1.1.2", - "mime": "1.3.6", - "qs": "6.4.0", - "readable-stream": "2.3.3" + "postcss": "5.2.18" } }, "superstatic": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/superstatic/-/superstatic-4.1.0.tgz", - "integrity": "sha1-5/l3rHAZOtgOFU/RNqVLTUURCwE=", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/superstatic/-/superstatic-5.0.1.tgz", + "integrity": "sha1-8Kg5Qq2Ok8XFOpg0HEo94inf+U4=", "dev": true, "requires": { "as-array": "2.0.0", @@ -13928,9 +16813,9 @@ "chalk": "1.1.3", "char-spinner": "1.0.1", "compare-semver": "1.1.0", - "compression": "1.7.0", - "connect": "3.6.2", - "connect-query": "0.2.0", + "compression": "1.7.1", + "connect": "3.6.5", + "connect-query": "1.0.0", "destroy": "1.0.4", "fast-url-parser": "1.1.3", "fs-extra": "0.30.0", @@ -13940,15 +16825,15 @@ "is-url": "1.2.2", "join-path": "1.1.1", "lodash": "4.17.4", - "mime-types": "2.1.15", + "mime-types": "2.1.17", "minimatch": "3.0.4", - "morgan": "1.8.2", + "morgan": "1.9.0", "nash": "2.0.4", "on-finished": "2.3.0", "on-headers": "1.0.1", "path-to-regexp": "1.7.0", - "router": "1.3.1", - "rsvp": "3.6.1", + "router": "1.3.2", + "rsvp": "3.6.2", "string-length": "1.0.1", "try-require": "1.2.1", "update-notifier": "1.0.3" @@ -13960,6 +16845,19 @@ "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", "dev": true }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, "configstore": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/configstore/-/configstore-2.1.0.tgz", @@ -14005,12 +16903,6 @@ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", "dev": true }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, "path-to-regexp": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", @@ -14020,6 +16912,12 @@ "isarray": "0.0.1" } }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, "update-notifier": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-1.0.3.tgz", @@ -14064,22 +16962,24 @@ } } }, - "supertest": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-3.0.0.tgz", - "integrity": "sha1-jUu2j9GDDuBwM7HFpamkAhyWUpY=", + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "dev": true, - "optional": true, "requires": { - "methods": "1.1.2", - "superagent": "3.5.2" + "has-flag": "1.0.0" } }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true + "sver-compat": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/sver-compat/-/sver-compat-1.5.0.tgz", + "integrity": "sha1-PPh9/rTQe0o/FIJ7wYaz/QxkXNg=", + "dev": true, + "requires": { + "es6-iterator": "2.0.3", + "es6-symbol": "3.1.1" + } }, "svg-tags": { "version": "1.0.0", @@ -14126,31 +17026,51 @@ } }, "table": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/table/-/table-4.0.1.tgz", - "integrity": "sha1-qBFsEz+sLGH0pCCrbN9cTWHw5DU=", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/table/-/table-4.0.2.tgz", + "integrity": "sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA==", "dev": true, "requires": { - "ajv": "4.11.8", - "ajv-keywords": "1.5.1", - "chalk": "1.1.3", + "ajv": "5.2.3", + "ajv-keywords": "2.1.0", + "chalk": "2.2.0", "lodash": "4.17.4", - "slice-ansi": "0.0.4", - "string-width": "2.1.0" + "slice-ansi": "1.0.0", + "string-width": "2.1.1" }, "dependencies": { - "ajv-keywords": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz", - "integrity": "sha1-MU3QpLM2j609/NxU7eYXG4htrzw=", - "dev": true - }, "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "dev": true }, + "ansi-styles": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", + "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "dev": true, + "requires": { + "color-convert": "1.9.0" + } + }, + "chalk": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.2.0.tgz", + "integrity": "sha512-0BMM/2hG3ZaoPfR6F+h/oWpZtsh3b/s62TjSM6MGCJWEbJDN1acqCXvyhhZsDSVFklpebUoQ5O1kKC7lOzrn9g==", + "dev": true, + "requires": { + "ansi-styles": "3.2.0", + "escape-string-regexp": "1.0.5", + "supports-color": "4.5.0" + } + }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true + }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", @@ -14158,9 +17078,9 @@ "dev": true }, "string-width": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.0.tgz", - "integrity": "sha1-AwZkVh/BRslCPsfZeP4kV0N/5tA=", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { "is-fullwidth-code-point": "2.0.0", @@ -14175,24 +17095,43 @@ "requires": { "ansi-regex": "3.0.0" } + }, + "supports-color": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", + "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", + "dev": true, + "requires": { + "has-flag": "2.0.0" + } } } }, "tapable": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.6.tgz", - "integrity": "sha1-IGvo4YiGC1FEJTdebxrom/sB/Y0=", + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.8.tgz", + "integrity": "sha1-mTcqXJmb8t8WCvwNdL7U9HlIzSI=", "dev": true }, "tar": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz", - "integrity": "sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-3.2.1.tgz", + "integrity": "sha512-ZSzds1E0IqutvMU8HxjMaU8eB7urw2fGwTq88ukDOVuUIh0656l7/P7LiVPxhO5kS4flcRJQk8USG+cghQbTUQ==", "dev": true, "requires": { - "block-stream": "0.0.9", - "fstream": "1.0.11", - "inherits": "2.0.3" + "chownr": "1.0.1", + "minipass": "2.2.1", + "minizlib": "1.0.4", + "mkdirp": "0.5.1", + "yallist": "3.0.2" + }, + "dependencies": { + "yallist": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz", + "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=", + "dev": true + } } }, "tar-stream": { @@ -14202,7 +17141,7 @@ "dev": true, "requires": { "bl": "1.2.1", - "end-of-stream": "1.0.0", + "end-of-stream": "1.4.0", "readable-stream": "2.3.3", "xtend": "4.0.1" } @@ -14219,16 +17158,16 @@ "integrity": "sha1-Bk5Im0tb9gumpre8fy9cJ07Pgmk=", "dev": true, "requires": { - "duplexify": "3.5.0", + "duplexify": "3.5.1", "fork-stream": "0.0.4", "merge-stream": "1.0.1", "through2": "2.0.3" } }, "text-extensions": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.5.0.tgz", - "integrity": "sha1-0cstFLXQvEW/3Kigikc/aMfrDLw=", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.7.0.tgz", + "integrity": "sha512-AKXZeDq230UaSzaO5s3qQUZOaC7iKbzq0jOFL614R7d9R593HLqAOL0cYoqLdkNrjBSOdmoQI06yigq1TSBXAg==", "dev": true }, "text-table": { @@ -14303,7 +17242,7 @@ "debug": "2.2.0", "faye-websocket": "0.10.0", "livereload-js": "2.2.2", - "parseurl": "1.3.1", + "parseurl": "1.3.2", "qs": "5.1.0" }, "dependencies": { @@ -14314,9 +17253,9 @@ "dev": true, "requires": { "bytes": "2.2.0", - "content-type": "1.0.2", + "content-type": "1.0.4", "debug": "2.2.0", - "depd": "1.1.0", + "depd": "1.1.1", "http-errors": "1.3.1", "iconv-lite": "0.4.13", "on-finished": "2.3.0", @@ -14348,6 +17287,15 @@ "ms": "0.7.1" } }, + "faye-websocket": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", + "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", + "dev": true, + "requires": { + "websocket-driver": "0.7.0" + } + }, "http-errors": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.3.1.tgz", @@ -14355,7 +17303,7 @@ "dev": true, "requires": { "inherits": "2.0.3", - "statuses": "1.3.1" + "statuses": "1.4.0" } }, "iconv-lite": { @@ -14403,7 +17351,7 @@ "integrity": "sha1-PhJyFtpY0rxb7PE3q5Ha46fNj6o=", "dev": true, "requires": { - "no-case": "2.3.1", + "no-case": "2.3.2", "upper-case": "1.1.3" } }, @@ -14429,12 +17377,20 @@ "dev": true, "requires": { "hoek": "2.16.3" + }, + "dependencies": { + "hoek": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", + "dev": true + } } }, "tough-cookie": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", - "integrity": "sha1-8IH3bkyFcg5sN6X6ztc3FQ2EByo=", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", + "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", "dev": true, "requires": { "punycode": "1.4.1" @@ -14500,41 +17456,41 @@ "dev": true }, "ts-node": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-3.2.0.tgz", - "integrity": "sha1-mBTwwBQXhJAM8S/vEZetS39NI9E=", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-3.3.0.tgz", + "integrity": "sha1-wTxqMCTjC+EYDdUwOPwgkonUv2k=", "dev": true, "requires": { "arrify": "1.0.1", - "chalk": "2.0.1", - "diff": "3.3.0", + "chalk": "2.2.0", + "diff": "3.4.0", "make-error": "1.3.0", "minimist": "1.2.0", "mkdirp": "0.5.1", - "source-map-support": "0.4.15", + "source-map-support": "0.4.18", "tsconfig": "6.0.0", - "v8flags": "2.1.1", + "v8flags": "3.0.1", "yn": "2.0.0" }, "dependencies": { "ansi-styles": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.1.0.tgz", - "integrity": "sha1-CcIC1ckX7CMYjKpcnLkXnNlUd1A=", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", + "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", "dev": true, "requires": { "color-convert": "1.9.0" } }, "chalk": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.0.1.tgz", - "integrity": "sha512-Mp+FXEI+FrwY/XYV45b2YD3E8i3HwnEAoFcM0qlZzq/RZ9RwWitt2Y/c7cqRAz70U7hfekqx6qNYthuKFO6K0g==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.2.0.tgz", + "integrity": "sha512-0BMM/2hG3ZaoPfR6F+h/oWpZtsh3b/s62TjSM6MGCJWEbJDN1acqCXvyhhZsDSVFklpebUoQ5O1kKC7lOzrn9g==", "dev": true, "requires": { - "ansi-styles": "3.1.0", + "ansi-styles": "3.2.0", "escape-string-regexp": "1.0.5", - "supports-color": "4.2.0" + "supports-color": "4.5.0" } }, "has-flag": { @@ -14544,13 +17500,22 @@ "dev": true }, "supports-color": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.2.0.tgz", - "integrity": "sha512-Ts0Mu/A1S1aZxEJNG88I4Oc9rcZSBFNac5e27yh4j2mqbhZSSzR1Ah79EYwSn9Zuh7lrlGD2cVGzw1RKGzyLSg==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", + "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", "dev": true, "requires": { "has-flag": "2.0.0" } + }, + "v8flags": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.0.1.tgz", + "integrity": "sha1-3Oj8N5wX2fLJ6e142JzgAFKxt2s=", + "dev": true, + "requires": { + "homedir-polyfill": "1.0.1" + } } } }, @@ -14562,25 +17527,26 @@ "requires": { "strip-bom": "3.0.0", "strip-json-comments": "2.0.1" - }, - "dependencies": { - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - } } }, "tsconfig-paths": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-2.2.0.tgz", - "integrity": "sha1-x2rOfyxFTNMcpvcsM4dtuuAgAOg=", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-2.3.0.tgz", + "integrity": "sha512-YRahNqogNr4Oh0qVZ7SICry+4KCeGgeDc6YuIfLYblXIQE/XWtnL1QahX681YtwisjUpePEbxfrBKTGRCNsKNw==", "dev": true, "requires": { "tsconfig": "5.0.3" }, "dependencies": { + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "0.2.1" + } + }, "tsconfig": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-5.0.3.tgz", @@ -14596,52 +17562,73 @@ } }, "tsickle": { - "version": "0.23.5", - "resolved": "https://registry.npmjs.org/tsickle/-/tsickle-0.23.5.tgz", - "integrity": "sha1-kYgnFwwMe4kYPWxhc8NWQZG6xqo=", + "version": "0.24.1", + "resolved": "https://registry.npmjs.org/tsickle/-/tsickle-0.24.1.tgz", + "integrity": "sha512-XloFQZhVhgjpQsi3u2ORNRJvuID5sflOg6HfP093IqAbhE1+fIUXznULpdDwHgG4p+v8w78KdHruQtkWUKx5AQ==", "requires": { "minimist": "1.2.0", "mkdirp": "0.5.1", - "source-map": "0.5.6", - "source-map-support": "0.4.15" + "source-map": "0.5.7", + "source-map-support": "0.4.18" } }, "tslib": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.7.1.tgz", - "integrity": "sha1-vIAEFkaRkjp5/oN4u+s9ogF1OOw=" + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.8.0.tgz", + "integrity": "sha512-ymKWWZJST0/CkgduC2qkzjMOWr4bouhuURNXCn/inEX0L57BnRG6FhX76o7FOnsjHazCjfU2LKeSrlS2sIKQJg==" }, "tslint": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.7.0.tgz", - "integrity": "sha1-wl4NDJL6EgHCvDDoROCOaCtPNVI=", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.8.0.tgz", + "integrity": "sha1-H0mtWy53x2w69N3K5VKuTjYS6xM=", "dev": true, "requires": { "babel-code-frame": "6.26.0", - "colors": "1.1.2", + "builtin-modules": "1.1.1", + "chalk": "2.2.0", "commander": "2.11.0", - "diff": "3.3.0", + "diff": "3.4.0", "glob": "7.1.2", "minimatch": "3.0.4", - "resolve": "1.3.3", - "semver": "5.3.0", - "tslib": "1.7.1", - "tsutils": "2.8.2" + "resolve": "1.4.0", + "semver": "5.4.1", + "tslib": "1.8.0", + "tsutils": "2.12.1" }, "dependencies": { - "colors": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", - "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", + "ansi-styles": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", + "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", + "dev": true, + "requires": { + "color-convert": "1.9.0" + } + }, + "chalk": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.2.0.tgz", + "integrity": "sha512-0BMM/2hG3ZaoPfR6F+h/oWpZtsh3b/s62TjSM6MGCJWEbJDN1acqCXvyhhZsDSVFklpebUoQ5O1kKC7lOzrn9g==", + "dev": true, + "requires": { + "ansi-styles": "3.2.0", + "escape-string-regexp": "1.0.5", + "supports-color": "4.5.0" + } + }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", "dev": true }, - "tsutils": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.8.2.tgz", - "integrity": "sha1-LBSGukMSYIRbCsb5Aq/Z1wio6mo=", + "supports-color": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz", + "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", "dev": true, "requires": { - "tslib": "1.7.1" + "has-flag": "2.0.0" } } } @@ -14653,12 +17640,12 @@ "dev": true }, "tsutils": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.6.0.tgz", - "integrity": "sha1-5emceaiszTl3zhjYP98dI1psLrs=", + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.12.1.tgz", + "integrity": "sha1-9Nlc4zkciXHkblTEzw7bCiHdWyQ=", "dev": true, "requires": { - "tslib": "1.7.1" + "tslib": "1.8.0" } }, "tunnel-agent": { @@ -14693,7 +17680,7 @@ "dev": true, "requires": { "media-typer": "0.3.0", - "mime-types": "2.1.15" + "mime-types": "2.1.17" } }, "typedarray": { @@ -14703,20 +17690,73 @@ "dev": true }, "typescript": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.2.2.tgz", - "integrity": "sha1-YGAiUIR5tV/6NotY/uljoD39eww=", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.4.2.tgz", + "integrity": "sha1-+DlfhdRZJ2BnyYiqQYN6j4KHCEQ=", "dev": true }, + "typescript-eslint-parser": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typescript-eslint-parser/-/typescript-eslint-parser-1.0.2.tgz", + "integrity": "sha1-/Sq6zy7j2Tgqs+RJyHYra+rk0Nc=", + "dev": true, + "requires": { + "lodash.unescape": "4.0.0", + "object-assign": "4.1.1" + } + }, "uglify-js": { "version": "2.8.29", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", "integrity": "sha1-KcVzMUgFe7Th913zW3qcty5qWd0=", "dev": true, "requires": { - "source-map": "0.5.6", + "source-map": "0.5.7", "uglify-to-browserify": "1.0.2", "yargs": "3.10.0" + }, + "dependencies": { + "camelcase": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", + "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", + "dev": true + }, + "cliui": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", + "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", + "dev": true, + "requires": { + "center-align": "0.1.3", + "right-align": "0.1.3", + "wordwrap": "0.0.2" + } + }, + "window-size": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", + "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", + "dev": true + }, + "wordwrap": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", + "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", + "dev": true + }, + "yargs": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", + "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "dev": true, + "requires": { + "camelcase": "1.2.1", + "cliui": "2.1.0", + "decamelize": "1.2.0", + "window-size": "0.1.0" + } + } } }, "uglify-to-browserify": { @@ -14801,7 +17841,7 @@ "requires": { "async": "0.2.10", "node-uuid": "1.4.8", - "request": "2.81.0", + "request": "2.83.0", "underscore": "1.6.0" }, "dependencies": { @@ -14820,9 +17860,9 @@ } }, "universalify": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.0.tgz", - "integrity": "sha1-nrHEZR3rzGcMyU8adXYjMruWd3g=", + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz", + "integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc=", "dev": true }, "unpipe": { @@ -14913,6 +17953,19 @@ "string-length": "1.0.1" }, "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, "configstore": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/configstore/-/configstore-1.4.0.tgz", @@ -14935,7 +17988,7 @@ "integrity": "sha1-5dDtSvVfw+701WAHdp2YGSvLLso=", "dev": true, "requires": { - "duplexify": "3.5.0", + "duplexify": "3.5.1", "infinity-agent": "2.0.3", "is-redirect": "1.0.0", "is-stream": "1.1.0", @@ -14964,12 +18017,6 @@ "package-json": "1.2.0" } }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, "package-json": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/package-json/-/package-json-1.2.0.tgz", @@ -14989,6 +18036,12 @@ "is-finite": "1.0.2" } }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, "timed-out": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-2.0.0.tgz", @@ -15053,6 +18106,13 @@ "prepend-http": "1.0.4" } }, + "url-to-options": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", + "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=", + "dev": true, + "optional": true + }, "user-home": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz", @@ -15063,9 +18123,9 @@ } }, "useragent": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.2.0.tgz", - "integrity": "sha1-74X0GQPP0F4rqMEa5hJJx6a79mM=", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/useragent/-/useragent-2.2.1.tgz", + "integrity": "sha1-z1k+9PLRdYdei7ZY6pLhik/QbY4=", "dev": true, "requires": { "lru-cache": "2.2.4", @@ -15095,7 +18155,7 @@ "requires": { "async": "0.9.2", "deep-equal": "0.2.2", - "i": "0.3.5", + "i": "0.3.6", "mkdirp": "0.5.1", "ncp": "1.0.1", "rimraf": "2.6.1" @@ -15168,18 +18228,20 @@ "dev": true }, "vary": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.1.tgz", - "integrity": "sha1-Z1Neu2lMHVIldFeYRmUyP1h+jTc=", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", "dev": true }, "verror": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz", - "integrity": "sha1-z/XfEpRtKX0rqu+qJoniW+AcAFw=", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", "dev": true, "requires": { - "extsprintf": "1.0.2" + "assert-plus": "1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "1.3.0" } }, "vhost": { @@ -15198,28 +18260,8 @@ "clone-buffer": "1.0.0", "clone-stats": "1.0.0", "cloneable-readable": "1.0.0", - "remove-trailing-separator": "1.0.2", + "remove-trailing-separator": "1.1.0", "replace-ext": "1.0.0" - }, - "dependencies": { - "clone": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.1.tgz", - "integrity": "sha1-0hfR6WERjjrJpLi7oyhVU79kfNs=", - "dev": true - }, - "clone-stats": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", - "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=", - "dev": true - }, - "replace-ext": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", - "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=", - "dev": true - } } }, "vinyl-fs": { @@ -15244,6 +18286,12 @@ "integrity": "sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8=", "dev": true }, + "clone-stats": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", + "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=", + "dev": true + }, "graceful-fs": { "version": "3.0.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.11.tgz", @@ -15315,13 +18363,13 @@ "integrity": "sha1-q2VJ1h0XLCsbh75cUI0jnI74dwU=", "dev": true, "requires": { - "source-map": "0.5.6" + "source-map": "0.5.7" } }, "vlq": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.2.tgz", - "integrity": "sha1-4xbVJXtAuGu0PLjV/qXX9U1rDKE=", + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/vlq/-/vlq-0.2.3.tgz", + "integrity": "sha512-DRibZL6DsNhIgYQ+wNdWDL2SL3bKPlVrRiBqV5yuMm++op8W4kGFtaQfCs4KEJn0wBZcHVHJ3eoywX8983k1ow==", "dev": true }, "void-elements": { @@ -15337,9 +18385,9 @@ "dev": true }, "wd": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/wd/-/wd-1.4.0.tgz", - "integrity": "sha512-VHii2f+jck5fgEcTQYCR3z99B99tPz0HlLCGsNowI2qsI21xMnKwd9O3SnJQnEBq0Erx9FPFfiZno+OYtXDXyw==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/wd/-/wd-1.4.1.tgz", + "integrity": "sha512-C0wWd2X4SWWcyx5qxaixiZE4Vb07sl0yDfWHPeml8lDHSbmI9erE9BmTHIqOGoDxGgJ3/hkFmODQ7ZLKiF8+8Q==", "dev": true, "requires": { "archiver": "1.3.0", @@ -15369,6 +18417,12 @@ "zip-stream": "1.2.0" } }, + "assert-plus": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", + "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", + "dev": true + }, "async": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/async/-/async-2.0.1.tgz", @@ -15378,16 +18432,44 @@ "lodash": "4.16.2" } }, + "aws-sign2": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", + "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", + "dev": true + }, + "boom": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", + "dev": true, + "requires": { + "hoek": "2.16.3" + } + }, "caseless": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=", "dev": true }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, "compress-commons": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-1.2.0.tgz", - "integrity": "sha1-WFhwku8g03y1i68AARLJJ4/3O58=", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-1.2.2.tgz", + "integrity": "sha1-UkqfEJA/OoEzibAiXSfEi7dRiQ8=", "dev": true, "requires": { "buffer-crc32": "0.2.13", @@ -15397,9 +18479,9 @@ } }, "crc": { - "version": "3.4.4", - "resolved": "https://registry.npmjs.org/crc/-/crc-3.4.4.tgz", - "integrity": "sha1-naHpgOO9RPxck79as9ozeNheRms=", + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/crc/-/crc-3.5.0.tgz", + "integrity": "sha1-mLi6fUiWZbo5efWbITgTdBAaGWQ=", "dev": true }, "crc32-stream": { @@ -15408,10 +18490,30 @@ "integrity": "sha1-483TtN8xaN10494/u8t7KX/pCPQ=", "dev": true, "requires": { - "crc": "3.4.4", + "crc": "3.5.0", "readable-stream": "2.3.3" } }, + "cryptiles": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", + "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", + "dev": true, + "requires": { + "boom": "2.10.1" + } + }, + "form-data": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", + "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", + "dev": true, + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.17" + } + }, "har-validator": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz", @@ -15420,22 +18522,45 @@ "requires": { "chalk": "1.1.3", "commander": "2.11.0", - "is-my-json-valid": "2.16.0", + "is-my-json-valid": "2.16.1", "pinkie-promise": "2.0.1" } }, + "hawk": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", + "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", + "dev": true, + "requires": { + "boom": "2.10.1", + "cryptiles": "2.0.5", + "hoek": "2.16.3", + "sntp": "1.0.9" + } + }, + "hoek": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", + "dev": true + }, + "http-signature": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", + "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", + "dev": true, + "requires": { + "assert-plus": "0.2.0", + "jsprim": "1.4.1", + "sshpk": "1.13.1" + } + }, "lodash": { "version": "4.16.2", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.16.2.tgz", "integrity": "sha1-PmJtuCcEimmSgaihJSJjJs/A5lI=", "dev": true }, - "q": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz", - "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=", - "dev": true - }, "qs": { "version": "6.3.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.3.2.tgz", @@ -15461,15 +18586,30 @@ "is-typedarray": "1.0.0", "isstream": "0.1.2", "json-stringify-safe": "5.0.1", - "mime-types": "2.1.15", + "mime-types": "2.1.17", "oauth-sign": "0.8.2", "qs": "6.3.2", "stringstream": "0.0.5", - "tough-cookie": "2.3.2", + "tough-cookie": "2.3.3", "tunnel-agent": "0.4.3", "uuid": "3.1.0" } }, + "sntp": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", + "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", + "dev": true, + "requires": { + "hoek": "2.16.3" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, "tar-stream": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.5.4.tgz", @@ -15477,7 +18617,7 @@ "dev": true, "requires": { "bl": "1.2.1", - "end-of-stream": "1.0.0", + "end-of-stream": "1.4.0", "readable-stream": "2.3.3", "xtend": "4.0.1" } @@ -15495,7 +18635,7 @@ "dev": true, "requires": { "archiver-utils": "1.3.0", - "compress-commons": "1.2.0", + "compress-commons": "1.2.2", "lodash": "4.16.2", "readable-stream": "2.3.3" } @@ -15503,9 +18643,9 @@ } }, "web-animations-js": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/web-animations-js/-/web-animations-js-2.2.5.tgz", - "integrity": "sha1-JsobNME0czKggT+LK/5pZk76gKo=", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/web-animations-js/-/web-animations-js-2.3.1.tgz", + "integrity": "sha1-Om2bwVGWN3qQ+OKAP6UmIWWwRRA=", "dev": true }, "webdriver-js-extender": { @@ -15556,7 +18696,7 @@ "dev": true, "requires": { "sax": "0.6.1", - "xmlbuilder": "4.2.1" + "xmlbuilder": "9.0.4" } } } @@ -15568,18 +18708,19 @@ "dev": true }, "websocket-driver": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.6.5.tgz", - "integrity": "sha1-XLJVbOuF9Dc8bYI4qmkchFThOjY=", + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.0.tgz", + "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", "dev": true, "requires": { - "websocket-extensions": "0.1.1" + "http-parser-js": "0.4.9", + "websocket-extensions": "0.1.2" } }, "websocket-extensions": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.1.tgz", - "integrity": "sha1-domUmcGEtu91Q3fC27DNbLVdKec=", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.2.tgz", + "integrity": "sha1-Dhh4HeYpoYMIzhSBZQ9n/6JpOl0=", "dev": true }, "whatwg-encoding": { @@ -15615,9 +18756,9 @@ "integrity": "sha1-xxMLan6gRpPoQs3J56Hyqjmjn4I=" }, "which": { - "version": "1.2.14", - "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz", - "integrity": "sha1-mofEN48D6CfOyvGs31bHNsAcFOU=", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", + "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", "dev": true, "requires": { "isexe": "2.0.0" @@ -15649,15 +18790,15 @@ } }, "window-size": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", - "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", + "integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY=", "dev": true }, "winston": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/winston/-/winston-2.3.1.tgz", - "integrity": "sha1-C0hCDZeMAYBM8CMLZIhhWYIloRk=", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.0.tgz", + "integrity": "sha1-gIBQuT1SZh7Z+2wms/DIJnCLCu4=", "dev": true, "requires": { "async": "1.0.0", @@ -15706,6 +18847,23 @@ "requires": { "boom": "2.10.1", "hoek": "2.16.3" + }, + "dependencies": { + "boom": { + "version": "2.10.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", + "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", + "dev": true, + "requires": { + "hoek": "2.16.3" + } + }, + "hoek": { + "version": "2.16.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", + "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", + "dev": true + } } }, "write": { @@ -15718,14 +18876,14 @@ } }, "write-file-atomic": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.1.0.tgz", - "integrity": "sha512-0TZ20a+xcIl4u0+Mj5xDH2yOWdmQiXlKf9Hm+TgDXjTMsEYb+gDrmb8e8UNAzMCitX8NBqG4Z/FUQIyzv/R1JQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.3.0.tgz", + "integrity": "sha512-xuPeK4OdjWqtfi59ylvVL0Yn35SF3zgcAcv7rBPFHVaEapaDr4GdGgm3j7ckTwH9wHL7fGmgfAnb0+THrHb8tA==", "dev": true, "requires": { "graceful-fs": "4.1.11", "imurmurhash": "0.1.4", - "slide": "1.1.6" + "signal-exit": "3.0.2" } }, "write-file-stdout": { @@ -15775,23 +18933,20 @@ "dev": true }, "xml2js": { - "version": "0.4.17", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.17.tgz", - "integrity": "sha1-F76T6q4/O3eTWceVtBlwWogX6Gg=", + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", "dev": true, "requires": { "sax": "1.2.4", - "xmlbuilder": "4.2.1" + "xmlbuilder": "9.0.4" } }, "xmlbuilder": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-4.2.1.tgz", - "integrity": "sha1-qlijBBoGb5DqoWwvU4n/GfP0YaU=", - "dev": true, - "requires": { - "lodash": "4.17.4" - } + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.4.tgz", + "integrity": "sha1-UZy0ymhtAFqEINNJbz8MruzKWA8=", + "dev": true }, "xmlhttprequest-ssl": { "version": "1.5.3", @@ -15818,23 +18973,18 @@ "dev": true }, "yargs": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", + "version": "3.32.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", + "integrity": "sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=", "dev": true, "requires": { - "camelcase": "1.2.1", - "cliui": "2.1.0", + "camelcase": "2.1.1", + "cliui": "3.2.0", "decamelize": "1.2.0", - "window-size": "0.1.0" - }, - "dependencies": { - "camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", - "dev": true - } + "os-locale": "1.4.0", + "string-width": "1.0.2", + "window-size": "0.1.4", + "y18n": "3.2.1" } }, "yargs-parser": { @@ -15912,9 +19062,9 @@ } }, "zone.js": { - "version": "0.8.12", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.8.12.tgz", - "integrity": "sha1-hv9QU8mK7CkaC/S7rFAdaUoFz7s=" + "version": "0.8.18", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.8.18.tgz", + "integrity": "sha512-knKOBQM0oea3/x9pdyDuDi7RhxDlJhOIkeixXSiTKWLgs4LpK37iBc+1HaHwzlciHUKT172CymJFKo8Xgh+44Q==" } } } diff --git a/package.json b/package.json index 64d6f1a0de56..b9b8dfa8b159 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "url": "https://github.com/angular/material2.git" }, "scripts": { + "postinstall": "ngc -p angular.tsconfig.json", "build": "gulp :publish:build-releases", "demo-app": "gulp serve:devapp", "test": "gulp test", @@ -19,39 +20,42 @@ "docs": "gulp docs", "api": "gulp api-docs" }, - "version": "2.0.0-beta.10", + "version": "5.0.0-rc.2", "license": "MIT", "engines": { "node": ">= 5.4.1" }, "dependencies": { - "@angular/animations": "~4.3.6", - "@angular/common": "~4.3.6", - "@angular/compiler": "~4.3.6", - "@angular/core": "~4.3.6", - "@angular/forms": "~4.3.6", - "@angular/http": "~4.3.6", - "@angular/platform-browser": "~4.3.6", + "@angular/animations": "^5.0.0", + "@angular/common": "^5.0.0", + "@angular/compiler": "^5.0.0", + "@angular/core": "^5.0.0", + "@angular/forms": "^5.0.0", + "@angular/platform-browser": "^5.0.0", "core-js": "^2.4.1", - "rxjs": "^5.0.1", + "rxjs": "^5.5.0", "systemjs": "0.19.43", - "tsickle": "^0.23.5", + "tsickle": "^0.24.x", "tslib": "^1.7.1", "zone.js": "^0.8.12" }, "devDependencies": { - "@angular/compiler-cli": "~4.3.6", - "@angular/platform-browser-dynamic": "~4.3.6", - "@angular/platform-server": "~4.3.6", - "@angular/router": "~4.3.6", - "@angular/tsc-wrapped": "~4.3.6", + "@angular/bazel": "^5.0.1", + "@angular/compiler-cli": "^5.0.0", + "@angular/http": "^5.0.0", + "@angular/platform-browser-dynamic": "^5.0.0", + "@angular/platform-server": "^5.0.0", + "@angular/router": "^5.0.0", + "@angular/upgrade": "^5.0.1", + "@bazel/ibazel": "0.0.1", + "@bazel/typescript": "0.2.x", "@google-cloud/storage": "^1.1.1", "@types/chalk": "^0.4.31", - "@types/fs-extra": "^3.0.1", - "@types/glob": "^5.0.30", + "@types/fs-extra": "^4.0.3", + "@types/glob": "^5.0.33", "@types/gulp": "3.8.32", - "@types/hammerjs": "^2.0.34", - "@types/jasmine": "2.5.45", + "@types/hammerjs": "^2.0.35", + "@types/jasmine": "^2.6.0", "@types/merge2": "^0.3.30", "@types/minimist": "^1.2.0", "@types/node": "^7.0.21", @@ -60,11 +64,11 @@ "axe-core": "^2.3.1", "axe-webdriverjs": "^1.1.1", "chalk": "^1.1.3", - "dgeni": "^0.4.7", - "dgeni-packages": "^0.19.1", + "dgeni": "^0.4.9", + "dgeni-packages": "^0.22.0", "firebase": "^4.0.0", "firebase-admin": "^5.0.0", - "firebase-tools": "^3.9.0", + "firebase-tools": "^3.11.0", "fs-extra": "^3.0.1", "glob": "^7.1.2", "google-closure-compiler": "20170409.0.0", @@ -76,47 +80,50 @@ "gulp-conventional-changelog": "^1.1.3", "gulp-dom": "^0.9.17", "gulp-flatten": "^0.3.1", - "gulp-highlight-files": "^0.0.4", + "gulp-highlight-files": "^0.0.5", "gulp-htmlmin": "^3.0.0", "gulp-if": "^2.0.2", "gulp-markdown": "^1.2.0", "gulp-rename": "^1.2.2", "gulp-sass": "^3.1.0", "gulp-transform": "^2.0.0", + "gulp-util": "^3.0.8", "hammerjs": "^2.0.8", "highlight.js": "^9.11.0", "http-rewrite-middleware": "^0.1.6", "image-diff": "^1.6.3", - "jasmine-core": "^2.6.2", + "jasmine-core": "^2.8.0", "jsonwebtoken": "^7.4.1", - "karma": "^1.7.0", + "karma": "^1.7.1", "karma-browserstack-launcher": "^1.3.0", - "karma-chrome-launcher": "^2.1.1", + "karma-chrome-launcher": "^2.2.0", "karma-coverage": "^1.1.1", "karma-firefox-launcher": "^1.0.1", "karma-jasmine": "^1.1.0", "karma-sauce-launcher": "^1.2.0", "karma-sourcemap-loader": "^0.3.7", - "madge": "^1.6.0", - "magic-string": "^0.21.3", + "madge": "^2.2.0", + "magic-string": "^0.22.4", "minimatch": "^3.0.4", "minimist": "^1.2.0", + "moment": "^2.18.1", "node-sass": "^4.5.3", - "protractor": "^5.1.2", - "request": "^2.81.0", + "protractor": "^5.2.0", + "request": "^2.83.0", "resolve-bin": "^0.4.0", "rollup": "^0.41.6", + "rollup-plugin-alias": "^1.3.1", "rollup-plugin-node-resolve": "^3.0.0", "run-sequence": "^1.2.2", "scss-bundle": "^2.0.1-beta.7", - "selenium-webdriver": "^3.4.0", + "selenium-webdriver": "^3.6.0", "sorcery": "^0.10.0", "stylelint": "^7.12.0", "ts-node": "^3.0.4", - "tsconfig-paths": "^2.2.0", - "tslint": "^5.7.0", + "tsconfig-paths": "^2.3.0", + "tslint": "^5.8.0", "tsutils": "^2.6.0", - "typescript": "~2.2.1", + "typescript": "~2.4.2", "uglify-js": "^2.8.14", "web-animations-js": "^2.2.5" } diff --git a/scripts/ci/sources/tunnel.sh b/scripts/ci/sources/tunnel.sh index adaaa9c18b2c..5256422d5073 100644 --- a/scripts/ci/sources/tunnel.sh +++ b/scripts/ci/sources/tunnel.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # Load the retry-call utility function. -source ./scripts/ci/sources/retry-call.sh +source ./scripts/retry-call.sh # Variable the specifies how often the wait script should be invoked if it fails. WAIT_RETRIES=2 diff --git a/scripts/ci/travis-deploy.sh b/scripts/ci/travis-deploy.sh index 1dad3036eb1f..544e7878c97c 100755 --- a/scripts/ci/travis-deploy.sh +++ b/scripts/ci/travis-deploy.sh @@ -9,6 +9,9 @@ set -e # Go to the project root directory cd $(dirname $0)/../.. +# Load the retry-call utility function. +source scripts/retry-call.sh + # If the current Travis job is triggered by a pull request skip the deployment. # This check is necessary because Travis still tries to run the deploy build-stage for # pull requests. @@ -17,22 +20,33 @@ if [[ "$TRAVIS_PULL_REQUEST" != "false" ]]; then exit 0; fi +# Variable the specifies how often the deploy script should be invoked if it fails. +DEPLOY_RETRIES=1 + echo "" echo "Starting the deployment script. Running mode: ${DEPLOY_MODE}" echo "" -if [[ "${DEPLOY_MODE}" == "build-artifacts" ]]; then - ./scripts/deploy/publish-build-artifacts.sh -fi - -if [[ "${DEPLOY_MODE}" == "docs-content" ]]; then - ./scripts/deploy/publish-docs-content.sh -fi - -if [[ "${DEPLOY_MODE}" == "screenshot-tool" ]]; then - ./scripts/deploy/deploy-screenshot-tool.sh -fi - -if [[ "${DEPLOY_MODE}" == "dashboard" ]]; then - ./scripts/deploy/deploy-dashboard.sh +# Deployment of the screenshot tool or dashboard should happen inside of a Cronjob. +# For example, always deploying the screenshot functions on a per-commit base might cause problems +# with the screenshot tests, because the functions can be non-responsive for a few seconds. +if [[ "${TRAVIS_EVENT_TYPE}" == "cron" ]]; then + if [[ "${DEPLOY_MODE}" == "screenshot-tool" ]]; then + retryCall ${DEPLOY_RETRIES} ./scripts/deploy/deploy-screenshot-tool.sh + elif [[ "${DEPLOY_MODE}" == "dashboard" ]]; then + retryCall ${DEPLOY_RETRIES} ./scripts/deploy/deploy-dashboard.sh + else + echo "Docs content and build artifacts won't be published in Travis cronjobs." + fi + +# Deployment of the build artifacts and docs-content should only happen on a per-commit base. +# The target is to provide build artifacts in the GitHub repository for every commit. +else + if [[ "${DEPLOY_MODE}" == "build-artifacts" ]]; then + retryCall ${DEPLOY_RETRIES} ./scripts/deploy/publish-build-artifacts.sh + elif [[ "${DEPLOY_MODE}" == "docs-content" ]]; then + retryCall ${DEPLOY_RETRIES} ./scripts/deploy/publish-docs-content.sh + else + echo "The dashboard and screenshot-tool will only be deployed in Travis cronjobs." + fi fi diff --git a/scripts/ci/travis-testing.sh b/scripts/ci/travis-testing.sh index e790d41f3cbb..2aca6151f8c3 100755 --- a/scripts/ci/travis-testing.sh +++ b/scripts/ci/travis-testing.sh @@ -25,8 +25,8 @@ else fi # Check if tests can be skipped -if [[ ${fileDiff} =~ ^(.*\.md\s*)*$ ]] && (is_e2e || is_unit); then - echo "Skipping e2e and unit tests since only markdown files changed" +if [[ ${fileDiff} =~ ^(.*\.md\s*)*$ ]]; then + echo "Skipping tests since only markdown files changed." exit 0 fi diff --git a/scripts/closure-compiler/build-devapp-bundle.sh b/scripts/closure-compiler/build-devapp-bundle.sh index 3d3b1607cdf8..1f1bd5363168 100755 --- a/scripts/closure-compiler/build-devapp-bundle.sh +++ b/scripts/closure-compiler/build-devapp-bundle.sh @@ -8,9 +8,10 @@ set -e -o pipefail # Go to the project root directory cd $(dirname $0)/../.. -# Build a release of material and of the CDK package. +# Build a release of material, material-moment-adapter, and cdk packages. $(npm bin)/gulp material:build-release:clean $(npm bin)/gulp cdk:build-release +$(npm bin)/gulp material-moment-adapter:build-release # Build demo-app with ES2015 modules. Closure compiler is then able to parse imports. $(npm bin)/gulp :build:devapp:assets :build:devapp:scss @@ -38,41 +39,46 @@ OPTS=( "--js_module_root=dist/packages" "--js_module_root=dist/releases/material" "--js_module_root=dist/releases/cdk" + "--js_module_root=dist/releases/material-moment-adapter" "--js_module_root=node_modules/@angular/core" "--js_module_root=node_modules/@angular/common" + "--js_module_root=node_modules/@angular/common/http" "--js_module_root=node_modules/@angular/compiler" "--js_module_root=node_modules/@angular/forms" - "--js_module_root=node_modules/@angular/http" "--js_module_root=node_modules/@angular/router" "--js_module_root=node_modules/@angular/platform-browser" "--js_module_root=node_modules/@angular/platform-browser/animations" "--js_module_root=node_modules/@angular/platform-browser-dynamic" "--js_module_root=node_modules/@angular/animations" "--js_module_root=node_modules/@angular/animations/browser" + "--js_module_root=node_modules/moment" # Flags to simplify debugging. "--formatting=PRETTY_PRINT" "--debug" # Include the Material and CDK FESM bundles - dist/releases/material/@angular/material.js - dist/releases/cdk/@angular/cdk.js + dist/releases/material/esm2015/material.js + dist/releases/cdk/esm2015/cdk.js + dist/releases/material-moment-adapter/esm2015/material-moment-adapter.js # Include all Angular FESM bundles. - node_modules/@angular/core/@angular/core.js - node_modules/@angular/common/@angular/common.js - node_modules/@angular/compiler/@angular/compiler.js - node_modules/@angular/forms/@angular/forms.js - node_modules/@angular/http/@angular/http.js - node_modules/@angular/router/@angular/router.js - node_modules/@angular/platform-browser/@angular/platform-browser.js - node_modules/@angular/platform-browser/@angular/platform-browser/animations.js - node_modules/@angular/platform-browser-dynamic/@angular/platform-browser-dynamic.js - node_modules/@angular/animations/@angular/animations.js - node_modules/@angular/animations/@angular/animations/browser.js - - # Include other dependencies like Zone.js and RxJS + node_modules/@angular/core/esm5/index.js + node_modules/@angular/common/esm5/index.js + node_modules/@angular/common/esm5/http.js + node_modules/@angular/compiler/esm5/index.js + node_modules/@angular/forms/esm5/index.js + node_modules/@angular/http/esm5/index.js + node_modules/@angular/router/esm5/index.js + node_modules/@angular/platform-browser/esm5/index.js + node_modules/@angular/platform-browser/esm5/animations/index.js + node_modules/@angular/platform-browser-dynamic/esm5/index.js + node_modules/@angular/animations/esm5/index.js + node_modules/@angular/animations/esm5/browser/index.js + + # Include other dependencies like Zone.js, Moment.js, and RxJS node_modules/zone.js/dist/zone.js + node_modules/moment/moment.js $rxjsSourceFiles # Include all files from the demo-app package. @@ -85,7 +91,7 @@ OPTS=( # Walk through every entry-point of the CDK and add it to closure options. for i in "${cdkEntryPoints[@]}"; do OPTS+=("--js_module_root=dist/releases/cdk/${i}") - OPTS+=("dist/releases/cdk/@angular/cdk/${i}.js") + OPTS+=("dist/releases/cdk/esm2015/${i}.js") done # Write closure flags to a closure flagfile. diff --git a/scripts/deploy/publish-build-artifacts.sh b/scripts/deploy/publish-build-artifacts.sh index c6151f259eb3..3dbbb6570326 100755 --- a/scripts/deploy/publish-build-artifacts.sh +++ b/scripts/deploy/publish-build-artifacts.sh @@ -16,8 +16,8 @@ if [ -z ${MATERIAL2_BUILDS_TOKEN} ]; then fi # Material packages that need to published. -PACKAGES=(cdk material) -REPOSITORIES=(cdk-builds material2-builds) +PACKAGES=(cdk material material-moment-adapter) +REPOSITORIES=(cdk-builds material2-builds material2-moment-adapter-builds) # Command line arguments. COMMAND_ARGS=${*} diff --git a/scripts/deploy/publish-docs-content.sh b/scripts/deploy/publish-docs-content.sh index df470f4ae1f7..64b3c45c099c 100755 --- a/scripts/deploy/publish-docs-content.sh +++ b/scripts/deploy/publish-docs-content.sh @@ -40,7 +40,7 @@ git clone $repoUrl $repoPath --depth 1 rm -rf $repoPath/* # Create folders that will contain docs content files. -mkdir $repoPath/{overview,guides,api,examples,plunker,examples-package} +mkdir $repoPath/{overview,guides,api,examples,stackblitz,examples-package} # Copy api files over to $repoPath/api cp -r $docsPath/api/* $repoPath/api @@ -75,8 +75,8 @@ done # Copy highlighted examples into $repoPath cp -r $examplesSource/* $repoPath/examples -# Copy example plunker assets -cp -r $docsPath/plunker/* $repoPath/plunker +# Copy example stackblitz assets +cp -r $docsPath/stackblitz/* $repoPath/stackblitz # Copies assets over to the docs-content repository. cp LICENSE $repoPath/ diff --git a/scripts/ci/sources/retry-call.sh b/scripts/retry-call.sh old mode 100755 new mode 100644 similarity index 100% rename from scripts/ci/sources/retry-call.sh rename to scripts/retry-call.sh diff --git a/scripts/saucelabs/start-tunnel.sh b/scripts/saucelabs/start-tunnel.sh index 611bfdfc901d..5c24b5facaee 100755 --- a/scripts/saucelabs/start-tunnel.sh +++ b/scripts/saucelabs/start-tunnel.sh @@ -2,7 +2,7 @@ set -e -o pipefail -TUNNEL_FILE="sc-4.4.9-linux.tar.gz" +TUNNEL_FILE="sc-4.4.10-linux.tar.gz" TUNNEL_URL="https://saucelabs.com/downloads/${TUNNEL_FILE}" TUNNEL_DIR="/tmp/saucelabs-connect" diff --git a/src/cdk/BUILD.bazel b/src/cdk/BUILD.bazel new file mode 100644 index 000000000000..c7d068323422 --- /dev/null +++ b/src/cdk/BUILD.bazel @@ -0,0 +1,11 @@ +package(default_visibility=["//visibility:public"]) +load("@build_bazel_rules_typescript//:defs.bzl", "ts_library") + + +ts_library( + name = "cdk", + srcs = glob(["*.ts"], exclude=["**/*.spec.ts"]), + module_name = "@angular/cdk", + deps = [], + tsconfig = ":tsconfig-build.json", +) diff --git a/src/cdk/a11y/BUILD.bazel b/src/cdk/a11y/BUILD.bazel new file mode 100644 index 000000000000..e13a5c02b6aa --- /dev/null +++ b/src/cdk/a11y/BUILD.bazel @@ -0,0 +1,14 @@ +package(default_visibility=["//visibility:public"]) +load("@angular//:index.bzl", "ng_module") + +ng_module( + name = "a11y", + srcs = glob(["**/*.ts"], exclude=["**/*.spec.ts"]), + module_name = "@angular/cdk/a11y", + deps = [ + "//src/cdk/coercion", + "//src/cdk/keycodes", + "//src/cdk/platform", + ], + tsconfig = ":tsconfig-build.json", +) diff --git a/src/lib/core/a11y/_a11y.scss b/src/cdk/a11y/_a11y.scss similarity index 100% rename from src/lib/core/a11y/_a11y.scss rename to src/cdk/a11y/_a11y.scss diff --git a/src/cdk/a11y/a11y-module.ts b/src/cdk/a11y/a11y-module.ts new file mode 100644 index 000000000000..cc2942625942 --- /dev/null +++ b/src/cdk/a11y/a11y-module.ts @@ -0,0 +1,31 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {PlatformModule} from '@angular/cdk/platform'; +import {CommonModule} from '@angular/common'; +import {NgModule} from '@angular/core'; +import {ARIA_DESCRIBER_PROVIDER, AriaDescriber} from './aria-describer'; +import {CdkMonitorFocus, FOCUS_MONITOR_PROVIDER} from './focus-monitor'; +import {CdkTrapFocus, FocusTrapDeprecatedDirective, FocusTrapFactory} from './focus-trap'; +import {InteractivityChecker} from './interactivity-checker'; +import {LIVE_ANNOUNCER_PROVIDER} from './live-announcer'; + +@NgModule({ + imports: [CommonModule, PlatformModule], + declarations: [CdkTrapFocus, FocusTrapDeprecatedDirective, CdkMonitorFocus], + exports: [CdkTrapFocus, FocusTrapDeprecatedDirective, CdkMonitorFocus], + providers: [ + InteractivityChecker, + FocusTrapFactory, + AriaDescriber, + LIVE_ANNOUNCER_PROVIDER, + ARIA_DESCRIBER_PROVIDER, + FOCUS_MONITOR_PROVIDER, + ] +}) +export class A11yModule {} diff --git a/src/cdk/a11y/a11y-prebuilt.scss b/src/cdk/a11y/a11y-prebuilt.scss new file mode 100644 index 000000000000..e30963e8ccf9 --- /dev/null +++ b/src/cdk/a11y/a11y-prebuilt.scss @@ -0,0 +1,3 @@ +@import './a11y'; + +@include cdk-a11y(); diff --git a/src/cdk/a11y/a11y.md b/src/cdk/a11y/a11y.md new file mode 100644 index 000000000000..548326f0bcc7 --- /dev/null +++ b/src/cdk/a11y/a11y.md @@ -0,0 +1,108 @@ +The `a11y` package provides a number of tools to improve accessibility, described below. + +### ListKeyManager +`ListKeyManager` manages the active option in a list of items based on keyboard interaction. +Intended to be used with components that correspond to a `role="menu"` or `role="listbox"` pattern. + +#### Basic usage +Any component that uses a `ListKeyManager` will generally do three things: +* Create a `@ViewChildren` query for the options being managed. +* Initialize the `ListKeyManager`, passing in the options. +* Forward keyboard events from the managed component to the `ListKeyManager`. + +Each option should implement the `ListKeyManagerOption` interface: +```ts +interface ListKeyManagerOption { + disabled?: boolean; + getLabel?(): string; +} +``` + +#### Wrapping +Navigation through options can be made to wrap via the `withWrap` method +```ts +this.keyManager = new FocusKeyManager(...).withWrap(); +``` + +#### Types of key managers +There are two varieties of `ListKeyManager`, `FocusKeyManager` and `ActiveDescendantKeyManager`. + +##### FocusKeyManager +Used when options will directly receive browser focus. Each item managed must implement the +`FocusableOption` interface: +```ts +interface FocusableOption extends ListKeyManagerOption { + focus(): void; +} +``` + +##### ActiveDescendantKeyManager +Used when options will be marked as active via `aria-activedescendant`. +Each item managed must implement the +`Highlightable` interface: +```ts +interface Highlightable extends ListKeyManagerOption { + setActiveStyles(): void; + setInactiveStyles(): void; +} +``` + +Each item must also have an ID bound to the listbox's or menu's `aria-activedescendant`. + + +### FocusTrap +The `cdkTrapFocus` directive traps Tab key focus within an element. This is intended to +be used to create accessible experience for components like +[modal dialogs](https://www.w3.org/TR/wai-aria-practices-1.1/#dialog_modal), where focus must be +constrained. + +This directive is declared in `A11yModule`. + +#### Example +```html +
+ +
+``` + +This directive will not prevent focus from moving out of the trapped region due to mouse +interaction. + +#### Regions +Regions can be declared explicitly with an initial focus element by using +the `cdkFocusRegionStart`, `cdkFocusRegionEnd` and `cdkFocusInitial` DOM attributes. +`cdkFocusInitial` specifies the element that will receive focus upon initialization of the region. +`cdkFocusRegionStart` and `cdkFocusRegionEnd` define the region within which focus will be +trapped. When using the tab key, focus will move through this region and wrap around on either end. + +For example: + +```html +Focus region start +Link +Initially focused +Focus region end +``` + + +### InteractivityChecker +`InteractivityChecker` is used to check the interactivity of an element, capturing disabled, +visible, tabbable, and focusable states for accessibility purposes. See the API docs for more +details. + + +### LiveAnnouncer +`LiveAnnouncer` is used to announce messages for screen-reader users using an `aria-live` region. +See [the W3C's WAI-ARIA](https://www.w3.org/TR/wai-aria/states_and_properties#aria-live) +for more information on aria-live regions. + +#### Example +```ts +@Component({...}) +export class MyComponent { + + constructor(liveAnnouncer: LiveAnnouncer) { + liveAnnouncer.announce("Hey Google"); + } +} +``` diff --git a/src/cdk/a11y/activedescendant-key-manager.ts b/src/cdk/a11y/activedescendant-key-manager.ts index 40f0d40144c9..b02bf6fbbd8c 100644 --- a/src/cdk/a11y/activedescendant-key-manager.ts +++ b/src/cdk/a11y/activedescendant-key-manager.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -14,7 +14,10 @@ import {ListKeyManager, ListKeyManagerOption} from './list-key-manager'; * currently disabled. */ export interface Highlightable extends ListKeyManagerOption { + /** Applies the styles for an active item to this item. */ setActiveStyles(): void; + + /** Applies the styles for an inactive item to this item. */ setInactiveStyles(): void; } @@ -26,15 +29,13 @@ export class ActiveDescendantKeyManager extends ListKeyManager { - if (this.activeItem) { - this.activeItem.setInactiveStyles(); - } - super.setActiveItem(index); - if (this.activeItem) { - this.activeItem.setActiveStyles(); - } - }); + if (this.activeItem) { + this.activeItem.setInactiveStyles(); + } + super.setActiveItem(index); + if (this.activeItem) { + this.activeItem.setActiveStyles(); + } } } diff --git a/src/cdk/a11y/aria-describer.spec.ts b/src/cdk/a11y/aria-describer.spec.ts new file mode 100644 index 000000000000..93164d3a518c --- /dev/null +++ b/src/cdk/a11y/aria-describer.spec.ts @@ -0,0 +1,173 @@ +import {A11yModule, CDK_DESCRIBEDBY_HOST_ATTRIBUTE} from './index'; +import {AriaDescriber, MESSAGES_CONTAINER_ID} from './aria-describer'; +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; +import {Component, ElementRef, ViewChild} from '@angular/core'; + +describe('AriaDescriber', () => { + let ariaDescriber: AriaDescriber; + let component: TestApp; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [A11yModule], + declarations: [TestApp], + providers: [AriaDescriber], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(TestApp); + component = fixture.componentInstance; + ariaDescriber = component.ariaDescriber; + }); + + afterEach(() => { + ariaDescriber.ngOnDestroy(); + }); + + it('should initialize without the message container', () => { + expect(getMessagesContainer()).toBeNull(); + }); + + it('should be able to create a message element', () => { + ariaDescriber.describe(component.element1, 'My Message'); + expectMessages(['My Message']); + }); + + it('should not register empty strings', () => { + ariaDescriber.describe(component.element1, ''); + expect(getMessageElements()).toBe(null); + }); + + it('should de-dupe a message registered multiple times', () => { + ariaDescriber.describe(component.element1, 'My Message'); + ariaDescriber.describe(component.element2, 'My Message'); + ariaDescriber.describe(component.element3, 'My Message'); + expectMessages(['My Message']); + expectMessage(component.element1, 'My Message'); + expectMessage(component.element2, 'My Message'); + expectMessage(component.element3, 'My Message'); + }); + + it('should be able to register multiple messages', () => { + ariaDescriber.describe(component.element1, 'First Message'); + ariaDescriber.describe(component.element2, 'Second Message'); + expectMessages(['First Message', 'Second Message']); + expectMessage(component.element1, 'First Message'); + expectMessage(component.element2, 'Second Message'); + }); + + it('should be able to unregister messages', () => { + ariaDescriber.describe(component.element1, 'My Message'); + expectMessages(['My Message']); + + // Register again to check dedupe + ariaDescriber.describe(component.element2, 'My Message'); + expectMessages(['My Message']); + + // Unregister one message and make sure the message is still present in the container + ariaDescriber.removeDescription(component.element1, 'My Message'); + expect(component.element1.hasAttribute(CDK_DESCRIBEDBY_HOST_ATTRIBUTE)).toBeFalsy(); + expectMessages(['My Message']); + + // Unregister the second message, message container should be gone + ariaDescriber.removeDescription(component.element2, 'My Message'); + expect(component.element2.hasAttribute(CDK_DESCRIBEDBY_HOST_ATTRIBUTE)).toBeFalsy(); + expect(getMessagesContainer()).toBeNull(); + }); + + it('should be able to unregister messages while having others registered', () => { + ariaDescriber.describe(component.element1, 'Persistent Message'); + ariaDescriber.describe(component.element2, 'My Message'); + expectMessages(['Persistent Message', 'My Message']); + + // Register again to check dedupe + ariaDescriber.describe(component.element3, 'My Message'); + expectMessages(['Persistent Message', 'My Message']); + + // Unregister one message and make sure the message is still present in the container + ariaDescriber.removeDescription(component.element2, 'My Message'); + expectMessages(['Persistent Message', 'My Message']); + + // Unregister the second message, message container should be gone + ariaDescriber.removeDescription(component.element3, 'My Message'); + expectMessages(['Persistent Message']); + }); + + it('should be able to append to an existing list of aria describedby', () => { + ariaDescriber.describe(component.element4, 'My Message'); + expectMessages(['My Message']); + expectMessage(component.element4, 'My Message'); + }); + + it('should be able to handle multiple regisitrations of the same message to an element', () => { + ariaDescriber.describe(component.element1, 'My Message'); + ariaDescriber.describe(component.element1, 'My Message'); + expectMessages(['My Message']); + expectMessage(component.element1, 'My Message'); + }); +}); + +function getMessagesContainer() { + return document.querySelector(`#${MESSAGES_CONTAINER_ID}`); +} + +function getMessageElements(): Node[] | null { + const messagesContainer = getMessagesContainer(); + if (!messagesContainer) { return null; } + + return messagesContainer ? Array.prototype.slice.call(messagesContainer.children) : null; +} + +/** Checks that the messages array matches the existing created message elements. */ +function expectMessages(messages: string[]) { + const messageElements = getMessageElements(); + expect(messageElements).toBeDefined(); + + expect(messages.length).toBe(messageElements!.length); + messages.forEach((message, i) => { + expect(messageElements![i].textContent).toBe(message); + }); +} + +/** Checks that an element points to a message element that contains the message. */ +function expectMessage(el: Element, message: string) { + const ariaDescribedBy = el.getAttribute('aria-describedby'); + expect(ariaDescribedBy).toBeDefined(); + + const cdkDescribedBy = el.getAttribute(CDK_DESCRIBEDBY_HOST_ATTRIBUTE); + expect(cdkDescribedBy).toBeDefined(); + + const messages = ariaDescribedBy!.split(' ').map(referenceId => { + const messageElement = document.querySelector(`#${referenceId}`); + return messageElement ? messageElement.textContent : ''; + }); + + expect(messages).toContain(message); +} + +@Component({ + template: ` +
+
+
+
+ `, +}) +class TestApp { + @ViewChild('element1') _element1: ElementRef; + get element1(): Element { return this._element1.nativeElement; } + + @ViewChild('element2') _element2: ElementRef; + get element2(): Element { return this._element2.nativeElement; } + + @ViewChild('element3') _element3: ElementRef; + get element3(): Element { return this._element3.nativeElement; } + + @ViewChild('element4') _element4: ElementRef; + get element4(): Element { return this._element4.nativeElement; } + + + constructor(public ariaDescriber: AriaDescriber) { } +} diff --git a/src/cdk/a11y/aria-describer.ts b/src/cdk/a11y/aria-describer.ts new file mode 100644 index 000000000000..54cc1f7fb72a --- /dev/null +++ b/src/cdk/a11y/aria-describer.ts @@ -0,0 +1,214 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Injectable, Inject, InjectionToken, Optional, SkipSelf} from '@angular/core'; +import {DOCUMENT} from '@angular/common'; +import {addAriaReferencedId, getAriaReferenceIds, removeAriaReferencedId} from './aria-reference'; + +/** + * Interface used to register message elements and keep a count of how many registrations have + * the same message and the reference to the message element used for the `aria-describedby`. + */ +export interface RegisteredMessage { + /** The element containing the message. */ + messageElement: Element; + + /** The number of elements that reference this message element via `aria-describedby`. */ + referenceCount: number; +} + +/** ID used for the body container where all messages are appended. */ +export const MESSAGES_CONTAINER_ID = 'cdk-describedby-message-container'; + +/** ID prefix used for each created message element. */ +export const CDK_DESCRIBEDBY_ID_PREFIX = 'cdk-describedby-message'; + +/** Attribute given to each host element that is described by a message element. */ +export const CDK_DESCRIBEDBY_HOST_ATTRIBUTE = 'cdk-describedby-host'; + +/** Global incremental identifier for each registered message element. */ +let nextId = 0; + +/** Global map of all registered message elements that have been placed into the document. */ +const messageRegistry = new Map(); + +/** Container for all registered messages. */ +let messagesContainer: HTMLElement | null = null; + +/** + * Utility that creates visually hidden elements with a message content. Useful for elements that + * want to use aria-describedby to further describe themselves without adding additional visual + * content. + * @docs-private + */ +@Injectable() +export class AriaDescriber { + private _document: Document; + + constructor(@Inject(DOCUMENT) _document: any) { + this._document = _document; + } + + /** + * Adds to the host element an aria-describedby reference to a hidden element that contains + * the message. If the same message has already been registered, then it will reuse the created + * message element. + */ + describe(hostElement: Element, message: string) { + if (!message.trim()) { + return; + } + + if (!messageRegistry.has(message)) { + this._createMessageElement(message); + } + + if (!this._isElementDescribedByMessage(hostElement, message)) { + this._addMessageReference(hostElement, message); + } + } + + /** Removes the host element's aria-describedby reference to the message element. */ + removeDescription(hostElement: Element, message: string) { + if (!message.trim()) { + return; + } + + if (this._isElementDescribedByMessage(hostElement, message)) { + this._removeMessageReference(hostElement, message); + } + + const registeredMessage = messageRegistry.get(message); + if (registeredMessage && registeredMessage.referenceCount === 0) { + this._deleteMessageElement(message); + } + + if (messagesContainer && messagesContainer.childNodes.length === 0) { + this._deleteMessagesContainer(); + } + } + + /** Unregisters all created message elements and removes the message container. */ + ngOnDestroy() { + const describedElements = + this._document.querySelectorAll(`[${CDK_DESCRIBEDBY_HOST_ATTRIBUTE}]`); + + for (let i = 0; i < describedElements.length; i++) { + this._removeCdkDescribedByReferenceIds(describedElements[i]); + describedElements[i].removeAttribute(CDK_DESCRIBEDBY_HOST_ATTRIBUTE); + } + + if (messagesContainer) { + this._deleteMessagesContainer(); + } + + messageRegistry.clear(); + } + + /** + * Creates a new element in the visually hidden message container element with the message + * as its content and adds it to the message registry. + */ + private _createMessageElement(message: string) { + const messageElement = this._document.createElement('div'); + messageElement.setAttribute('id', `${CDK_DESCRIBEDBY_ID_PREFIX}-${nextId++}`); + messageElement.appendChild(this._document.createTextNode(message)!); + + if (!messagesContainer) { this._createMessagesContainer(); } + messagesContainer!.appendChild(messageElement); + + messageRegistry.set(message, {messageElement, referenceCount: 0}); + } + + /** Deletes the message element from the global messages container. */ + private _deleteMessageElement(message: string) { + const registeredMessage = messageRegistry.get(message); + const messageElement = registeredMessage && registeredMessage.messageElement; + if (messagesContainer && messageElement) { + messagesContainer.removeChild(messageElement); + } + messageRegistry.delete(message); + } + + /** Creates the global container for all aria-describedby messages. */ + private _createMessagesContainer() { + messagesContainer = this._document.createElement('div'); + + messagesContainer.setAttribute('id', MESSAGES_CONTAINER_ID); + messagesContainer.setAttribute('aria-hidden', 'true'); + messagesContainer.style.display = 'none'; + this._document.body.appendChild(messagesContainer); + } + + /** Deletes the global messages container. */ + private _deleteMessagesContainer() { + this._document.body.removeChild(messagesContainer!); + messagesContainer = null; + } + + /** Removes all cdk-describedby messages that are hosted through the element. */ + private _removeCdkDescribedByReferenceIds(element: Element) { + // Remove all aria-describedby reference IDs that are prefixed by CDK_DESCRIBEDBY_ID_PREFIX + const originalReferenceIds = getAriaReferenceIds(element, 'aria-describedby') + .filter(id => id.indexOf(CDK_DESCRIBEDBY_ID_PREFIX) != 0); + element.setAttribute('aria-describedby', originalReferenceIds.join(' ')); + } + + /** + * Adds a message reference to the element using aria-describedby and increments the registered + * message's reference count. + */ + private _addMessageReference(element: Element, message: string) { + const registeredMessage = messageRegistry.get(message)!; + + // Add the aria-describedby reference and set the + // describedby_host attribute to mark the element. + addAriaReferencedId(element, 'aria-describedby', registeredMessage.messageElement.id); + element.setAttribute(CDK_DESCRIBEDBY_HOST_ATTRIBUTE, ''); + + registeredMessage.referenceCount++; + } + + /** + * Removes a message reference from the element using aria-describedby + * and decrements the registered message's reference count. + */ + private _removeMessageReference(element: Element, message: string) { + const registeredMessage = messageRegistry.get(message)!; + registeredMessage.referenceCount--; + + removeAriaReferencedId(element, 'aria-describedby', registeredMessage.messageElement.id); + element.removeAttribute(CDK_DESCRIBEDBY_HOST_ATTRIBUTE); + } + + /** Returns true if the element has been described by the provided message ID. */ + private _isElementDescribedByMessage(element: Element, message: string): boolean { + const referenceIds = getAriaReferenceIds(element, 'aria-describedby'); + const registeredMessage = messageRegistry.get(message); + const messageId = registeredMessage && registeredMessage.messageElement.id; + + return !!messageId && referenceIds.indexOf(messageId) != -1; + } + +} + +/** @docs-private */ +export function ARIA_DESCRIBER_PROVIDER_FACTORY(parentDispatcher: AriaDescriber, _document: any) { + return parentDispatcher || new AriaDescriber(_document); +} + +/** @docs-private */ +export const ARIA_DESCRIBER_PROVIDER = { + // If there is already an AriaDescriber available, use that. Otherwise, provide a new one. + provide: AriaDescriber, + deps: [ + [new Optional(), new SkipSelf(), AriaDescriber], + DOCUMENT as InjectionToken + ], + useFactory: ARIA_DESCRIBER_PROVIDER_FACTORY +}; diff --git a/src/cdk/a11y/aria-reference.spec.ts b/src/cdk/a11y/aria-reference.spec.ts new file mode 100644 index 000000000000..849bafb561e3 --- /dev/null +++ b/src/cdk/a11y/aria-reference.spec.ts @@ -0,0 +1,67 @@ +import {addAriaReferencedId, getAriaReferenceIds, removeAriaReferencedId} from './aria-reference'; + +describe('AriaReference', () => { + let testElement: HTMLElement | null; + + beforeEach(() => { + testElement = document.createElement('div'); + document.body.appendChild(testElement); + }); + + afterEach(() => { + document.body.removeChild(testElement!); + }); + + it('should be able to append/remove aria reference IDs', () => { + addAriaReferencedId(testElement!, 'aria-describedby', 'reference_1'); + expectIds('aria-describedby', ['reference_1']); + + addAriaReferencedId(testElement!, 'aria-describedby', 'reference_2'); + expectIds('aria-describedby', ['reference_1', 'reference_2']); + + removeAriaReferencedId(testElement!, 'aria-describedby', 'reference_1'); + expectIds('aria-describedby', ['reference_2']); + + removeAriaReferencedId(testElement!, 'aria-describedby', 'reference_2'); + expectIds('aria-describedby', []); + }); + + it('should trim whitespace when adding/removing reference IDs', () => { + addAriaReferencedId(testElement!, 'aria-describedby', ' reference_1 '); + addAriaReferencedId(testElement!, 'aria-describedby', ' reference_2 '); + expectIds('aria-describedby', ['reference_1', 'reference_2']); + + removeAriaReferencedId(testElement!, 'aria-describedby', ' reference_1 '); + expectIds('aria-describedby', ['reference_2']); + + removeAriaReferencedId(testElement!, 'aria-describedby', ' reference_2 '); + expectIds('aria-describedby', []); + }); + + it('should ignore empty string', () => { + addAriaReferencedId(testElement!, 'aria-describedby', ' '); + expectIds('aria-describedby', []); + }); + + it('should not add the same reference id if it already exists', () => { + addAriaReferencedId(testElement!, 'aria-describedby', 'reference_1'); + addAriaReferencedId(testElement!, 'aria-describedby', 'reference_1'); + expect(['reference_1']); + }); + + it('should retrieve ids that are deliminated by extra whitespace', () => { + testElement!.setAttribute('aria-describedby', 'reference_1 reference_2'); + expect(getAriaReferenceIds(testElement!, 'aria-describedby')) + .toEqual(['reference_1', 'reference_2']); + }); + + /** + * Expects the equal array from getAriaReferenceIds and a space-deliminated list from + * the actual element attribute. If ids is empty, assumes the element should not have any + * value + */ + function expectIds(attr: string, ids: string[]) { + expect(getAriaReferenceIds(testElement!, attr)).toEqual(ids); + expect(testElement!.getAttribute(attr)).toBe(ids.length ? ids.join(' ') : ''); + } +}); diff --git a/src/cdk/a11y/aria-reference.ts b/src/cdk/a11y/aria-reference.ts new file mode 100644 index 000000000000..45bd71263562 --- /dev/null +++ b/src/cdk/a11y/aria-reference.ts @@ -0,0 +1,42 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +/** IDs are deliminated by an empty space, as per the spec. */ +const ID_DELIMINATOR = ' '; + +/** + * Adds the given ID to the specified ARIA attribute on an element. + * Used for attributes such as aria-labelledby, aria-owns, etc. + */ +export function addAriaReferencedId(el: Element, attr: string, id: string) { + const ids = getAriaReferenceIds(el, attr); + if (ids.some(existingId => existingId.trim() == id.trim())) { return; } + ids.push(id.trim()); + + el.setAttribute(attr, ids.join(ID_DELIMINATOR)); +} + +/** + * Removes the given ID from the specified ARIA attribute on an element. + * Used for attributes such as aria-labelledby, aria-owns, etc. + */ +export function removeAriaReferencedId(el: Element, attr: string, id: string) { + const ids = getAriaReferenceIds(el, attr); + const filteredIds = ids.filter(val => val != id.trim()); + + el.setAttribute(attr, filteredIds.join(ID_DELIMINATOR)); +} + +/** + * Gets the list of IDs referenced by the given ARIA attribute on an element. + * Used for attributes such as aria-labelledby, aria-owns, etc. + */ +export function getAriaReferenceIds(el: Element, attr: string): string[] { + // Get string array of all individual ids (whitespace deliminated) in the attribute value + return (el.getAttribute(attr) || '').match(/\S+/g) || []; +} diff --git a/src/cdk/a11y/fake-mousedown.ts b/src/cdk/a11y/fake-mousedown.ts index 87f6eb92592e..1376faa3ca1b 100644 --- a/src/cdk/a11y/fake-mousedown.ts +++ b/src/cdk/a11y/fake-mousedown.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license diff --git a/src/cdk/a11y/focus-key-manager.md b/src/cdk/a11y/focus-key-manager.md new file mode 100644 index 000000000000..9cbe16f0a7ee --- /dev/null +++ b/src/cdk/a11y/focus-key-manager.md @@ -0,0 +1,2 @@ +### FocusKeyManager +`ListKeyManager` manages the focus of an item based on keyboard interaction. \ No newline at end of file diff --git a/src/cdk/a11y/focus-key-manager.ts b/src/cdk/a11y/focus-key-manager.ts index 8c8f06102a87..154bfcea2dc3 100644 --- a/src/cdk/a11y/focus-key-manager.ts +++ b/src/cdk/a11y/focus-key-manager.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -14,6 +14,7 @@ import {ListKeyManager, ListKeyManagerOption} from './list-key-manager'; * and be able to supply it's label. */ export interface FocusableOption extends ListKeyManagerOption { + /** Focuses the `FocusableOption`. */ focus(): void; } diff --git a/src/lib/core/style/focus-origin-monitor.spec.ts b/src/cdk/a11y/focus-monitor.spec.ts similarity index 91% rename from src/lib/core/style/focus-origin-monitor.spec.ts rename to src/cdk/a11y/focus-monitor.spec.ts index 672d8dedc16f..1b53d56be5d3 100644 --- a/src/lib/core/style/focus-origin-monitor.spec.ts +++ b/src/cdk/a11y/focus-monitor.spec.ts @@ -1,38 +1,36 @@ -import {ComponentFixture, inject, TestBed, fakeAsync, tick} from '@angular/core/testing'; -import {Component, Renderer2} from '@angular/core'; -import {StyleModule} from './index'; -import {By} from '@angular/platform-browser'; -import {TAB} from '../keyboard/keycodes'; -import {FocusOrigin, FocusOriginMonitor, TOUCH_BUFFER_MS} from './focus-origin-monitor'; +import {TAB} from '@angular/cdk/keycodes'; import {dispatchFakeEvent, dispatchKeyboardEvent, dispatchMouseEvent} from '@angular/cdk/testing'; +import {Component} from '@angular/core'; +import {ComponentFixture, fakeAsync, inject, TestBed, tick} from '@angular/core/testing'; +import {By} from '@angular/platform-browser'; +import {FocusMonitor, FocusOrigin, TOUCH_BUFFER_MS} from './focus-monitor'; +import {A11yModule} from './index'; -describe('FocusOriginMonitor', () => { +describe('FocusMonitor', () => { let fixture: ComponentFixture; let buttonElement: HTMLElement; - let buttonRenderer: Renderer2; - let focusOriginMonitor: FocusOriginMonitor; + let focusMonitor: FocusMonitor; let changeHandler: (origin: FocusOrigin) => void; beforeEach(() => { TestBed.configureTestingModule({ - imports: [StyleModule], + imports: [A11yModule], declarations: [ PlainButton, ], }).compileComponents(); }); - beforeEach(inject([FocusOriginMonitor], (fom: FocusOriginMonitor) => { + beforeEach(inject([FocusMonitor], (fm: FocusMonitor) => { fixture = TestBed.createComponent(PlainButton); fixture.detectChanges(); buttonElement = fixture.debugElement.query(By.css('button')).nativeElement; - buttonRenderer = fixture.componentInstance.renderer; - focusOriginMonitor = fom; + focusMonitor = fm; changeHandler = jasmine.createSpy('focus origin change handler'); - focusOriginMonitor.monitor(buttonElement, buttonRenderer, false).subscribe(changeHandler); + focusMonitor.monitor(buttonElement, false).subscribe(changeHandler); patchElementFocus(buttonElement); })); @@ -80,7 +78,7 @@ describe('FocusOriginMonitor', () => { it('should detect focus via touch', fakeAsync(() => { // Simulate focus via touch. - dispatchMouseEvent(buttonElement, 'touchstart'); + dispatchFakeEvent(buttonElement, 'touchstart'); buttonElement.focus(); fixture.detectChanges(); tick(TOUCH_BUFFER_MS); @@ -110,7 +108,7 @@ describe('FocusOriginMonitor', () => { })); it('focusVia keyboard should simulate keyboard focus', fakeAsync(() => { - focusOriginMonitor.focusVia(buttonElement, 'keyboard'); + focusMonitor.focusVia(buttonElement, 'keyboard'); tick(); expect(buttonElement.classList.length) @@ -123,7 +121,7 @@ describe('FocusOriginMonitor', () => { })); it('focusVia mouse should simulate mouse focus', fakeAsync(() => { - focusOriginMonitor.focusVia(buttonElement, 'mouse'); + focusMonitor.focusVia(buttonElement, 'mouse'); fixture.detectChanges(); tick(); @@ -137,7 +135,7 @@ describe('FocusOriginMonitor', () => { })); it('focusVia mouse should simulate mouse focus', fakeAsync(() => { - focusOriginMonitor.focusVia(buttonElement, 'touch'); + focusMonitor.focusVia(buttonElement, 'touch'); fixture.detectChanges(); tick(); @@ -151,7 +149,7 @@ describe('FocusOriginMonitor', () => { })); it('focusVia program should simulate programmatic focus', fakeAsync(() => { - focusOriginMonitor.focusVia(buttonElement, 'program'); + focusMonitor.focusVia(buttonElement, 'program'); fixture.detectChanges(); tick(); @@ -175,7 +173,7 @@ describe('FocusOriginMonitor', () => { // Call `blur` directly because invoking `buttonElement.blur()` does not always trigger the // handler on IE11 on SauceLabs. - focusOriginMonitor._onBlur({} as any, buttonElement); + focusMonitor._onBlur({} as any, buttonElement); fixture.detectChanges(); expect(buttonElement.classList.length) @@ -191,7 +189,7 @@ describe('FocusOriginMonitor', () => { expect(buttonElement.classList.length) .toBe(2, 'button should have exactly 2 focus classes'); - focusOriginMonitor.stopMonitoring(buttonElement); + focusMonitor.stopMonitoring(buttonElement); fixture.detectChanges(); expect(buttonElement.classList.length).toBe(0, 'button should not have any focus classes'); @@ -202,7 +200,7 @@ describe('FocusOriginMonitor', () => { describe('cdkMonitorFocus', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [StyleModule], + imports: [A11yModule], declarations: [ ButtonWithFocusClasses, ComplexComponentWithMonitorElementFocus, @@ -262,7 +260,7 @@ describe('cdkMonitorFocus', () => { it('should detect focus via touch', fakeAsync(() => { // Simulate focus via touch. - dispatchMouseEvent(buttonElement, 'touchstart'); + dispatchFakeEvent(buttonElement, 'touchstart'); buttonElement.focus(); fixture.detectChanges(); tick(TOUCH_BUFFER_MS); @@ -380,9 +378,7 @@ describe('cdkMonitorFocus', () => { @Component({ template: `` }) -class PlainButton { - constructor(public renderer: Renderer2) {} -} +class PlainButton {} @Component({ diff --git a/src/lib/core/style/focus-origin-monitor.ts b/src/cdk/a11y/focus-monitor.ts similarity index 73% rename from src/lib/core/style/focus-origin-monitor.ts rename to src/cdk/a11y/focus-monitor.ts index 418777bec907..ae8cc1f8eb3c 100644 --- a/src/lib/core/style/focus-origin-monitor.ts +++ b/src/cdk/a11y/focus-monitor.ts @@ -1,11 +1,12 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ +import {Platform, supportsPassiveEventListeners} from '@angular/cdk/platform'; import { Directive, ElementRef, @@ -19,10 +20,9 @@ import { SkipSelf, } from '@angular/core'; import {Observable} from 'rxjs/Observable'; +import {of as observableOf} from 'rxjs/observable/of'; import {Subject} from 'rxjs/Subject'; import {Subscription} from 'rxjs/Subscription'; -import {Platform} from '../platform/platform'; -import {of as observableOf} from 'rxjs/observable/of'; // This is the value used by AngularJS Material. Through trial and error (on iPhone 6S) they found @@ -36,18 +36,17 @@ export type FocusOrigin = 'touch' | 'mouse' | 'keyboard' | 'program' | null; type MonitoredElementInfo = { unlisten: Function, checkChildren: boolean, - renderer: Renderer2, subject: Subject }; /** Monitors mouse and keyboard events to determine the cause of focus events. */ @Injectable() -export class FocusOriginMonitor { +export class FocusMonitor { /** The focus origin that the next focus event is a result of. */ private _origin: FocusOrigin = null; - /** The FocusOrigin of the last focus event tracked by the FocusOriginMonitor. */ + /** The FocusOrigin of the last focus event tracked by the FocusMonitor. */ private _lastFocusOrigin: FocusOrigin; /** Whether the window has just been focused. */ @@ -62,22 +61,38 @@ export class FocusOriginMonitor { /** Weak map of elements being monitored to their info. */ private _elementInfo = new WeakMap(); - constructor(private _ngZone: NgZone, private _platform: Platform) { - this._ngZone.runOutsideAngular(() => this._registerDocumentEvents()); - } + /** A map of global objects to lists of current listeners. */ + private _unregisterGlobalListeners = () => {}; + + /** The number of elements currently being monitored. */ + private _monitoredElementCount = 0; + constructor(private _ngZone: NgZone, private _platform: Platform) {} + + /** + * @docs-private + * @deprecated renderer param no longer needed. + */ + monitor(element: HTMLElement, renderer: Renderer2, checkChildren: boolean): + Observable; /** * Monitors focus on an element and applies appropriate CSS classes. * @param element The element to monitor - * @param renderer The renderer to use to apply CSS classes to the element. * @param checkChildren Whether to count the element as focused when its children are focused. * @returns An observable that emits when the focus state of the element changes. * When the element is blurred, null will be emitted. */ + monitor(element: HTMLElement, checkChildren: boolean): Observable; monitor( element: HTMLElement, - renderer: Renderer2, - checkChildren: boolean): Observable { + renderer: Renderer2 | boolean, + checkChildren?: boolean): Observable { + // TODO(mmalerba): clean up after deprecated signature is removed. + if (!(renderer instanceof Renderer2)) { + checkChildren = renderer; + } + checkChildren = !!checkChildren; + // Do nothing if we're not on the browser platform. if (!this._platform.isBrowser) { return observableOf(null); @@ -93,10 +108,10 @@ export class FocusOriginMonitor { let info: MonitoredElementInfo = { unlisten: () => {}, checkChildren: checkChildren, - renderer: renderer, subject: new Subject() }; this._elementInfo.set(element, info); + this._incrementMonitoredElementCount(); // Start listening. We need to listen in capture phase since focus events don't bubble. let focusListener = (event: FocusEvent) => this._onFocus(event, element); @@ -128,6 +143,7 @@ export class FocusOriginMonitor { this._setClasses(element); this._elementInfo.delete(element); + this._decrementMonitoredElementCount(); } } @@ -142,46 +158,69 @@ export class FocusOriginMonitor { } /** Register necessary event listeners on the document and window. */ - private _registerDocumentEvents() { + private _registerGlobalListeners() { // Do nothing if we're not on the browser platform. if (!this._platform.isBrowser) { return; } - // Note: we listen to events in the capture phase so we can detect them even if the user stops - // propagation. - // On keydown record the origin and clear any touch event that may be in progress. - document.addEventListener('keydown', () => { + let documentKeydownListener = () => { this._lastTouchTarget = null; this._setOriginForCurrentEventQueue('keyboard'); - }, true); + }; // On mousedown record the origin only if there is not touch target, since a mousedown can // happen as a result of a touch event. - document.addEventListener('mousedown', () => { + let documentMousedownListener = () => { if (!this._lastTouchTarget) { this._setOriginForCurrentEventQueue('mouse'); } - }, true); + }; // When the touchstart event fires the focus event is not yet in the event queue. This means // we can't rely on the trick used above (setting timeout of 0ms). Instead we wait 650ms to // see if a focus happens. - document.addEventListener('touchstart', (event: Event) => { + let documentTouchstartListener = (event: TouchEvent) => { if (this._touchTimeout != null) { clearTimeout(this._touchTimeout); } this._lastTouchTarget = event.target; this._touchTimeout = setTimeout(() => this._lastTouchTarget = null, TOUCH_BUFFER_MS); - }, true); + }; // Make a note of when the window regains focus, so we can restore the origin info for the // focused element. - window.addEventListener('focus', () => { + let windowFocusListener = () => { this._windowFocused = true; setTimeout(() => this._windowFocused = false, 0); + }; + + // Note: we listen to events in the capture phase so we can detect them even if the user stops + // propagation. + this._ngZone.runOutsideAngular(() => { + document.addEventListener('keydown', documentKeydownListener, true); + document.addEventListener('mousedown', documentMousedownListener, true); + document.addEventListener('touchstart', documentTouchstartListener, + supportsPassiveEventListeners() ? ({passive: true, capture: true} as any) : true); + window.addEventListener('focus', windowFocusListener); }); + + this._unregisterGlobalListeners = () => { + document.removeEventListener('keydown', documentKeydownListener, true); + document.removeEventListener('mousedown', documentMousedownListener, true); + document.removeEventListener('touchstart', documentTouchstartListener, + supportsPassiveEventListeners() ? ({passive: true, capture: true} as any) : true); + window.removeEventListener('focus', windowFocusListener); + }; + } + + private _toggleClass(element: Element, className: string, shouldSet: boolean) { + if (shouldSet) { + element.classList.add(className); + } else { + element.classList.remove(className); + } } /** @@ -193,16 +232,11 @@ export class FocusOriginMonitor { const elementInfo = this._elementInfo.get(element); if (elementInfo) { - const toggleClass = (className: string, shouldSet: boolean) => { - shouldSet ? elementInfo.renderer.addClass(element, className) : - elementInfo.renderer.removeClass(element, className); - }; - - toggleClass('cdk-focused', !!origin); - toggleClass('cdk-touch-focused', origin === 'touch'); - toggleClass('cdk-keyboard-focused', origin === 'keyboard'); - toggleClass('cdk-mouse-focused', origin === 'mouse'); - toggleClass('cdk-program-focused', origin === 'program'); + this._toggleClass(element, 'cdk-focused', !!origin); + this._toggleClass(element, 'cdk-touch-focused', origin === 'touch'); + this._toggleClass(element, 'cdk-keyboard-focused', origin === 'keyboard'); + this._toggleClass(element, 'cdk-mouse-focused', origin === 'mouse'); + this._toggleClass(element, 'cdk-program-focused', origin === 'program'); } } @@ -232,7 +266,7 @@ export class FocusOriginMonitor { // result, this code will still consider it to have been caused by the touch event and will // apply the cdk-touch-focused class rather than the cdk-program-focused class. This is a // relatively small edge-case that can be worked around by using - // focusVia(parentEl, renderer, 'program') to focus the parent element. + // focusVia(parentEl, 'program') to focus the parent element. // // If we decide that we absolutely must handle this case correctly, we can do so by listening // for the first focus event after the touchstart, and then the first blur event after that @@ -301,6 +335,22 @@ export class FocusOriginMonitor { this._setClasses(element); elementInfo.subject.next(null); } + + private _incrementMonitoredElementCount() { + // Register global listeners when first element is monitored. + if (++this._monitoredElementCount == 1) { + this._registerGlobalListeners(); + } + } + + private _decrementMonitoredElementCount() { + // Unregister global listeners when last element is unmonitored. + if (!--this._monitoredElementCount) { + this._unregisterGlobalListeners(); + this._unregisterGlobalListeners = () => {}; + } + } + } @@ -320,30 +370,29 @@ export class CdkMonitorFocus implements OnDestroy { private _monitorSubscription: Subscription; @Output() cdkFocusChange = new EventEmitter(); - constructor(private _elementRef: ElementRef, private _focusOriginMonitor: FocusOriginMonitor, - renderer: Renderer2) { - this._monitorSubscription = this._focusOriginMonitor.monitor( - this._elementRef.nativeElement, renderer, + constructor(private _elementRef: ElementRef, private _focusMonitor: FocusMonitor) { + this._monitorSubscription = this._focusMonitor.monitor( + this._elementRef.nativeElement, this._elementRef.nativeElement.hasAttribute('cdkMonitorSubtreeFocus')) .subscribe(origin => this.cdkFocusChange.emit(origin)); } ngOnDestroy() { - this._focusOriginMonitor.stopMonitoring(this._elementRef.nativeElement); + this._focusMonitor.stopMonitoring(this._elementRef.nativeElement); this._monitorSubscription.unsubscribe(); } } /** @docs-private */ -export function FOCUS_ORIGIN_MONITOR_PROVIDER_FACTORY( - parentDispatcher: FocusOriginMonitor, ngZone: NgZone, platform: Platform) { - return parentDispatcher || new FocusOriginMonitor(ngZone, platform); +export function FOCUS_MONITOR_PROVIDER_FACTORY( + parentDispatcher: FocusMonitor, ngZone: NgZone, platform: Platform) { + return parentDispatcher || new FocusMonitor(ngZone, platform); } /** @docs-private */ -export const FOCUS_ORIGIN_MONITOR_PROVIDER = { - // If there is already a FocusOriginMonitor available, use that. Otherwise, provide a new one. - provide: FocusOriginMonitor, - deps: [[new Optional(), new SkipSelf(), FocusOriginMonitor], NgZone, Platform], - useFactory: FOCUS_ORIGIN_MONITOR_PROVIDER_FACTORY +export const FOCUS_MONITOR_PROVIDER = { + // If there is already a FocusMonitor available, use that. Otherwise, provide a new one. + provide: FocusMonitor, + deps: [[new Optional(), new SkipSelf(), FocusMonitor], NgZone, Platform], + useFactory: FOCUS_MONITOR_PROVIDER_FACTORY }; diff --git a/src/cdk/a11y/focus-trap.spec.ts b/src/cdk/a11y/focus-trap.spec.ts index 28d07e46842c..13cbc9ca1c04 100644 --- a/src/cdk/a11y/focus-trap.spec.ts +++ b/src/cdk/a11y/focus-trap.spec.ts @@ -1,8 +1,8 @@ -import {ComponentFixture, TestBed, async} from '@angular/core/testing'; +import {Platform} from '@angular/cdk/platform'; import {Component, ViewChild} from '@angular/core'; -import {FocusTrapFactory, FocusTrapDirective, FocusTrap} from './focus-trap'; +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; +import {FocusTrap, CdkTrapFocus, FocusTrapFactory} from './focus-trap'; import {InteractivityChecker} from './interactivity-checker'; -import {Platform} from '../platform/platform'; describe('FocusTrap', () => { @@ -10,12 +10,13 @@ describe('FocusTrap', () => { beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [ - FocusTrapDirective, + CdkTrapFocus, FocusTrapWithBindings, SimpleFocusTrap, FocusTrapTargets, FocusTrapWithSvg, FocusTrapWithoutFocusableElements, + FocusTrapWithAutoCapture, ], providers: [InteractivityChecker, Platform, FocusTrapFactory] }); @@ -103,7 +104,7 @@ describe('FocusTrap', () => { expect(anchors.every(current => current.getAttribute('tabindex') === '0')).toBe(true); - fixture.componentInstance.isFocusTrapEnabled = false; + fixture.componentInstance._isFocusTrapEnabled = false; fixture.detectChanges(); expect(anchors.every(current => current.getAttribute('tabindex') === '-1')).toBe(true); @@ -154,6 +155,27 @@ describe('FocusTrap', () => { expect(() => focusTrapInstance.focusLastTabbableElement()).not.toThrow(); }); }); + + describe('with autoCapture', () => { + it('should automatically capture and return focus on init / destroy', async(() => { + const fixture = TestBed.createComponent(FocusTrapWithAutoCapture); + fixture.detectChanges(); + + const buttonOutsideTrappedRegion = fixture.nativeElement.querySelector('button'); + buttonOutsideTrappedRegion.focus(); + expect(document.activeElement).toBe(buttonOutsideTrappedRegion); + + fixture.componentInstance.showTrappedRegion = true; + fixture.detectChanges(); + + fixture.whenStable().then(() => { + expect(document.activeElement.id).toBe('auto-capture-target'); + + fixture.destroy(); + expect(document.activeElement).toBe(buttonOutsideTrappedRegion); + }); + })); + }); }); @@ -166,22 +188,36 @@ describe('FocusTrap', () => { ` }) class SimpleFocusTrap { - @ViewChild(FocusTrapDirective) focusTrapDirective: FocusTrapDirective; + @ViewChild(CdkTrapFocus) focusTrapDirective: CdkTrapFocus; +} + +@Component({ + template: ` + +
+ + +
+ ` +}) +class FocusTrapWithAutoCapture { + @ViewChild(CdkTrapFocus) focusTrapDirective: CdkTrapFocus; + showTrappedRegion = false; } @Component({ template: ` -
+
` }) class FocusTrapWithBindings { - @ViewChild(FocusTrapDirective) focusTrapDirective: FocusTrapDirective; + @ViewChild(CdkTrapFocus) focusTrapDirective: CdkTrapFocus; renderFocusTrap = true; - isFocusTrapEnabled = true; + _isFocusTrapEnabled = true; } @@ -190,16 +226,16 @@ class FocusTrapWithBindings {
- - - + + +
` }) class FocusTrapTargets { - @ViewChild(FocusTrapDirective) focusTrapDirective: FocusTrapDirective; + @ViewChild(CdkTrapFocus) focusTrapDirective: CdkTrapFocus; } @@ -213,7 +249,7 @@ class FocusTrapTargets { ` }) class FocusTrapWithSvg { - @ViewChild(FocusTrapDirective) focusTrapDirective: FocusTrapDirective; + @ViewChild(CdkTrapFocus) focusTrapDirective: CdkTrapFocus; } @Component({ @@ -224,5 +260,5 @@ class FocusTrapWithSvg { ` }) class FocusTrapWithoutFocusableElements { - @ViewChild(FocusTrapDirective) focusTrapDirective: FocusTrapDirective; + @ViewChild(CdkTrapFocus) focusTrapDirective: CdkTrapFocus; } diff --git a/src/cdk/a11y/focus-trap.ts b/src/cdk/a11y/focus-trap.ts index cb09557accba..4bcb4bec0548 100644 --- a/src/cdk/a11y/focus-trap.ts +++ b/src/cdk/a11y/focus-trap.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -14,20 +14,20 @@ import { OnDestroy, AfterContentInit, Injectable, + Inject, } from '@angular/core'; import {coerceBooleanProperty} from '@angular/cdk/coercion'; -import {Platform} from '@angular/cdk/platform'; -import {first} from '@angular/cdk/rxjs'; +import {take} from 'rxjs/operators/take'; import {InteractivityChecker} from './interactivity-checker'; +import {DOCUMENT} from '@angular/common'; /** * Class that allows for trapping focus within a DOM element. * - * NOTE: This class currently uses a very simple (naive) approach to focus trapping. + * This class currently uses a relatively simple approach to focus trapping. * It assumes that the tab order is the same as DOM order, which is not necessarily true. * Things like tabIndex > 0, flex `order`, and shadow roots can cause to two to misalign. - * This will be replaced with a more intelligent solution before the library is considered stable. */ export class FocusTrap { private _startAnchor: HTMLElement | null; @@ -46,9 +46,9 @@ export class FocusTrap { constructor( private _element: HTMLElement, - private _platform: Platform, private _checker: InteractivityChecker, private _ngZone: NgZone, + private _document: Document, deferAnchors = false) { if (!deferAnchors) { @@ -74,11 +74,6 @@ export class FocusTrap { * in the constructor, but can be deferred for cases like directives with `*ngIf`. */ attachAnchors(): void { - // If we're not on the browser, there can be no focus to trap. - if (!this._platform.isBrowser) { - return; - } - if (!this._startAnchor) { this._startAnchor = this._createAnchor(); } @@ -147,12 +142,16 @@ export class FocusTrap { private _getRegionBoundary(bound: 'start' | 'end'): HTMLElement | null { // Contains the deprecated version of selector, for temporary backwards comparability. let markers = this._element.querySelectorAll(`[cdk-focus-region-${bound}], ` + + `[cdkFocusRegion${bound}], ` + `[cdk-focus-${bound}]`) as NodeListOf; for (let i = 0; i < markers.length; i++) { if (markers[i].hasAttribute(`cdk-focus-${bound}`)) { console.warn(`Found use of deprecated attribute 'cdk-focus-${bound}',` + - ` use 'cdk-focus-region-${bound}' instead.`, markers[i]); + ` use 'cdkFocusRegion${bound}' instead.`, markers[i]); + } else if (markers[i].hasAttribute(`cdk-focus-region-${bound}`)) { + console.warn(`Found use of deprecated attribute 'cdk-focus-region-${bound}',` + + ` use 'cdkFocusRegion${bound}' instead.`, markers[i]); } } @@ -165,10 +164,17 @@ export class FocusTrap { /** * Focuses the element that should be focused when the focus trap is initialized. - * @returns Returns whether focus was moved successfuly. + * @returns Whether focus was moved successfuly. */ focusInitialElement(): boolean { - const redirectToElement = this._element.querySelector('[cdk-focus-initial]') as HTMLElement; + // Contains the deprecated version of selector, for temporary backwards comparability. + const redirectToElement = this._element.querySelector(`[cdk-focus-initial], ` + + `[cdkFocusInitial]`) as HTMLElement; + + if (this._element.hasAttribute(`cdk-focus-initial`)) { + console.warn(`Found use of deprecated attribute 'cdk-focus-initial',` + + ` use 'cdkFocusInitial' instead.`, this._element); + } if (redirectToElement) { redirectToElement.focus(); @@ -180,7 +186,7 @@ export class FocusTrap { /** * Focuses the first tabbable element within the focus trap region. - * @returns Returns whether focus was moved successfuly. + * @returns Whether focus was moved successfuly. */ focusFirstTabbableElement(): boolean { const redirectToElement = this._getRegionBoundary('start'); @@ -194,7 +200,7 @@ export class FocusTrap { /** * Focuses the last tabbable element within the focus trap region. - * @returns Returns whether focus was moved successfuly. + * @returns Whether focus was moved successfuly. */ focusLastTabbableElement(): boolean { const redirectToElement = this._getRegionBoundary('end'); @@ -253,7 +259,7 @@ export class FocusTrap { /** Creates an anchor element. */ private _createAnchor(): HTMLElement { - let anchor = document.createElement('div'); + const anchor = this._document.createElement('div'); anchor.tabIndex = this._enabled ? 0 : -1; anchor.classList.add('cdk-visually-hidden'); anchor.classList.add('cdk-focus-trap-anchor'); @@ -265,7 +271,7 @@ export class FocusTrap { if (this._ngZone.isStable) { fn(); } else { - first.call(this._ngZone.onStable).subscribe(fn); + this._ngZone.onStable.asObservable().pipe(take(1)).subscribe(fn); } } } @@ -274,19 +280,33 @@ export class FocusTrap { /** Factory that allows easy instantiation of focus traps. */ @Injectable() export class FocusTrapFactory { + private _document: Document; + constructor( private _checker: InteractivityChecker, - private _platform: Platform, - private _ngZone: NgZone) { } + private _ngZone: NgZone, + @Inject(DOCUMENT) _document: any) { - create(element: HTMLElement, deferAnchors = false): FocusTrap { - return new FocusTrap(element, this._platform, this._checker, this._ngZone, deferAnchors); + this._document = _document; + } + + /** + * Creates a focus-trapped region around the given element. + * @param element The element around which focus will be trapped. + * @param deferCaptureElements Defers the creation of focus-capturing elements to be done + * manually by the user. + * @returns The created focus trap instance. + */ + create(element: HTMLElement, deferCaptureElements: boolean = false): FocusTrap { + return new FocusTrap( + element, this._checker, this._ngZone, this._document, deferCaptureElements); } } /** * Directive for trapping focus within a region. + * @docs-private * @deprecated */ @Directive({ @@ -321,23 +341,55 @@ export class FocusTrapDeprecatedDirective implements OnDestroy, AfterContentInit selector: '[cdkTrapFocus]', exportAs: 'cdkTrapFocus', }) -export class FocusTrapDirective implements OnDestroy, AfterContentInit { +export class CdkTrapFocus implements OnDestroy, AfterContentInit { + private _document: Document; + + /** Underlying FocusTrap instance. */ focusTrap: FocusTrap; + /** Previously focused element to restore focus to upon destroy when using autoCapture. */ + private _previouslyFocusedElement: HTMLElement | null = null; + /** Whether the focus trap is active. */ @Input('cdkTrapFocus') get enabled(): boolean { return this.focusTrap.enabled; } set enabled(value: boolean) { this.focusTrap.enabled = coerceBooleanProperty(value); } - constructor(private _elementRef: ElementRef, private _focusTrapFactory: FocusTrapFactory) { + /** + * Whether the directive should automatially move focus into the trapped region upon + * initialization and return focus to the previous activeElement upon destruction. + */ + @Input('cdkTrapFocusAutoCapture') + get autoCapture(): boolean { return this._autoCapture; } + set autoCapture(value: boolean) { this._autoCapture = coerceBooleanProperty(value); } + private _autoCapture: boolean; + + constructor( + private _elementRef: ElementRef, + private _focusTrapFactory: FocusTrapFactory, + @Inject(DOCUMENT) _document: any) { + + this._document = _document; this.focusTrap = this._focusTrapFactory.create(this._elementRef.nativeElement, true); } ngOnDestroy() { this.focusTrap.destroy(); + + // If we stored a previously focused element when using autoCapture, return focus to that + // element now that the trapped region is being destroyed. + if (this._previouslyFocusedElement) { + this._previouslyFocusedElement.focus(); + this._previouslyFocusedElement = null; + } } ngAfterContentInit() { this.focusTrap.attachAnchors(); + + if (this.autoCapture) { + this._previouslyFocusedElement = this._document.activeElement as HTMLElement; + this.focusTrap.focusInitialElementWhenReady(); + } } } diff --git a/src/cdk/a11y/index.ts b/src/cdk/a11y/index.ts index f93e7c31d564..676ca90f1ffa 100644 --- a/src/cdk/a11y/index.ts +++ b/src/cdk/a11y/index.ts @@ -1,9 +1,9 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -export * from './public_api'; +export * from './public-api'; diff --git a/src/cdk/a11y/interactivity-checker.spec.ts b/src/cdk/a11y/interactivity-checker.spec.ts index 3f28acb8b905..e5fcdc61950d 100644 --- a/src/cdk/a11y/interactivity-checker.spec.ts +++ b/src/cdk/a11y/interactivity-checker.spec.ts @@ -1,5 +1,6 @@ +import {Platform} from '@angular/cdk/platform'; import {InteractivityChecker} from './interactivity-checker'; -import {Platform} from '../platform/platform'; + describe('InteractivityChecker', () => { let testContainerElement: HTMLElement; @@ -20,7 +21,7 @@ describe('InteractivityChecker', () => { describe('isDisabled', () => { it('should return true for disabled elements', () => { - let elements = createElements('input', 'textarea', 'select', 'button', 'md-checkbox'); + let elements = createElements('input', 'textarea', 'select', 'button', 'mat-checkbox'); elements.forEach(el => el.setAttribute('disabled', '')); appendElements(elements); @@ -31,7 +32,7 @@ describe('InteractivityChecker', () => { }); it('should return false for elements without disabled', () => { - let elements = createElements('input', 'textarea', 'select', 'button', 'md-checkbox'); + let elements = createElements('input', 'textarea', 'select', 'button', 'mat-checkbox'); appendElements(elements); elements.forEach(el => { diff --git a/src/cdk/a11y/interactivity-checker.ts b/src/cdk/a11y/interactivity-checker.ts index dac06f66ea4f..cc133d95c0cd 100644 --- a/src/cdk/a11y/interactivity-checker.ts +++ b/src/cdk/a11y/interactivity-checker.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -9,11 +9,10 @@ import {Injectable} from '@angular/core'; import {Platform} from '@angular/cdk/platform'; -/** - * The InteractivityChecker leans heavily on the ally.js accessibility utilities. - * Methods like `isTabbable` are only covering specific edge-cases for the browsers which are - * supported. - */ + +// The InteractivityChecker leans heavily on the ally.js accessibility utilities. +// Methods like `isTabbable` are only covering specific edge-cases for the browsers which are +// supported. /** * Utility for checking the interactivity of an element, such as whether is is focusable or @@ -148,7 +147,8 @@ export class InteractivityChecker { function hasGeometry(element: HTMLElement): boolean { // Use logic from jQuery to check for an invisible element. // See https://github.com/jquery/jquery/blob/master/src/css/hiddenVisibleSelectors.js#L12 - return !!(element.offsetWidth || element.offsetHeight || element.getClientRects().length); + return !!(element.offsetWidth || element.offsetHeight || + (typeof element.getClientRects === 'function' && element.getClientRects().length)); } /** Gets whether an element's */ diff --git a/src/cdk/a11y/list-key-manager.spec.ts b/src/cdk/a11y/list-key-manager.spec.ts index 499b5a628266..507a7cb0d513 100644 --- a/src/cdk/a11y/list-key-manager.spec.ts +++ b/src/cdk/a11y/list-key-manager.spec.ts @@ -1,11 +1,11 @@ +import {DOWN_ARROW, TAB, UP_ARROW} from '@angular/cdk/keycodes'; +import {take} from 'rxjs/operators/take'; import {QueryList} from '@angular/core'; import {fakeAsync, tick} from '@angular/core/testing'; +import {createKeyboardEvent} from '../testing/event-objects'; +import {ActiveDescendantKeyManager} from './activedescendant-key-manager'; import {FocusKeyManager} from './focus-key-manager'; -import {DOWN_ARROW, UP_ARROW, TAB} from '../keycodes/keycodes'; import {ListKeyManager} from './list-key-manager'; -import {ActiveDescendantKeyManager} from './activedescendant-key-manager'; -import {createKeyboardEvent} from '../testing/event-objects'; -import {first} from '../rxjs/index'; class FakeFocusable { @@ -196,7 +196,7 @@ describe('Key managers', () => { it('should emit tabOut when the tab key is pressed', () => { let spy = jasmine.createSpy('tabOut spy'); - first.call(keyManager.tabOut).subscribe(spy); + keyManager.tabOut.pipe(take(1)).subscribe(spy); keyManager.onKeydown(fakeKeyEvents.tab); expect(spy).toHaveBeenCalled(); @@ -242,6 +242,31 @@ describe('Key managers', () => { expect(keyManager.activeItemIndex).toBe(0, 'Expected first item to become active.'); }); + it('should emit an event whenever the active item changes', () => { + const spy = jasmine.createSpy('change spy'); + const subscription = keyManager.change.subscribe(spy); + + keyManager.onKeydown(fakeKeyEvents.downArrow); + expect(spy).toHaveBeenCalledTimes(1); + + keyManager.onKeydown(fakeKeyEvents.upArrow); + expect(spy).toHaveBeenCalledTimes(2); + + subscription.unsubscribe(); + }); + + it('should not emit an event if the item did not change', () => { + const spy = jasmine.createSpy('change spy'); + const subscription = keyManager.change.subscribe(spy); + + keyManager.setActiveItem(2); + keyManager.setActiveItem(2); + + expect(spy).toHaveBeenCalledTimes(1); + + subscription.unsubscribe(); + }); + }); describe('programmatic focus', () => { @@ -481,6 +506,72 @@ describe('Key managers', () => { expect(keyManager.activeItem).toBe(itemList.items[0]); })); + it('should not focus disabled items', fakeAsync(() => { + expect(keyManager.activeItem).toBeFalsy(); + + itemList.items[0].disabled = true; + keyManager.onKeydown(createKeyboardEvent('keydown', 79, undefined, 'o')); // types "o" + tick(debounceInterval); + + expect(keyManager.activeItem).toBeFalsy(); + })); + + it('should start looking for matches after the active item', fakeAsync(() => { + itemList.items = [ + new FakeFocusable('Bilbo'), + new FakeFocusable('Frodo'), + new FakeFocusable('Pippin'), + new FakeFocusable('Boromir'), + new FakeFocusable('Aragorn') + ]; + + keyManager.setActiveItem(1); + keyManager.onKeydown(createKeyboardEvent('keydown', 66, undefined, 'b')); + tick(debounceInterval); + + expect(keyManager.activeItem).toBe(itemList.items[3]); + })); + + it('should wrap back around if there were no matches after the active item', fakeAsync(() => { + itemList.items = [ + new FakeFocusable('Bilbo'), + new FakeFocusable('Frodo'), + new FakeFocusable('Pippin'), + new FakeFocusable('Boromir'), + new FakeFocusable('Aragorn') + ]; + + keyManager.setActiveItem(3); + keyManager.onKeydown(createKeyboardEvent('keydown', 66, undefined, 'b')); + tick(debounceInterval); + + expect(keyManager.activeItem).toBe(itemList.items[0]); + })); + + it('should wrap back around if the last item is active', fakeAsync(() => { + keyManager.setActiveItem(2); + keyManager.onKeydown(createKeyboardEvent('keydown', 79, undefined, 'o')); + tick(debounceInterval); + + expect(keyManager.activeItem).toBe(itemList.items[0]); + })); + + it('should be able to select the first item', fakeAsync(() => { + keyManager.setActiveItem(-1); + keyManager.onKeydown(createKeyboardEvent('keydown', 79, undefined, 'o')); + tick(debounceInterval); + + expect(keyManager.activeItem).toBe(itemList.items[0]); + })); + + it('should not do anything if there is no match', fakeAsync(() => { + keyManager.setActiveItem(1); + keyManager.onKeydown(createKeyboardEvent('keydown', 87, undefined, 'w')); + tick(debounceInterval); + + expect(keyManager.activeItem).toBe(itemList.items[1]); + })); + }); }); @@ -540,13 +631,12 @@ describe('Key managers', () => { describe('ActiveDescendantKeyManager', () => { let keyManager: ActiveDescendantKeyManager; - beforeEach(fakeAsync(() => { + beforeEach(() => { itemList.items = [new FakeHighlightable(), new FakeHighlightable(), new FakeHighlightable()]; keyManager = new ActiveDescendantKeyManager(itemList); // first item is already focused keyManager.setFirstItemActive(); - tick(); spyOn(itemList.items[0], 'setActiveStyles'); spyOn(itemList.items[1], 'setActiveStyles'); @@ -555,44 +645,38 @@ describe('Key managers', () => { spyOn(itemList.items[0], 'setInactiveStyles'); spyOn(itemList.items[1], 'setInactiveStyles'); spyOn(itemList.items[2], 'setInactiveStyles'); - })); + }); - it('should set subsequent items as active with the DOWN arrow', fakeAsync(() => { + it('should set subsequent items as active with the DOWN arrow', () => { keyManager.onKeydown(fakeKeyEvents.downArrow); - tick(); expect(itemList.items[1].setActiveStyles).toHaveBeenCalled(); expect(itemList.items[2].setActiveStyles).not.toHaveBeenCalled(); keyManager.onKeydown(fakeKeyEvents.downArrow); - tick(); expect(itemList.items[2].setActiveStyles).toHaveBeenCalled(); - })); + }); - it('should set previous items as active with the UP arrow', fakeAsync(() => { + it('should set previous items as active with the UP arrow', () => { keyManager.setLastItemActive(); - tick(); - keyManager.onKeydown(fakeKeyEvents.upArrow); - tick(); + expect(itemList.items[1].setActiveStyles).toHaveBeenCalled(); expect(itemList.items[0].setActiveStyles).not.toHaveBeenCalled(); keyManager.onKeydown(fakeKeyEvents.upArrow); - tick(); + expect(itemList.items[0].setActiveStyles).toHaveBeenCalled(); - })); + }); - it('should set inactive styles on previously active items', fakeAsync(() => { + it('should set inactive styles on previously active items', () => { keyManager.onKeydown(fakeKeyEvents.downArrow); - tick(); expect(itemList.items[0].setInactiveStyles).toHaveBeenCalled(); keyManager.onKeydown(fakeKeyEvents.upArrow); - tick(); expect(itemList.items[1].setInactiveStyles).toHaveBeenCalled(); - })); + }); }); diff --git a/src/cdk/a11y/list-key-manager.ts b/src/cdk/a11y/list-key-manager.ts index 47fafcfa1856..4452852261c0 100644 --- a/src/cdk/a11y/list-key-manager.ts +++ b/src/cdk/a11y/list-key-manager.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -10,13 +10,17 @@ import {QueryList} from '@angular/core'; import {Subject} from 'rxjs/Subject'; import {Subscription} from 'rxjs/Subscription'; import {UP_ARROW, DOWN_ARROW, TAB, A, Z, ZERO, NINE} from '@angular/cdk/keycodes'; -import {RxChain, debounceTime, filter, map, doOperator} from '@angular/cdk/rxjs'; +import {debounceTime} from 'rxjs/operators/debounceTime'; +import {filter} from 'rxjs/operators/filter'; +import {map} from 'rxjs/operators/map'; +import {tap} from 'rxjs/operators/tap'; -/** - * This interface is for items that can be passed to a ListKeyManager. - */ +/** This interface is for items that can be passed to a ListKeyManager. */ export interface ListKeyManagerOption { + /** Whether the option is disabled. */ disabled?: boolean; + + /** Gets the label for this option. */ getLabel?(): string; } @@ -29,7 +33,7 @@ export class ListKeyManager { private _activeItem: T; private _wrap = false; private _letterKeyStream = new Subject(); - private _typeaheadSubscription: Subscription; + private _typeaheadSubscription = Subscription.EMPTY; // Buffer for the letters that the user has pressed when the typeahead option is turned on. private _pressedLetters: string[] = []; @@ -42,6 +46,9 @@ export class ListKeyManager { */ tabOut: Subject = new Subject(); + /** Stream that emits whenever the active item of the list manager changes. */ + change = new Subject(); + /** * Turns on wrapping mode, which ensures that the active item will wrap to * the other end of list when there are no more items in the given direction. @@ -55,35 +62,38 @@ export class ListKeyManager { * Turns on typeahead mode which allows users to set the active item by typing. * @param debounceInterval Time to wait after the last keystroke before setting the active item. */ - withTypeAhead(debounceInterval = 200): this { + withTypeAhead(debounceInterval: number = 200): this { if (this._items.length && this._items.some(item => typeof item.getLabel !== 'function')) { throw Error('ListKeyManager items in typeahead mode must implement the `getLabel` method.'); } - if (this._typeaheadSubscription) { - this._typeaheadSubscription.unsubscribe(); - } + this._typeaheadSubscription.unsubscribe(); // Debounce the presses of non-navigational keys, collect the ones that correspond to letters // and convert those letters back into a string. Afterwards find the first item that starts // with that string and select it. - this._typeaheadSubscription = RxChain.from(this._letterKeyStream) - .call(doOperator, keyCode => this._pressedLetters.push(keyCode)) - .call(debounceTime, debounceInterval) - .call(filter, () => this._pressedLetters.length > 0) - .call(map, () => this._pressedLetters.join('')) - .subscribe(inputString => { - const items = this._items.toArray(); - - for (let i = 0; i < items.length; i++) { - if (items[i].getLabel!().toUpperCase().trim().indexOf(inputString) === 0) { - this.setActiveItem(i); - break; - } + this._typeaheadSubscription = this._letterKeyStream.pipe( + tap(keyCode => this._pressedLetters.push(keyCode)), + debounceTime(debounceInterval), + filter(() => this._pressedLetters.length > 0), + map(() => this._pressedLetters.join('')) + ).subscribe(inputString => { + const items = this._items.toArray(); + + // Start at 1 because we want to start searching at the item immediately + // following the current active item. + for (let i = 1; i < items.length + 1; i++) { + const index = (this._activeItemIndex + i) % items.length; + const item = items[index]; + + if (!item.disabled && item.getLabel!().toUpperCase().trim().indexOf(inputString) === 0) { + this.setActiveItem(index); + break; } + } - this._pressedLetters = []; - }); + this._pressedLetters = []; + }); return this; } @@ -93,8 +103,14 @@ export class ListKeyManager { * @param index The index of the item to be set as active. */ setActiveItem(index: number): void { + const previousIndex = this._activeItemIndex; + this._activeItemIndex = index; this._activeItem = this._items.toArray()[index]; + + if (this._activeItemIndex !== previousIndex) { + this.change.next(index); + } } /** diff --git a/src/cdk/a11y/live-announcer.ts b/src/cdk/a11y/live-announcer.ts index d1789e06f88c..7bf205f031e4 100644 --- a/src/cdk/a11y/live-announcer.ts +++ b/src/cdk/a11y/live-announcer.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -14,7 +14,7 @@ import { SkipSelf, OnDestroy, } from '@angular/core'; -import {Platform} from '@angular/cdk/platform'; +import {DOCUMENT} from '@angular/common'; export const LIVE_ANNOUNCER_ELEMENT_TOKEN = new InjectionToken('liveAnnouncerElement'); @@ -28,14 +28,12 @@ export class LiveAnnouncer implements OnDestroy { constructor( @Optional() @Inject(LIVE_ANNOUNCER_ELEMENT_TOKEN) elementToken: any, - platform: Platform) { - // Only do anything if we're on the browser platform. - if (platform.isBrowser) { - // We inject the live element as `any` because the constructor signature cannot reference - // browser globals (HTMLElement) on non-browser environments, since having a class decorator - // causes TypeScript to preserve the constructor signature types. - this._liveElement = elementToken || this._createLiveElement(); - } + @Inject(DOCUMENT) private _document: any) { + + // We inject the live element as `any` because the constructor signature cannot reference + // browser globals (HTMLElement) on non-browser environments, since having a class decorator + // causes TypeScript to preserve the constructor signature types. + this._liveElement = elementToken || this._createLiveElement(); } /** @@ -64,13 +62,13 @@ export class LiveAnnouncer implements OnDestroy { } private _createLiveElement(): Element { - let liveEl = document.createElement('div'); + let liveEl = this._document.createElement('div'); liveEl.classList.add('cdk-visually-hidden'); liveEl.setAttribute('aria-atomic', 'true'); liveEl.setAttribute('aria-live', 'polite'); - document.body.appendChild(liveEl); + this._document.body.appendChild(liveEl); return liveEl; } @@ -79,8 +77,8 @@ export class LiveAnnouncer implements OnDestroy { /** @docs-private */ export function LIVE_ANNOUNCER_PROVIDER_FACTORY( - parentDispatcher: LiveAnnouncer, liveElement: any, platform: Platform) { - return parentDispatcher || new LiveAnnouncer(liveElement, platform); + parentDispatcher: LiveAnnouncer, liveElement: any, _document: any) { + return parentDispatcher || new LiveAnnouncer(liveElement, _document); } /** @docs-private */ @@ -90,7 +88,7 @@ export const LIVE_ANNOUNCER_PROVIDER = { deps: [ [new Optional(), new SkipSelf(), LiveAnnouncer], [new Optional(), new Inject(LIVE_ANNOUNCER_ELEMENT_TOKEN)], - Platform, + DOCUMENT, ], useFactory: LIVE_ANNOUNCER_PROVIDER_FACTORY }; diff --git a/src/cdk/a11y/public-api.ts b/src/cdk/a11y/public-api.ts new file mode 100644 index 000000000000..552cd7a5bfc7 --- /dev/null +++ b/src/cdk/a11y/public-api.ts @@ -0,0 +1,23 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import {CdkTrapFocus} from './focus-trap'; + + +export * from './activedescendant-key-manager'; +export * from './aria-describer'; +export * from './fake-mousedown'; +export * from './focus-key-manager'; +export * from './focus-trap'; +export * from './interactivity-checker'; +export * from './list-key-manager'; +export * from './live-announcer'; +export * from './focus-monitor'; +export * from './a11y-module'; + +/** @deprecated Renamed to CdkTrapFocus. */ +export {CdkTrapFocus as FocusTrapDirective}; diff --git a/src/cdk/a11y/public_api.ts b/src/cdk/a11y/public_api.ts deleted file mode 100644 index 2d327ccc0a20..000000000000 --- a/src/cdk/a11y/public_api.ts +++ /dev/null @@ -1,30 +0,0 @@ -/** - * @license - * Copyright Google Inc. All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {NgModule} from '@angular/core'; -import {FocusTrapDirective, FocusTrapDeprecatedDirective, FocusTrapFactory} from './focus-trap'; -import {LIVE_ANNOUNCER_PROVIDER} from './live-announcer'; -import {InteractivityChecker} from './interactivity-checker'; -import {CommonModule} from '@angular/common'; -import {PlatformModule} from '@angular/cdk/platform'; - -@NgModule({ - imports: [CommonModule, PlatformModule], - declarations: [FocusTrapDirective, FocusTrapDeprecatedDirective], - exports: [FocusTrapDirective, FocusTrapDeprecatedDirective], - providers: [InteractivityChecker, FocusTrapFactory, LIVE_ANNOUNCER_PROVIDER] -}) -export class A11yModule {} - -export * from './live-announcer'; -export * from './fake-mousedown'; -export * from './focus-trap'; -export * from './interactivity-checker'; -export * from './list-key-manager'; -export * from './activedescendant-key-manager'; -export * from './focus-key-manager'; diff --git a/src/cdk/a11y/tsconfig-build.json b/src/cdk/a11y/tsconfig-build.json index ed655bf2a095..3abe5d1758bb 100644 --- a/src/cdk/a11y/tsconfig-build.json +++ b/src/cdk/a11y/tsconfig-build.json @@ -1,7 +1,7 @@ { "extends": "../tsconfig-build", "files": [ - "public_api.ts" + "public-api.ts" ], "angularCompilerOptions": { "annotateForClosureCompiler": true, diff --git a/src/cdk/accordion/BUILD.bazel b/src/cdk/accordion/BUILD.bazel new file mode 100644 index 000000000000..768e16cdd168 --- /dev/null +++ b/src/cdk/accordion/BUILD.bazel @@ -0,0 +1,13 @@ +package(default_visibility=["//visibility:public"]) +load("@angular//:index.bzl", "ng_module") + +ng_module( + name = "accordion", + srcs = glob(["**/*.ts"], exclude=["**/*.spec.ts"]), + module_name = "@angular/cdk/accordion", + deps = [ + "//src/cdk/coercion", + "//src/cdk/collections", + ], + tsconfig = ":tsconfig-build.json", +) diff --git a/src/cdk/accordion/accordion-item.spec.ts b/src/cdk/accordion/accordion-item.spec.ts new file mode 100644 index 000000000000..57604d67cd43 --- /dev/null +++ b/src/cdk/accordion/accordion-item.spec.ts @@ -0,0 +1,155 @@ +import {async, TestBed, ComponentFixture} from '@angular/core/testing'; +import {Component} from '@angular/core'; +import {By} from '@angular/platform-browser'; +import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; +import {CdkAccordionModule, CdkAccordionItem} from './public-api'; + +describe('CdkAccordionItem', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + BrowserAnimationsModule, + CdkAccordionModule + ], + declarations: [ + SingleItem, + ItemGroupWithoutAccordion, + ItemGroupWithAccordion + ], + }); + TestBed.compileComponents(); + })); + + describe('single item', () => { + let fixture: ComponentFixture; + let item: CdkAccordionItem; + + beforeEach(() => { + fixture = TestBed.createComponent(SingleItem); + item = fixture.debugElement + .query(By.directive(CdkAccordionItem)) + .injector.get(CdkAccordionItem); + }); + + it('should toggle its expanded state', () => { + expect(item.expanded).toBeFalsy(); + item.toggle(); + expect(item.expanded).toBeTruthy(); + item.toggle(); + expect(item.expanded).toBeFalsy(); + }); + + it('should set its expanded state to expanded', () => { + item.expanded = false; + item.open(); + expect(item.expanded).toBeTruthy(); + }); + + it('should set its expanded state to closed', () => { + item.expanded = true; + item.close(); + expect(item.expanded).toBeFalsy(); + }); + + it('should emit a closed event', () => { + spyOn(item.closed, 'emit'); + item.close(); + fixture.detectChanges(); + expect(item.closed.emit).toHaveBeenCalledWith(); + }); + + it('should emit an opened event', () => { + spyOn(item.opened, 'emit'); + item.open(); + fixture.detectChanges(); + expect(item.opened.emit).toHaveBeenCalledWith(); + }); + + it('should emit an destroyed event', () => { + spyOn(item.destroyed, 'emit'); + item.ngOnDestroy(); + fixture.detectChanges(); + expect(item.destroyed.emit).toHaveBeenCalledWith(); + }); + }); + + describe('items without accordion', () => { + let fixture: ComponentFixture; + let firstItem: CdkAccordionItem; + let secondItem: CdkAccordionItem; + + beforeEach(() => { + fixture = TestBed.createComponent(ItemGroupWithoutAccordion); + [firstItem, secondItem] = fixture.debugElement + .queryAll(By.directive(CdkAccordionItem)) + .map(el => { + return el.injector.get(CdkAccordionItem) as CdkAccordionItem; + }); + }); + + it('should not change expanded state based on unrelated items', () => { + expect(firstItem.expanded).toBeFalsy(); + expect(secondItem.expanded).toBeFalsy(); + firstItem.open(); + fixture.detectChanges(); + expect(firstItem.expanded).toBeTruthy(); + expect(secondItem.expanded).toBeFalsy(); + secondItem.open(); + fixture.detectChanges(); + expect(firstItem.expanded).toBeTruthy(); + expect(secondItem.expanded).toBeTruthy(); + }); + }); + + + describe('items in accordion', () => { + let fixture: ComponentFixture; + let firstItem: CdkAccordionItem; + let secondItem: CdkAccordionItem; + + beforeEach(() => { + fixture = TestBed.createComponent(ItemGroupWithAccordion); + [firstItem, secondItem] = fixture.debugElement + .queryAll(By.directive(CdkAccordionItem)) + .map(el => { + return el.injector.get(CdkAccordionItem) as CdkAccordionItem; + }); + }); + + it('should change expanded state based on related items', () => { + expect(firstItem.expanded).toBeFalsy(); + expect(secondItem.expanded).toBeFalsy(); + firstItem.open(); + fixture.detectChanges(); + expect(firstItem.expanded).toBeTruthy(); + expect(secondItem.expanded).toBeFalsy(); + secondItem.open(); + fixture.detectChanges(); + expect(firstItem.expanded).toBeFalsy(); + expect(secondItem.expanded).toBeTruthy(); + }); + }); +}); + +@Component({ + template: `` +}) +class SingleItem {} + +@Component({ + template: ` + + + ` +}) +class ItemGroupWithoutAccordion {} + +@Component({ + template: ` + + + + + ` +}) +class ItemGroupWithAccordion {} diff --git a/src/lib/expansion/accordion-item.ts b/src/cdk/accordion/accordion-item.ts similarity index 70% rename from src/lib/expansion/accordion-item.ts rename to src/cdk/accordion/accordion-item.ts index bf4d6d042722..f058b3e5d4bb 100644 --- a/src/lib/expansion/accordion-item.ts +++ b/src/cdk/accordion/accordion-item.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -8,44 +8,44 @@ import { Output, + Directive, EventEmitter, Input, - Injectable, OnDestroy, Optional, ChangeDetectorRef, } from '@angular/core'; -import {UniqueSelectionDispatcher} from '../core'; +import {UniqueSelectionDispatcher} from '@angular/cdk/collections'; import {CdkAccordion} from './accordion'; -import {mixinDisabled, CanDisable} from '../core/common-behaviors/disabled'; +import {coerceBooleanProperty} from '@angular/cdk/coercion'; -/** Used to generate unique ID for each expansion panel. */ +/** Used to generate unique ID for each accordion item. */ let nextId = 0; -// Boilerplate for applying mixins to MdSlider. -/** @docs-private */ -export class AccordionItemBase { } -export const _AccordionItemMixinBase = mixinDisabled(AccordionItemBase); - /** - * An abstract class to be extended and decorated as a component. Sets up all + * An basic directive expected to be extended and decorated as a component. Sets up all * events and attributes needed to be managed by a CdkAccordion parent. */ -@Injectable() -export class AccordionItem extends _AccordionItemMixinBase implements OnDestroy, CanDisable { - /** Event emitted every time the MdAccordionChild is closed. */ +@Directive({ + selector: 'cdk-accordion-item', + exportAs: 'cdkAccordionItem', +}) +export class CdkAccordionItem implements OnDestroy { + /** Event emitted every time the AccordionItem is closed. */ @Output() closed = new EventEmitter(); - /** Event emitted every time the MdAccordionChild is opened. */ + /** Event emitted every time the AccordionItem is opened. */ @Output() opened = new EventEmitter(); - /** Event emitted when the MdAccordionChild is destroyed. */ + /** Event emitted when the AccordionItem is destroyed. */ @Output() destroyed = new EventEmitter(); - /** The unique MdAccordionChild id. */ + /** The unique AccordionItem id. */ readonly id = `cdk-accordion-child-${nextId++}`; - /** Whether the MdAccordionChild is expanded. */ + /** Whether the AccordionItem is expanded. */ @Input() - get expanded(): boolean { return this._expanded; } - set expanded(expanded: boolean) { + get expanded(): any { return this._expanded; } + set expanded(expanded: any) { + expanded = coerceBooleanProperty(expanded); + // Only emit events and update the internal value if the value changes. if (this._expanded !== expanded) { this._expanded = expanded; @@ -68,15 +68,12 @@ export class AccordionItem extends _AccordionItemMixinBase implements OnDestroy, } private _expanded: boolean; - /** Unregister function for _expansionDispatcher **/ + /** Unregister function for _expansionDispatcher. */ private _removeUniqueSelectionListener: () => void = () => {}; constructor(@Optional() public accordion: CdkAccordion, private _changeDetectorRef: ChangeDetectorRef, protected _expansionDispatcher: UniqueSelectionDispatcher) { - - super(); - this._removeUniqueSelectionListener = _expansionDispatcher.listen((id: string, accordionId: string) => { if (this.accordion && !this.accordion.multi && diff --git a/src/cdk/accordion/accordion-module.ts b/src/cdk/accordion/accordion-module.ts new file mode 100644 index 000000000000..7536e3ef4e53 --- /dev/null +++ b/src/cdk/accordion/accordion-module.ts @@ -0,0 +1,19 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {NgModule} from '@angular/core'; +import {UNIQUE_SELECTION_DISPATCHER_PROVIDER} from '@angular/cdk/collections'; +import {CdkAccordion} from './accordion'; +import {CdkAccordionItem} from './accordion-item'; + +@NgModule({ + exports: [CdkAccordion, CdkAccordionItem], + declarations: [CdkAccordion, CdkAccordionItem], + providers: [UNIQUE_SELECTION_DISPATCHER_PROVIDER], +}) +export class CdkAccordionModule {} diff --git a/src/cdk/accordion/accordion.spec.ts b/src/cdk/accordion/accordion.spec.ts new file mode 100644 index 000000000000..4fd65bf86a60 --- /dev/null +++ b/src/cdk/accordion/accordion.spec.ts @@ -0,0 +1,67 @@ +import {async, TestBed} from '@angular/core/testing'; +import {Component, ViewChild} from '@angular/core'; +import {By} from '@angular/platform-browser'; +import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; +import {CdkAccordionModule, CdkAccordionItem} from './public-api'; + +describe('CdkAccordion', () => { + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + BrowserAnimationsModule, + CdkAccordionModule + ], + declarations: [ + SetOfItems + ], + }); + TestBed.compileComponents(); + })); + + it('should ensure only one item is expanded at a time', () => { + const fixture = TestBed.createComponent(SetOfItems); + const [firstPanel, secondPanel] = fixture.debugElement + .queryAll(By.directive(CdkAccordionItem)) + .map(el => { + return el.injector.get(CdkAccordionItem) as CdkAccordionItem; + }); + + firstPanel.open(); + fixture.detectChanges(); + expect(firstPanel.expanded).toBeTruthy(); + expect(secondPanel.expanded).toBeFalsy(); + + secondPanel.open(); + fixture.detectChanges(); + expect(firstPanel.expanded).toBeFalsy(); + expect(secondPanel.expanded).toBeTruthy(); + }); + + it('should allow multiple items to be expanded simultaneously', () => { + const fixture = TestBed.createComponent(SetOfItems); + const [firstPanel, secondPanel] = fixture.debugElement + .queryAll(By.directive(CdkAccordionItem)) + .map(el => { + return el.injector.get(CdkAccordionItem) as CdkAccordionItem; + }); + + fixture.componentInstance.multi = true; + fixture.detectChanges(); + firstPanel.expanded = true; + secondPanel.expanded = true; + fixture.detectChanges(); + expect(firstPanel.expanded).toBeTruthy(); + expect(secondPanel.expanded).toBeTruthy(); + }); +}); + +@Component({template: ` + + + + `}) +class SetOfItems { + @ViewChild('item1') item1; + @ViewChild('item2') item2; + multi: boolean = false; +} diff --git a/src/cdk/accordion/accordion.ts b/src/cdk/accordion/accordion.ts new file mode 100644 index 000000000000..9a6e3d62b20f --- /dev/null +++ b/src/cdk/accordion/accordion.ts @@ -0,0 +1,30 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Directive, Input} from '@angular/core'; +import {coerceBooleanProperty} from '@angular/cdk/coercion'; + +/** Used to generate unique ID for each accordion. */ +let nextId = 0; + +/** + * Directive whose purpose is to manage the expanded state of CdkAccordionItem children. + */ +@Directive({ + selector: 'cdk-accordion, [cdkAccordion]', + exportAs: 'cdkAccordion', +}) +export class CdkAccordion { + /** A readonly id value to use for unique selection coordination. */ + readonly id = `cdk-accordion-${nextId++}`; + + /** Whether the accordion should allow multiple expanded accordion items simulateously. */ + @Input() get multi(): boolean { return this._multi; } + set multi(multi: boolean) { this._multi = coerceBooleanProperty(multi); } + private _multi: boolean = false; +} diff --git a/src/lib/core.ts b/src/cdk/accordion/index.ts similarity index 67% rename from src/lib/core.ts rename to src/cdk/accordion/index.ts index 442e5925d59d..746305603e52 100644 --- a/src/lib/core.ts +++ b/src/cdk/accordion/index.ts @@ -1,9 +1,9 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -export * from './core/index'; + export * from './public-api'; diff --git a/src/cdk/accordion/public-api.ts b/src/cdk/accordion/public-api.ts new file mode 100644 index 000000000000..9464746d6f99 --- /dev/null +++ b/src/cdk/accordion/public-api.ts @@ -0,0 +1,11 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + + export {CdkAccordionItem} from './accordion-item'; +export {CdkAccordion} from './accordion'; +export * from './accordion-module'; diff --git a/src/cdk/accordion/tsconfig-build.json b/src/cdk/accordion/tsconfig-build.json new file mode 100644 index 000000000000..054f98f2f2fe --- /dev/null +++ b/src/cdk/accordion/tsconfig-build.json @@ -0,0 +1,13 @@ +{ + "extends": "../tsconfig-build", + "files": [ + "public-api.ts" + ], + "angularCompilerOptions": { + "annotateForClosureCompiler": true, + "strictMetadataEmit": true, + "flatModuleOutFile": "index.js", + "flatModuleId": "@angular/cdk/accordion", + "skipTemplateCodegen": true + } +} diff --git a/src/cdk/bidi/BUILD.bazel b/src/cdk/bidi/BUILD.bazel new file mode 100644 index 000000000000..521ece864c55 --- /dev/null +++ b/src/cdk/bidi/BUILD.bazel @@ -0,0 +1,10 @@ +package(default_visibility=["//visibility:public"]) +load("@angular//:index.bzl", "ng_module") + +ng_module( + name = "bidi", + srcs = glob(["**/*.ts"], exclude=["**/*.spec.ts"]), + module_name = "@angular/cdk/bidi", + deps = [], + tsconfig = ":tsconfig-build.json", +) diff --git a/src/cdk/bidi/public_api.ts b/src/cdk/bidi/bidi-module.ts similarity index 63% rename from src/cdk/bidi/public_api.ts rename to src/cdk/bidi/bidi-module.ts index d42a66e628a8..84c482f100d7 100644 --- a/src/cdk/bidi/public_api.ts +++ b/src/cdk/bidi/bidi-module.ts @@ -1,24 +1,16 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ import {NgModule} from '@angular/core'; -import {DOCUMENT} from '@angular/platform-browser'; +import {DOCUMENT} from '@angular/common'; import {Dir} from './dir'; import {DIR_DOCUMENT, Directionality} from './directionality'; -export { - Directionality, - DIRECTIONALITY_PROVIDER_FACTORY, - DIRECTIONALITY_PROVIDER, - DIR_DOCUMENT, - Direction, -} from './directionality'; -export {Dir} from './dir'; @NgModule({ exports: [Dir], diff --git a/src/cdk/bidi/bidi.md b/src/cdk/bidi/bidi.md index 5627d9d16bae..dddc26874980 100644 --- a/src/cdk/bidi/bidi.md +++ b/src/cdk/bidi/bidi.md @@ -1,4 +1,7 @@ -### BidiModule +The `bidi` package provides a common system for components to get and respond to change in the +application's LTR/RTL layout direction. + +### Directionality When including the CDK's `BidiModule`, components can inject `Directionality` to get the current text direction (RTL or LTR); @@ -6,16 +9,30 @@ text direction (RTL or LTR); #### Example ```ts @Component({ ... }) -export class MyWidget { +export class MyWidget implements OnDestroy { + + /** Whether the widget is in RTL mode or not. */ private isRtl: boolean; + /** Subscription to the Directionality change EventEmitter. */ + private _dirChangeSubscription = Subscription.EMPTY; + constructor(dir: Directionality) { this.isRtl = dir.value === 'rtl'; - dir.change.subscribe(() => { + _dirChangeSubscription = dir.change.subscribe(() => { this.flipDirection(); }); } + + ngOnDestroy() { + this._dirChangeSubscription.unsubscribe(); + } } ``` +### The `Dir` directive +The `BidiModule` also includes a directive that matches any elements with a `dir` attribute. This +directive has the same API as Directionality and provides itself _as_ `Directionality`. By doing +this, any component that injects `Directionality` will get the closest ancestor layout direction +context. diff --git a/src/cdk/bidi/dir.ts b/src/cdk/bidi/dir.ts index 31bf0c489f1f..cd4afa11570e 100644 --- a/src/cdk/bidi/dir.ts +++ b/src/cdk/bidi/dir.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -18,7 +18,8 @@ import {Direction, Directionality} from './directionality'; /** * Directive to listen for changes of direction of part of the DOM. * - * Would provide itself in case a component looks for the Directionality service + * Provides itself as Directionality such that descendant directives only need to ever inject + * Directionality to get the closest direction. */ @Directive({ selector: '[dir]', @@ -27,7 +28,6 @@ import {Direction, Directionality} from './directionality'; exportAs: 'dir', }) export class Dir implements Directionality { - /** Layout direction of the element. */ _dir: Direction = 'ltr'; /** Whether the `value` has been set to its initial value. */ @@ -38,10 +38,7 @@ export class Dir implements Directionality { /** @docs-private */ @Input('dir') - get dir(): Direction { - return this._dir; - } - + get dir(): Direction { return this._dir; } set dir(v: Direction) { let old = this._dir; this._dir = v; diff --git a/src/cdk/bidi/directionality.ts b/src/cdk/bidi/directionality.ts index 76c6985aa61c..8bb96ed61337 100644 --- a/src/cdk/bidi/directionality.ts +++ b/src/cdk/bidi/directionality.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -10,11 +10,9 @@ import { EventEmitter, Injectable, Optional, - SkipSelf, Inject, InjectionToken, } from '@angular/core'; -import {DOCUMENT} from '@angular/platform-browser'; export type Direction = 'ltr' | 'rtl'; @@ -29,7 +27,7 @@ export type Direction = 'ltr' | 'rtl'; * We also can't re-provide the DOCUMENT token from platform-brower because the unit tests * themselves use things like `querySelector` in test code. */ -export const DIR_DOCUMENT = new InjectionToken('md-dir-doc'); +export const DIR_DOCUMENT = new InjectionToken('cdk-dir-doc'); /** * The directionality (LTR / RTL) context for the application (or a subtree of it). @@ -37,7 +35,10 @@ export const DIR_DOCUMENT = new InjectionToken('md-dir-doc'); */ @Injectable() export class Directionality { + /** The current 'ltr' or 'rtl' value. */ readonly value: Direction = 'ltr'; + + /** Stream that emits whenever the 'ltr' / 'rtl' state changes. */ readonly change = new EventEmitter(); constructor(@Optional() @Inject(DIR_DOCUMENT) _document?: any) { @@ -52,16 +53,3 @@ export class Directionality { } } } - -/** @docs-private */ -export function DIRECTIONALITY_PROVIDER_FACTORY(parentDirectionality, _document) { - return parentDirectionality || new Directionality(_document); -} - -/** @docs-private */ -export const DIRECTIONALITY_PROVIDER = { - // If there is already a Directionality available, use that. Otherwise, provide a new one. - provide: Directionality, - deps: [[new Optional(), new SkipSelf(), Directionality], [new Optional(), DOCUMENT]], - useFactory: DIRECTIONALITY_PROVIDER_FACTORY -}; diff --git a/src/cdk/bidi/index.ts b/src/cdk/bidi/index.ts index f93e7c31d564..676ca90f1ffa 100644 --- a/src/cdk/bidi/index.ts +++ b/src/cdk/bidi/index.ts @@ -1,9 +1,9 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -export * from './public_api'; +export * from './public-api'; diff --git a/src/cdk/bidi/public-api.ts b/src/cdk/bidi/public-api.ts new file mode 100644 index 000000000000..b3da3c0b0b67 --- /dev/null +++ b/src/cdk/bidi/public-api.ts @@ -0,0 +1,12 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +export {Directionality, DIR_DOCUMENT, Direction} from './directionality'; +export {Dir} from './dir'; +export * from './bidi-module'; + diff --git a/src/cdk/bidi/tsconfig-build.json b/src/cdk/bidi/tsconfig-build.json index e21cf03bc8ad..660f94acd255 100644 --- a/src/cdk/bidi/tsconfig-build.json +++ b/src/cdk/bidi/tsconfig-build.json @@ -1,7 +1,7 @@ { "extends": "../tsconfig-build", "files": [ - "public_api.ts" + "public-api.ts" ], "angularCompilerOptions": { "annotateForClosureCompiler": true, diff --git a/src/cdk/coercion/BUILD.bazel b/src/cdk/coercion/BUILD.bazel new file mode 100644 index 000000000000..dea79534da7d --- /dev/null +++ b/src/cdk/coercion/BUILD.bazel @@ -0,0 +1,11 @@ +package(default_visibility=["//visibility:public"]) +load("@build_bazel_rules_typescript//:defs.bzl", "ts_library") + +# Note: this will need to ng_module if any Angular-specific stuff is added to coercion. +ts_library( + name = "coercion", + srcs = glob(["**/*.ts"], exclude=["**/*.spec.ts"]), + module_name = "@angular/cdk/coercion", + deps = [], + tsconfig = ":tsconfig-build.json", +) diff --git a/src/cdk/coercion/array.spec.ts b/src/cdk/coercion/array.spec.ts new file mode 100644 index 000000000000..acc24cdd7b9d --- /dev/null +++ b/src/cdk/coercion/array.spec.ts @@ -0,0 +1,35 @@ +import {coerceArray} from './array'; + +describe('coerceArray', () => { + + it('should wrap a string in an array', () => { + let stringVal = 'just a string'; + expect(coerceArray(stringVal)).toEqual([stringVal]); + }); + + it('should wrap a number in an array', () => { + let numberVal = 42; + expect(coerceArray(numberVal)).toEqual([numberVal]); + }); + + it('should wrap an object in an array', () => { + let objectVal = { something: 'clever' }; + expect(coerceArray(objectVal)).toEqual([objectVal]); + }); + + it('should wrap a null vall in an array', () => { + let nullVal = null; + expect(coerceArray(nullVal)).toEqual([nullVal]); + }); + + it('should wrap an undefined value in an array', () => { + let undefinedVal = undefined; + expect(coerceArray(undefinedVal)).toEqual([undefinedVal]); + }); + + it('should not wrap an array in an array', () => { + let arrayVal = [1, 2, 3]; + expect(coerceArray(arrayVal)).toBe(arrayVal); + }); + +}); diff --git a/src/cdk/coercion/array.ts b/src/cdk/coercion/array.ts new file mode 100644 index 000000000000..6ab9c7eb1d4b --- /dev/null +++ b/src/cdk/coercion/array.ts @@ -0,0 +1,12 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +/** Wraps the provided value in an array, unless the provided value is an array. */ +export function coerceArray(value: T | T[]): T[] { + return Array.isArray(value) ? value : [value]; +} diff --git a/src/cdk/coercion/boolean-property.ts b/src/cdk/coercion/boolean-property.ts index a48fe51187ad..ca95f2a81f8a 100644 --- a/src/cdk/coercion/boolean-property.ts +++ b/src/cdk/coercion/boolean-property.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license diff --git a/src/cdk/coercion/coercion.md b/src/cdk/coercion/coercion.md new file mode 100644 index 000000000000..e050b542f3ad --- /dev/null +++ b/src/cdk/coercion/coercion.md @@ -0,0 +1,3 @@ +### Coercion + +Utility functions for coercing `@Input`s into specific types. diff --git a/src/cdk/coercion/index.ts b/src/cdk/coercion/index.ts index f93e7c31d564..676ca90f1ffa 100644 --- a/src/cdk/coercion/index.ts +++ b/src/cdk/coercion/index.ts @@ -1,9 +1,9 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -export * from './public_api'; +export * from './public-api'; diff --git a/src/cdk/coercion/number-property.ts b/src/cdk/coercion/number-property.ts index eaa782c97c63..38d783974a6b 100644 --- a/src/cdk/coercion/number-property.ts +++ b/src/cdk/coercion/number-property.ts @@ -1,12 +1,14 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ /** Coerces a data-bound value (typically a string) to a number. */ +export function coerceNumberProperty(value: any): number; +export function coerceNumberProperty(value: any, fallback: D): number | D; export function coerceNumberProperty(value: any, fallbackValue = 0) { // parseFloat(value) handles most of the cases we're interested in (it treats null, empty string, // and other non-number values as NaN, where Number just uses 0) but it considers the string diff --git a/src/cdk/coercion/public_api.ts b/src/cdk/coercion/public-api.ts similarity index 76% rename from src/cdk/coercion/public_api.ts rename to src/cdk/coercion/public-api.ts index 53767a7ac274..aa2fd160b1ab 100644 --- a/src/cdk/coercion/public_api.ts +++ b/src/cdk/coercion/public-api.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -8,3 +8,4 @@ export * from './boolean-property'; export * from './number-property'; +export * from './array'; diff --git a/src/cdk/coercion/tsconfig-build.json b/src/cdk/coercion/tsconfig-build.json index 6c423b857928..5c72df273dbd 100644 --- a/src/cdk/coercion/tsconfig-build.json +++ b/src/cdk/coercion/tsconfig-build.json @@ -1,7 +1,7 @@ { "extends": "../tsconfig-build", "files": [ - "public_api.ts" + "public-api.ts" ], "angularCompilerOptions": { "annotateForClosureCompiler": true, diff --git a/src/cdk/collections/BUILD.bazel b/src/cdk/collections/BUILD.bazel new file mode 100644 index 000000000000..8653318e0862 --- /dev/null +++ b/src/cdk/collections/BUILD.bazel @@ -0,0 +1,10 @@ +package(default_visibility=["//visibility:public"]) +load("@angular//:index.bzl", "ng_module") + +ng_module( + name = "collections", + srcs = glob(["**/*.ts"], exclude=["**/*.spec.ts"]), + module_name = "@angular/cdk/collections", + deps = [], + tsconfig = ":tsconfig-build.json", +) diff --git a/src/cdk/collections/collection-viewer.ts b/src/cdk/collections/collection-viewer.ts index 83591723829f..63543da18a45 100644 --- a/src/cdk/collections/collection-viewer.ts +++ b/src/cdk/collections/collection-viewer.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -13,5 +13,9 @@ import {Observable} from 'rxjs/Observable'; * information regarding the view and any changes made. */ export interface CollectionViewer { + /** + * A stream that emits whenever the `CollectionViewer` starts looking at a new portion of the + * data. The `start` index is inclusive, while the `end` is exclusive. + */ viewChange: Observable<{start: number, end: number}>; } diff --git a/src/cdk/collections/collections.md b/src/cdk/collections/collections.md new file mode 100644 index 000000000000..1306b8f8df89 --- /dev/null +++ b/src/cdk/collections/collections.md @@ -0,0 +1,3 @@ +### Collections + +A set of utilities for managing collections. \ No newline at end of file diff --git a/src/cdk/collections/data-source.ts b/src/cdk/collections/data-source.ts index c52c155e04f9..b872d1c49cec 100644 --- a/src/cdk/collections/data-source.ts +++ b/src/cdk/collections/data-source.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license diff --git a/src/cdk/collections/index.ts b/src/cdk/collections/index.ts index f93e7c31d564..676ca90f1ffa 100644 --- a/src/cdk/collections/index.ts +++ b/src/cdk/collections/index.ts @@ -1,9 +1,9 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -export * from './public_api'; +export * from './public-api'; diff --git a/src/cdk/collections/public_api.ts b/src/cdk/collections/public-api.ts similarity index 56% rename from src/cdk/collections/public_api.ts rename to src/cdk/collections/public-api.ts index ed7251e3c14c..f18860e3756c 100644 --- a/src/cdk/collections/public_api.ts +++ b/src/cdk/collections/public-api.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -9,3 +9,8 @@ export * from './collection-viewer'; export * from './data-source'; export * from './selection'; +export { + UniqueSelectionDispatcher, + UniqueSelectionDispatcherListener, + UNIQUE_SELECTION_DISPATCHER_PROVIDER, +} from './unique-selection-dispatcher'; diff --git a/src/cdk/collections/selection.spec.ts b/src/cdk/collections/selection.spec.ts index 146758053499..024192f97713 100644 --- a/src/cdk/collections/selection.spec.ts +++ b/src/cdk/collections/selection.spec.ts @@ -1,4 +1,4 @@ -import {SelectionModel} from './selection'; +import {getMultipleValuesInSingleSelectionError, SelectionModel} from './selection'; describe('SelectionModel', () => { @@ -22,6 +22,10 @@ describe('SelectionModel', () => { expect(model.isSelected(2)).toBe(true); }); + it('should throw an error if multiple values are passed to model', () => { + expect(() => model.select(1, 2)).toThrow(getMultipleValuesInSingleSelectionError()); + }); + it('should only preselect one value', () => { model = new SelectionModel(false, [1, 2]); @@ -36,13 +40,29 @@ describe('SelectionModel', () => { beforeEach(() => model = new SelectionModel(true)); - it('should be able to select multiple options at the same time', () => { + it('should be able to select multiple options', () => { + const onChangeSpy = jasmine.createSpy('onChange spy'); + + model.onChange!.subscribe(onChangeSpy); model.select(1); model.select(2); expect(model.selected.length).toBe(2); expect(model.isSelected(1)).toBe(true); expect(model.isSelected(2)).toBe(true); + expect(onChangeSpy).toHaveBeenCalledTimes(2); + }); + + it('should be able to select multiple options at the same time', () => { + const onChangeSpy = jasmine.createSpy('onChange spy'); + + model.onChange!.subscribe(onChangeSpy); + model.select(1, 2); + + expect(model.selected.length).toBe(2); + expect(model.isSelected(1)).toBe(true); + expect(model.isSelected(2)).toBe(true); + expect(onChangeSpy).toHaveBeenCalledTimes(1); }); it('should be able to preselect multiple options', () => { diff --git a/src/cdk/collections/selection.ts b/src/cdk/collections/selection.ts index b2c199cfc9ef..b03f90672035 100644 --- a/src/cdk/collections/selection.ts +++ b/src/cdk/collections/selection.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -56,16 +56,18 @@ export class SelectionModel { /** * Selects a value or an array of values. */ - select(value: T): void { - this._markSelected(value); + select(...values: T[]): void { + this._verifyValueAssignment(values); + values.forEach(value => this._markSelected(value)); this._emitChangeEvent(); } /** * Deselects a value or an array of values. */ - deselect(value: T): void { - this._unmarkSelected(value); + deselect(...values: T[]): void { + this._verifyValueAssignment(values); + values.forEach(value => this._unmarkSelected(value)); this._emitChangeEvent(); } @@ -162,12 +164,30 @@ export class SelectionModel { this._selection.forEach(value => this._unmarkSelected(value)); } } + + /** + * Verifies the value assignment and throws an error if the specified value array is + * including multiple values while the selection model is not supporting multiple values. + */ + private _verifyValueAssignment(values: T[]) { + if (values.length > 1 && !this._isMulti) { + throw getMultipleValuesInSingleSelectionError(); + } + } } /** - * Describes an event emitted when the value of a MdSelectionModel has changed. + * Describes an event emitted when the value of a MatSelectionModel has changed. * @docs-private */ export class SelectionChange { constructor(public added?: T[], public removed?: T[]) { } } + +/** + * Returns an error that reports that multiple values are passed into a selection model + * with a single value. + */ +export function getMultipleValuesInSingleSelectionError() { + return Error('Cannot pass multiple values into SelectionModel with single-value mode.'); +} diff --git a/src/cdk/collections/tsconfig-build.json b/src/cdk/collections/tsconfig-build.json index 7f637f1343af..0a7c5dd6bb32 100644 --- a/src/cdk/collections/tsconfig-build.json +++ b/src/cdk/collections/tsconfig-build.json @@ -1,7 +1,7 @@ { "extends": "../tsconfig-build", "files": [ - "public_api.ts" + "public-api.ts" ], "angularCompilerOptions": { "annotateForClosureCompiler": true, diff --git a/src/lib/core/coordination/unique-selection-dispatcher.spec.ts b/src/cdk/collections/unique-selection-dispatcher.spec.ts similarity index 100% rename from src/lib/core/coordination/unique-selection-dispatcher.spec.ts rename to src/cdk/collections/unique-selection-dispatcher.spec.ts diff --git a/src/lib/core/coordination/unique-selection-dispatcher.ts b/src/cdk/collections/unique-selection-dispatcher.ts similarity index 97% rename from src/lib/core/coordination/unique-selection-dispatcher.ts rename to src/cdk/collections/unique-selection-dispatcher.ts index 248696b73a9f..67784f326972 100644 --- a/src/lib/core/coordination/unique-selection-dispatcher.ts +++ b/src/cdk/collections/unique-selection-dispatcher.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -39,7 +39,7 @@ export class UniqueSelectionDispatcher { /** * Listen for future changes to item selection. * @return Function used to deregister listener - **/ + */ listen(listener: UniqueSelectionDispatcherListener): () => void { this._listeners.push(listener); return () => { diff --git a/src/cdk/index.ts b/src/cdk/index.ts index f93e7c31d564..676ca90f1ffa 100644 --- a/src/cdk/index.ts +++ b/src/cdk/index.ts @@ -1,9 +1,9 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -export * from './public_api'; +export * from './public-api'; diff --git a/src/cdk/keycodes/BUILD.bazel b/src/cdk/keycodes/BUILD.bazel new file mode 100644 index 000000000000..f4857161e958 --- /dev/null +++ b/src/cdk/keycodes/BUILD.bazel @@ -0,0 +1,11 @@ +package(default_visibility=["//visibility:public"]) +load("@build_bazel_rules_typescript//:defs.bzl", "ts_library") + +# Note: this will need to ng_module if any Angular-specific stuff is added to coercion. +ts_library( + name = "keycodes", + srcs = glob(["**/*.ts"], exclude=["**/*.spec.ts"]), + module_name = "@angular/cdk/keycodes", + deps = [], + tsconfig = ":tsconfig-build.json", +) diff --git a/src/cdk/keycodes/index.ts b/src/cdk/keycodes/index.ts index f93e7c31d564..676ca90f1ffa 100644 --- a/src/cdk/keycodes/index.ts +++ b/src/cdk/keycodes/index.ts @@ -1,9 +1,9 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -export * from './public_api'; +export * from './public-api'; diff --git a/src/cdk/keycodes/keycodes.md b/src/cdk/keycodes/keycodes.md new file mode 100644 index 000000000000..68c4cd393a45 --- /dev/null +++ b/src/cdk/keycodes/keycodes.md @@ -0,0 +1,26 @@ +### KeyCodes + +Commonly used keycode constants. + +#### Example +```ts +import {Directive} from '@angular/core'; +import {UP_ARROW, DOWN_ARROW, LEFT_ARROW, RIGHT_ARROW} from '@angular/cdk/keycodes'; + +@Directive({ + selector: '[count-arrows]' + host: { + (keypress): 'handleKeyPress($event)' + } +}) +export class ArrowCounterDirective { + arrowPressCount = 0; + + handleKeyPress(event: KeyboardEvent) { + if ([UP_ARROW, DOWN_ARROW, LEFT_ARROW, RIGHT_ARROW].includes(event.keyCode)) { + this.arrowPresscount++; + } + } +} +``` + diff --git a/src/cdk/keycodes/keycodes.ts b/src/cdk/keycodes/keycodes.ts index d1ddf1231f03..f8e93d23edbb 100644 --- a/src/cdk/keycodes/keycodes.ts +++ b/src/cdk/keycodes/keycodes.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -24,3 +24,4 @@ export const A = 65; export const Z = 90; export const ZERO = 48; export const NINE = 91; +export const COMMA = 188; diff --git a/src/cdk/keycodes/public_api.ts b/src/cdk/keycodes/public-api.ts similarity index 80% rename from src/cdk/keycodes/public_api.ts rename to src/cdk/keycodes/public-api.ts index e486715ffd5e..35f62cbde084 100644 --- a/src/cdk/keycodes/public_api.ts +++ b/src/cdk/keycodes/public-api.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license diff --git a/src/cdk/keycodes/tsconfig-build.json b/src/cdk/keycodes/tsconfig-build.json index 14ba0c50bf55..487789fef280 100644 --- a/src/cdk/keycodes/tsconfig-build.json +++ b/src/cdk/keycodes/tsconfig-build.json @@ -1,7 +1,7 @@ { "extends": "../tsconfig-build", "files": [ - "public_api.ts" + "public-api.ts" ], "angularCompilerOptions": { "annotateForClosureCompiler": true, diff --git a/src/cdk/layout/BUILD.bazel b/src/cdk/layout/BUILD.bazel new file mode 100644 index 000000000000..2d74d18ad3ac --- /dev/null +++ b/src/cdk/layout/BUILD.bazel @@ -0,0 +1,13 @@ +package(default_visibility=["//visibility:public"]) +load("@angular//:index.bzl", "ng_module") + +ng_module( + name = "layout", + srcs = glob(["**/*.ts"], exclude=["**/*.spec.ts"]), + module_name = "@angular/cdk/layout", + deps = [ + "//src/cdk/coercion", + "//src/cdk/platform", + ], + tsconfig = ":tsconfig-build.json", +) diff --git a/src/cdk/layout/breakpoints-observer.md b/src/cdk/layout/breakpoints-observer.md new file mode 100644 index 000000000000..c5dfc710b58b --- /dev/null +++ b/src/cdk/layout/breakpoints-observer.md @@ -0,0 +1,26 @@ +### BreakpointsModule + +When including the CDK's `LayoutModule`, components can inject `BreakpointsObserver` to request +the matching state of a CSS Media Query. + +A set of breakpoints is provided based on the Material Design +[breakpoint system](https://material.io/guidelines/layout/responsive-ui.html#responsive-ui-breakpoints). + +#### Example +```ts +@Component({ ... }) +export class MyWidget { + isHandset: Observable; + + constructor(bm: BreakpointObserver) { + bm.observe(Handset).subscribe((state: BreakpointState) => { + if (state.matches) { + this.makeEverythingFitOnSmallScreen(); + } else { + this.expandEverythingToFillTheScreen(); + } + }); + } +} +``` + diff --git a/src/cdk/layout/breakpoints-observer.spec.ts b/src/cdk/layout/breakpoints-observer.spec.ts new file mode 100644 index 000000000000..e4298638704a --- /dev/null +++ b/src/cdk/layout/breakpoints-observer.spec.ts @@ -0,0 +1,149 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import {LayoutModule, BreakpointObserver, BreakpointState} from './index'; +import {MediaMatcher} from './media-matcher'; +import {async, TestBed, inject} from '@angular/core/testing'; +import {Injectable} from '@angular/core'; + +describe('BreakpointObserver', () => { + let breakpointManager: BreakpointObserver; + let mediaMatcher: FakeMediaMatcher; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [LayoutModule], + providers: [{provide: MediaMatcher, useClass: FakeMediaMatcher}] + }); + })); + + beforeEach(inject( + [BreakpointObserver, MediaMatcher], + (bm: BreakpointObserver, mm: FakeMediaMatcher) => { + breakpointManager = bm; + mediaMatcher = mm; + })); + + afterEach(() => { + mediaMatcher.clear(); + }); + + it('retrieves the whether a query is currently matched', () => { + let query = 'everything starts as true in the FakeMediaMatcher'; + expect(breakpointManager.isMatched(query)).toBeTruthy(); + }); + + it('reuses the same MediaQueryList for matching queries', () => { + expect(mediaMatcher.queryCount).toBe(0); + breakpointManager.observe('query1'); + expect(mediaMatcher.queryCount).toBe(1); + breakpointManager.observe('query1'); + expect(mediaMatcher.queryCount).toBe(1); + breakpointManager.observe('query2'); + expect(mediaMatcher.queryCount).toBe(2); + breakpointManager.observe('query1'); + expect(mediaMatcher.queryCount).toBe(2); + }); + + it('accepts an array of queries', () => { + let queries = ['1 query', '2 query', 'red query', 'blue query']; + breakpointManager.observe(queries); + expect(mediaMatcher.queryCount).toBe(queries.length); + }); + + it('completes all events when the breakpoint manager is destroyed', () => { + let firstTest = jasmine.createSpy('test1'); + breakpointManager.observe('test1').subscribe(undefined, undefined, firstTest); + let secondTest = jasmine.createSpy('test2'); + breakpointManager.observe('test2').subscribe(undefined, undefined, secondTest); + + expect(firstTest).not.toHaveBeenCalled(); + expect(secondTest).not.toHaveBeenCalled(); + + breakpointManager.ngOnDestroy(); + + expect(firstTest).toHaveBeenCalled(); + expect(secondTest).toHaveBeenCalled(); + }); + + it('emits an event on the observable when values change', () => { + let query = '(width: 999px)'; + let queryMatchState: boolean = false; + breakpointManager.observe(query).subscribe((state: BreakpointState) => { + queryMatchState = state.matches; + }); + + async(() => { + expect(queryMatchState).toBeTruthy(); + mediaMatcher.setMatchesQuery(query, false); + expect(queryMatchState).toBeFalsy(); + }); + }); + + it('emits a true matches state when the query is matched', () => { + let query = '(width: 999px)'; + mediaMatcher.setMatchesQuery(query, true); + expect(breakpointManager.isMatched(query)).toBeTruthy(); + }); + + it('emits a false matches state when the query is not matched', () => { + let query = '(width: 999px)'; + mediaMatcher.setMatchesQuery(query, false); + expect(breakpointManager.isMatched(query)).toBeTruthy(); + }); +}); + +export class FakeMediaQueryList implements MediaQueryList { + /** The callback for change events. */ + addListenerCallback?: (mql: MediaQueryList) => void; + + constructor(public matches, public media) {} + + /** Toggles the matches state and "emits" a change event. */ + setMatches(matches: boolean) { + this.matches = matches; + this.addListenerCallback!(this); + } + + /** Registers the callback method for change events. */ + addListener(callback: (mql: MediaQueryList) => void) { + this.addListenerCallback = callback; + } + + /** Noop, but required for implementing MediaQueryList. */ + removeListener() {} +} + +@Injectable() +export class FakeMediaMatcher { + /** A map of match media queries. */ + private queries: Map = new Map(); + + /** The number of distinct queries created in the media matcher during a test. */ + get queryCount(): number { + return this.queries.size; + } + + /** Fakes the match media response to be controlled in tests. */ + matchMedia(query: string): FakeMediaQueryList { + let mql = new FakeMediaQueryList(true, query); + this.queries.set(query, mql); + return mql; + } + + /** Clears all queries from the map of queries. */ + clear() { + this.queries.clear(); + } + + /** Toggles the matching state of the provided query. */ + setMatchesQuery(query: string, matches: boolean) { + if (this.queries.has(query)) { + this.queries.get(query)!.setMatches(matches); + } + } +} diff --git a/src/cdk/layout/breakpoints-observer.ts b/src/cdk/layout/breakpoints-observer.ts new file mode 100644 index 000000000000..13dd3ac6f008 --- /dev/null +++ b/src/cdk/layout/breakpoints-observer.ts @@ -0,0 +1,104 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import {Injectable, NgZone, OnDestroy} from '@angular/core'; +import {MediaMatcher} from './media-matcher'; +import {Observable} from 'rxjs/Observable'; +import {Subject} from 'rxjs/Subject'; +import {map} from 'rxjs/operators/map'; +import {startWith} from 'rxjs/operators/startWith'; +import {takeUntil} from 'rxjs/operators/takeUntil'; +import {coerceArray} from '@angular/cdk/coercion'; +import {combineLatest} from 'rxjs/observable/combineLatest'; +import {fromEventPattern} from 'rxjs/observable/fromEventPattern'; + +/** The current state of a layout breakpoint. */ +export interface BreakpointState { + /** Whether the breakpoint is currently matching. */ + matches: boolean; +} + +interface Query { + observable: Observable; + mql: MediaQueryList; +} + +/** Utility for checking the matching state of @media queries. */ +@Injectable() +export class BreakpointObserver implements OnDestroy { + /** A map of all media queries currently being listened for. */ + private _queries: Map = new Map(); + /** A subject for all other observables to takeUntil based on. */ + private _destroySubject: Subject<{}> = new Subject(); + + constructor(private mediaMatcher: MediaMatcher, private zone: NgZone) {} + + /** Completes the active subject, signalling to all other observables to complete. */ + ngOnDestroy() { + this._destroySubject.next(); + this._destroySubject.complete(); + } + + /** + * Whether one or more media queries match the current viewport size. + * @param value One or more media queries to check. + * @returns Whether any of the media queries match. + */ + isMatched(value: string | string[]): boolean { + let queries = coerceArray(value); + return queries.some(mediaQuery => this._registerQuery(mediaQuery).mql.matches); + } + + /** + * Gets an observable of results for the given queries that will emit new results for any changes + * in matching of the given queries. + * @returns A stream of matches for the given queries. + */ + observe(value: string | string[]): Observable { + let queries = coerceArray(value); + let observables = queries.map(query => this._registerQuery(query).observable); + + return combineLatest(observables, (a: BreakpointState, b: BreakpointState) => { + return { + matches: !!((a && a.matches) || (b && b.matches)), + }; + }); + } + + /** Registers a specific query to be listened for. */ + private _registerQuery(query: string): Query { + // Only set up a new MediaQueryList if it is not already being listened for. + if (this._queries.has(query)) { + return this._queries.get(query)!; + } + + let mql: MediaQueryList = this.mediaMatcher.matchMedia(query); + // Create callback for match changes and add it is as a listener. + let queryObservable = fromEventPattern( + // Listener callback methods are wrapped to be placed back in ngZone. Callbacks must be placed + // back into the zone because matchMedia is only included in Zone.js by loading the + // webapis-media-query.js file alongside the zone.js file. Additionally, some browsers do not + // have MediaQueryList inherit from EventTarget, which causes inconsistencies in how Zone.js + // patches it. + (listener: MediaQueryListListener) => { + mql.addListener((e: MediaQueryList) => this.zone.run(() => listener(e))); + }, + (listener: MediaQueryListListener) => { + mql.removeListener((e: MediaQueryList) => this.zone.run(() => listener(e))); + }) + .pipe( + takeUntil(this._destroySubject), + startWith(mql), + map((nextMql: MediaQueryList) => ({matches: nextMql.matches})) + ); + + // Add the MediaQueryList to the set of queries. + let output = {observable: queryObservable, mql: mql}; + this._queries.set(query, output); + return output; + } +} diff --git a/src/cdk/layout/breakpoints.ts b/src/cdk/layout/breakpoints.ts new file mode 100644 index 000000000000..19e3f22edf1b --- /dev/null +++ b/src/cdk/layout/breakpoints.ts @@ -0,0 +1,25 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +// PascalCase is being used as Breakpoints is used like an enum. +// tslint:disable-next-line:variable-name +export const Breakpoints = { + Handset: '(max-width: 599px) and (orientation: portrait), ' + + '(max-width: 959px) and (orientation: landscape)', + Tablet: '(min-width: 600px) and (max-width: 839px) and (orientation: portrait), ' + + '(min-width: 960px) and (max-width: 1279px) and (orientation: landscape)', + Web: '(min-width: 840px) and (orientation: portrait), ' + + '(min-width: 1280px) and (orientation: landscape)', + + HandsetPortrait: '(max-width: 599px) and (orientation: portrait)', + TabletPortrait: '(min-width: 600px) and (max-width: 839px) and (orientation: portrait)', + WebPortrait: '(min-width: 840px) and (orientation: portrait)', + + HandsetLandscape: '(max-width: 959px) and (orientation: landscape)', + TabletLandscape: '(min-width: 960px) and (max-width: 1279px) and (orientation: landscape)', + WebLandscape: '(min-width: 1280px) and (orientation: landscape)', +}; diff --git a/src/cdk/layout/index.ts b/src/cdk/layout/index.ts new file mode 100644 index 000000000000..ca845d8223a8 --- /dev/null +++ b/src/cdk/layout/index.ts @@ -0,0 +1,8 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +export * from './public-api'; diff --git a/src/cdk/layout/layout.md b/src/cdk/layout/layout.md new file mode 100644 index 000000000000..7168d3d1ef37 --- /dev/null +++ b/src/cdk/layout/layout.md @@ -0,0 +1,77 @@ +The `layout` package provides utilities to build responsive UIs that react to screen-size changes. + +### BreakpointObserver + +`BreakpointObserver` is a utility for evaluating media queries and reacting to their changing. + +#### Evaluate against the current viewport +The `isMatched` method is used to evaluate one or more media queries against the current viewport +size. +```ts +const isSmallScreen = breakpointObserver.isMatched('(max-width: 599px)'); +``` + +#### React to changes to the viewport +The `observe` method is used to get an observable stream that will emit whenever one of the given +media queries would have a different result. +```ts +const layoutChanges = breakpointObserver.observe([ + '(orientation: portrait)', + '(orientation: landscape)', +]); + +layoutChanges.subscribe(result => { + updateMyLayoutForOrientationChange(); +}); +``` + +#### Default breakpoints +A set of default media queries are available corresponding to breakpoints for different device +types. + +```ts +import {BreakpointObserver, Breakpoints} from '@angular/cdk/layout'; + +@Component({...}) +class MyComponent { + constructor(breakpointObserver: BreakpointObserver) { + breakpointObserver.observe([ + Breakpoints.HandsetLandscape, + Breakpoints.HandsetPortrait + ]).subscribe(result => { + if (result.matches) { + this.activateHandsetLayout(); + } + }); + } +} +``` + +The built-in breakpoints based on [Google's Material Design +specification](https://material.io/guidelines/layout/responsive-ui.html#responsive-ui-breakpoints). +The available values are: +* Handset +* Tablet +* Web +* HandsetPortrait +* TabletPortrait +* WebPortrait +* HandsetLandscape +* TabletLandscape +* WebLandscape + + +### MediaMatcher +`MediaMatcher` is a lower-level utility that wraps the native `matchMedia`. This service normalizes +browser differences and serves as a convenient API that can be replaces with a fake in unit tests. +The `matchMedia` method can be used to get a native +[`MediaQueryList`](https://developer.mozilla.org/en-US/docs/Web/API/MediaQueryList). + +```ts +@Component({...}) +class MyComponent { + constructor(mediaMatcher: MediaMatcher) { + const mediaQueryList = mediaMatcher.matchMedia('(min-width: 1px)'); + } +} +``` diff --git a/src/cdk/layout/media-matcher.md b/src/cdk/layout/media-matcher.md new file mode 100644 index 000000000000..6e071b5bd226 --- /dev/null +++ b/src/cdk/layout/media-matcher.md @@ -0,0 +1,17 @@ +### MediaMatcher + +When including the CDK's `LayoutModule`, components can inject `MediaMatcher` to access the +matchMedia method, if available on the platform. + +#### Example +```ts +@Component({ ... }) +export class MyWidget { + constructor(mm: MediaMatcher) { + mm.matchMedia('(orientation: landscape)').matches ? + this.setLandscapeMode() : + this.setPortraitMode(); + } +} +``` + diff --git a/src/cdk/layout/media-matcher.spec.ts b/src/cdk/layout/media-matcher.spec.ts new file mode 100644 index 000000000000..f355b3533667 --- /dev/null +++ b/src/cdk/layout/media-matcher.spec.ts @@ -0,0 +1,47 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import {LayoutModule, BreakpointObserver} from './index'; +import {MediaMatcher} from './media-matcher'; +import {async, TestBed, inject} from '@angular/core/testing'; +import {Platform} from '@angular/cdk/platform'; + +describe('MediaMatcher', () => { + let breakpointManager: BreakpointObserver; + let mediaMatcher: MediaMatcher; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [LayoutModule] + }); + })); + + beforeEach(inject( + [BreakpointObserver, MediaMatcher], + (bm: BreakpointObserver, mm: MediaMatcher) => { + breakpointManager = bm; + mediaMatcher = mm; + })); + + it('correctly returns a MediaQueryList to check for matches', () => { + expect(mediaMatcher.matchMedia('(min-width: 1px)').matches).toBeTruthy(); + expect(mediaMatcher.matchMedia('(max-width: 1px)').matches).toBeFalsy(); + }); + + it('adds css rules for provided queries when the platform is webkit, otherwise adds nothing.', + inject([Platform], (platform: Platform) => { + let randomWidth = Math.random(); + expect(document.head.textContent).not.toContain(randomWidth); + mediaMatcher.matchMedia(`(width: ${randomWidth})`); + + if (platform.WEBKIT) { + expect(document.head.textContent).toContain(randomWidth); + } else { + expect(document.head.textContent).not.toContain(randomWidth); + } + })); +}); diff --git a/src/cdk/layout/media-matcher.ts b/src/cdk/layout/media-matcher.ts new file mode 100644 index 000000000000..2079aa1004f8 --- /dev/null +++ b/src/cdk/layout/media-matcher.ts @@ -0,0 +1,77 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import {Injectable} from '@angular/core'; +import {Platform} from '@angular/cdk/platform'; + +/** + * Global registry for all dynamically-created, injected style tags. + */ +const styleElementForWebkitCompatibility: Map = new Map(); + +/** A utility for calling matchMedia queries. */ +@Injectable() +export class MediaMatcher { + /** The internal matchMedia method to return back a MediaQueryList like object. */ + private _matchMedia: (query: string) => MediaQueryList; + + constructor(private platform: Platform) { + this._matchMedia = this.platform.isBrowser ? + // matchMedia is bound to the window scope intentionally as it is an illegal invocation to + // call it from a different scope. + window.matchMedia.bind(window) : + noopMatchMedia; + } + + /** + * Evaluates the given media query and returns the native MediaQueryList from which results + * can be retrieved. + * Confirms the layout engine will trigger for the selector query provided and returns the + * MediaQueryList for the query provided. + */ + matchMedia(query: string): MediaQueryList { + if (this.platform.WEBKIT) { + createEmptyStyleRule(query); + } + return this._matchMedia(query); + } +} + +/** + * For Webkit engines that only trigger the MediaQueryListListener when there is at least one CSS + * selector for the respective media query. + */ +function createEmptyStyleRule(query: string) { + if (!styleElementForWebkitCompatibility.has(query)) { + try { + const style = document.createElement('style'); + + style.setAttribute('type', 'text/css'); + if (!style.sheet) { + const cssText = `@media ${query} {.fx-query-test{ }}`; + style.appendChild(document.createTextNode(cssText)); + } + + document.getElementsByTagName('head')[0].appendChild(style); + + // Store in private global registry + styleElementForWebkitCompatibility.set(query, style); + } catch (e) { + console.error(e); + } + } +} + +/** No-op matchMedia replacement for non-browser platforms. */ +function noopMatchMedia(query: string): MediaQueryList { + return { + matches: query === 'all' || query === '', + media: query, + addListener: () => {}, + removeListener: () => {} + }; +} diff --git a/src/cdk/layout/public-api.ts b/src/cdk/layout/public-api.ts new file mode 100644 index 000000000000..a6f7fa6b5b0a --- /dev/null +++ b/src/cdk/layout/public-api.ts @@ -0,0 +1,21 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import {NgModule} from '@angular/core'; +import {PlatformModule} from '@angular/cdk/platform'; +import {BreakpointObserver} from './breakpoints-observer'; +import {MediaMatcher} from './media-matcher'; + +@NgModule({ + providers: [BreakpointObserver, MediaMatcher], + imports: [PlatformModule], +}) +export class LayoutModule {} + +export {BreakpointObserver, BreakpointState} from './breakpoints-observer'; +export {Breakpoints} from './breakpoints'; +export {MediaMatcher} from './media-matcher'; diff --git a/src/cdk/rxjs/tsconfig-build.json b/src/cdk/layout/tsconfig-build.json similarity index 78% rename from src/cdk/rxjs/tsconfig-build.json rename to src/cdk/layout/tsconfig-build.json index 6ee37efb86c8..83c719f82566 100644 --- a/src/cdk/rxjs/tsconfig-build.json +++ b/src/cdk/layout/tsconfig-build.json @@ -1,13 +1,13 @@ { "extends": "../tsconfig-build", "files": [ - "public_api.ts" + "public-api.ts" ], "angularCompilerOptions": { "annotateForClosureCompiler": true, "strictMetadataEmit": true, "flatModuleOutFile": "index.js", - "flatModuleId": "@angular/cdk/rxjs", + "flatModuleId": "@angular/cdk/layout", "skipTemplateCodegen": true } } diff --git a/src/cdk/observers/BUILD.bazel b/src/cdk/observers/BUILD.bazel new file mode 100644 index 000000000000..ff3e7cbb28a6 --- /dev/null +++ b/src/cdk/observers/BUILD.bazel @@ -0,0 +1,10 @@ +package(default_visibility=["//visibility:public"]) +load("@angular//:index.bzl", "ng_module") + +ng_module( + name = "observers", + srcs = glob(["**/*.ts"], exclude=["**/*.spec.ts"]), + module_name = "@angular/cdk/observers", + deps = [], + tsconfig = ":tsconfig-build.json", +) diff --git a/src/cdk/observers/index.ts b/src/cdk/observers/index.ts index f93e7c31d564..676ca90f1ffa 100644 --- a/src/cdk/observers/index.ts +++ b/src/cdk/observers/index.ts @@ -1,9 +1,9 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -export * from './public_api'; +export * from './public-api'; diff --git a/src/cdk/observers/observe-content.spec.ts b/src/cdk/observers/observe-content.spec.ts index 4f05369ea9c3..7cb175245d80 100644 --- a/src/cdk/observers/observe-content.spec.ts +++ b/src/cdk/observers/observe-content.spec.ts @@ -1,6 +1,6 @@ import {Component} from '@angular/core'; import {async, TestBed, ComponentFixture, fakeAsync, tick} from '@angular/core/testing'; -import {ObserversModule, MdMutationObserverFactory} from './observe-content'; +import {ObserversModule, MutationObserverFactory} from './observe-content'; // TODO(elad): `ProxyZone` doesn't seem to capture the events raised by // `MutationObserver` and needs to be investigated @@ -61,7 +61,7 @@ describe('Observe content', () => { imports: [ObserversModule], declarations: [ComponentWithDebouncedListener], providers: [{ - provide: MdMutationObserverFactory, + provide: MutationObserverFactory, useValue: { create: function(callback: Function) { callbacks.push(callback); diff --git a/src/cdk/observers/observe-content.ts b/src/cdk/observers/observe-content.ts index 04871646290c..3ba74c12269a 100644 --- a/src/cdk/observers/observe-content.ts +++ b/src/cdk/observers/observe-content.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -19,15 +19,15 @@ import { NgZone, } from '@angular/core'; import {Subject} from 'rxjs/Subject'; -import {RxChain, debounceTime} from '@angular/cdk/rxjs'; +import {debounceTime} from 'rxjs/operators/debounceTime'; /** * Factory that creates a new MutationObserver and allows us to stub it out in unit tests. * @docs-private */ @Injectable() -export class MdMutationObserverFactory { - create(callback): MutationObserver | null { +export class MutationObserverFactory { + create(callback: MutationCallback): MutationObserver | null { return typeof MutationObserver === 'undefined' ? null : new MutationObserver(callback); } } @@ -37,9 +37,10 @@ export class MdMutationObserverFactory { * its associated element has changed. */ @Directive({ - selector: '[cdkObserveContent]' + selector: '[cdkObserveContent]', + exportAs: 'cdkObserveContent', }) -export class ObserveContent implements AfterContentInit, OnDestroy { +export class CdkObserveContent implements AfterContentInit, OnDestroy { private _observer: MutationObserver | null; /** Event emitted for each change in the element's content. */ @@ -52,16 +53,15 @@ export class ObserveContent implements AfterContentInit, OnDestroy { @Input() debounce: number; constructor( - private _mutationObserverFactory: MdMutationObserverFactory, + private _mutationObserverFactory: MutationObserverFactory, private _elementRef: ElementRef, private _ngZone: NgZone) { } ngAfterContentInit() { if (this.debounce > 0) { this._ngZone.runOutsideAngular(() => { - RxChain.from(this._debouncer) - .call(debounceTime, this.debounce) - .subscribe((mutations: MutationRecord[]) => this.event.emit(mutations)); + this._debouncer.pipe(debounceTime(this.debounce)) + .subscribe((mutations: MutationRecord[]) => this.event.emit(mutations)); }); } else { this._debouncer.subscribe(mutations => this.event.emit(mutations)); @@ -75,9 +75,9 @@ export class ObserveContent implements AfterContentInit, OnDestroy { if (this._observer) { this._observer.observe(this._elementRef.nativeElement, { - characterData: true, - childList: true, - subtree: true + 'characterData': true, + 'childList': true, + 'subtree': true }); } } @@ -93,8 +93,8 @@ export class ObserveContent implements AfterContentInit, OnDestroy { @NgModule({ - exports: [ObserveContent], - declarations: [ObserveContent], - providers: [MdMutationObserverFactory] + exports: [CdkObserveContent], + declarations: [CdkObserveContent], + providers: [MutationObserverFactory] }) export class ObserversModule {} diff --git a/src/cdk/observers/observers.md b/src/cdk/observers/observers.md new file mode 100644 index 000000000000..4b622846169a --- /dev/null +++ b/src/cdk/observers/observers.md @@ -0,0 +1,14 @@ +The `observers` package provides convenience directives built on top of native web platform +observers, such as MutationObserver. + + +### cdkObserveContent + +A directive for observing when the content of the host element changes. An event is emitted when a +mutation to the content is observed. + +```html +
+ +
+``` diff --git a/src/cdk/observers/public-api.ts b/src/cdk/observers/public-api.ts new file mode 100644 index 000000000000..03579f42eb88 --- /dev/null +++ b/src/cdk/observers/public-api.ts @@ -0,0 +1,12 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +export * from './observe-content'; + +/** @deprecated Use CdkObserveContent */ +export {CdkObserveContent as ObserveContent} from './observe-content'; diff --git a/src/cdk/observers/tsconfig-build.json b/src/cdk/observers/tsconfig-build.json index ba4de8f00ad6..4f4d795d4f4e 100644 --- a/src/cdk/observers/tsconfig-build.json +++ b/src/cdk/observers/tsconfig-build.json @@ -1,7 +1,7 @@ { "extends": "../tsconfig-build", "files": [ - "public_api.ts" + "public-api.ts" ], "angularCompilerOptions": { "annotateForClosureCompiler": true, diff --git a/src/cdk/overlay/BUILD.bazel b/src/cdk/overlay/BUILD.bazel new file mode 100644 index 000000000000..03f7d8383a16 --- /dev/null +++ b/src/cdk/overlay/BUILD.bazel @@ -0,0 +1,17 @@ +package(default_visibility=["//visibility:public"]) +load("@angular//:index.bzl", "ng_module") + +ng_module( + name = "overlay", + srcs = glob(["**/*.ts"], exclude=["**/*.spec.ts"]), + module_name = "@angular/cdk/overlay", + deps = [ + "//src/cdk/bidi", + "//src/cdk/coercion", + "//src/cdk/keycodes", + "//src/cdk/platform", + "//src/cdk/portal", + "//src/cdk/scrolling", + ], + tsconfig = ":tsconfig-build.json", +) diff --git a/src/cdk/overlay/fullscreen-overlay-container.ts b/src/cdk/overlay/fullscreen-overlay-container.ts index 08f1db6b3f4e..aa7c16d33f18 100644 --- a/src/cdk/overlay/fullscreen-overlay-container.ts +++ b/src/cdk/overlay/fullscreen-overlay-container.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -10,13 +10,11 @@ import {Injectable} from '@angular/core'; import {OverlayContainer} from './overlay-container'; /** - * The FullscreenOverlayContainer is the alternative to OverlayContainer - * that supports correct displaying of overlay elements in Fullscreen mode + * Alternative to OverlayContainer that supports correct displaying of overlay elements in + * Fullscreen mode * https://developer.mozilla.org/en-US/docs/Web/API/Element/requestFullScreen - * It should be provided in the root component that way: - * providers: [ - * {provide: OverlayContainer, useClass: FullscreenOverlayContainer} - * ], + * + * Should be provided in the root component. */ @Injectable() export class FullscreenOverlayContainer extends OverlayContainer { diff --git a/src/cdk/overlay/index.ts b/src/cdk/overlay/index.ts index f93e7c31d564..676ca90f1ffa 100644 --- a/src/cdk/overlay/index.ts +++ b/src/cdk/overlay/index.ts @@ -1,9 +1,9 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -export * from './public_api'; +export * from './public-api'; diff --git a/src/cdk/overlay/keyboard/overlay-keyboard-dispatcher.spec.ts b/src/cdk/overlay/keyboard/overlay-keyboard-dispatcher.spec.ts new file mode 100644 index 000000000000..93fa2a99ec0f --- /dev/null +++ b/src/cdk/overlay/keyboard/overlay-keyboard-dispatcher.spec.ts @@ -0,0 +1,151 @@ +import {TestBed, inject} from '@angular/core/testing'; +import {dispatchKeyboardEvent} from '@angular/cdk/testing'; +import {ESCAPE} from '@angular/cdk/keycodes'; +import {Component, NgModule} from '@angular/core'; +import {Overlay} from '../overlay'; +import {OverlayModule} from '../index'; +import {OverlayKeyboardDispatcher} from './overlay-keyboard-dispatcher'; +import {ComponentPortal} from '@angular/cdk/portal'; + + +describe('OverlayKeyboardDispatcher', () => { + let keyboardDispatcher: OverlayKeyboardDispatcher; + let overlay: Overlay; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [OverlayModule, TestComponentModule], + }); + }); + + beforeEach(inject([OverlayKeyboardDispatcher, Overlay], + (kbd: OverlayKeyboardDispatcher, o: Overlay) => { + keyboardDispatcher = kbd; + overlay = o; + })); + + it('should track overlays in order as they are attached and detached', () => { + const overlayOne = overlay.create(); + const overlayTwo = overlay.create(); + + // Attach overlays + keyboardDispatcher.add(overlayOne); + keyboardDispatcher.add(overlayTwo); + + expect(keyboardDispatcher._attachedOverlays.length) + .toBe(2, 'Expected both overlays to be tracked.'); + expect(keyboardDispatcher._attachedOverlays[0]).toBe(overlayOne, 'Expected one to be first.'); + expect(keyboardDispatcher._attachedOverlays[1]).toBe(overlayTwo, 'Expected two to be last.'); + + // Detach first one and re-attach it + keyboardDispatcher.remove(overlayOne); + keyboardDispatcher.add(overlayOne); + + expect(keyboardDispatcher._attachedOverlays[0]) + .toBe(overlayTwo, 'Expected two to now be first.'); + expect(keyboardDispatcher._attachedOverlays[1]) + .toBe(overlayOne, 'Expected one to now be last.'); + }); + + it('should dispatch body keyboard events to the most recently attached overlay', () => { + const overlayOne = overlay.create(); + const overlayTwo = overlay.create(); + const overlayOneSpy = jasmine.createSpy('overlayOne keyboard event spy'); + const overlayTwoSpy = jasmine.createSpy('overlayOne keyboard event spy'); + + overlayOne.keydownEvents().subscribe(overlayOneSpy); + overlayTwo.keydownEvents().subscribe(overlayTwoSpy); + + // Attach overlays + keyboardDispatcher.add(overlayOne); + keyboardDispatcher.add(overlayTwo); + + dispatchKeyboardEvent(document.body, 'keydown', ESCAPE); + + // Most recent overlay should receive event + expect(overlayOneSpy).not.toHaveBeenCalled(); + expect(overlayTwoSpy).toHaveBeenCalled(); + }); + + it('should dispatch targeted keyboard events to the overlay containing that target', () => { + const overlayOne = overlay.create(); + const overlayTwo = overlay.create(); + const overlayOneSpy = jasmine.createSpy('overlayOne keyboard event spy'); + const overlayTwoSpy = jasmine.createSpy('overlayOne keyboard event spy'); + + overlayOne.keydownEvents().subscribe(overlayOneSpy); + overlayTwo.keydownEvents().subscribe(overlayTwoSpy); + + // Attach overlays + keyboardDispatcher.add(overlayOne); + keyboardDispatcher.add(overlayTwo); + + const overlayOnePane = overlayOne.overlayElement; + + dispatchKeyboardEvent(document.body, 'keydown', ESCAPE, overlayOnePane); + + // Targeted overlay should receive event + expect(overlayOneSpy).toHaveBeenCalled(); + expect(overlayTwoSpy).not.toHaveBeenCalled(); + }); + + it('should complete the keydown stream on dispose', () => { + const overlayRef = overlay.create(); + const completeSpy = jasmine.createSpy('keydown complete spy'); + + overlayRef.keydownEvents().subscribe(undefined, undefined, completeSpy); + + overlayRef.dispose(); + + expect(completeSpy).toHaveBeenCalled(); + }); + + it('should stop emitting events to detached overlays', () => { + const instance = overlay.create(); + const spy = jasmine.createSpy('keyboard event spy'); + + instance.attach(new ComponentPortal(TestComponent)); + instance.keydownEvents().subscribe(spy); + + dispatchKeyboardEvent(document.body, 'keydown', ESCAPE, instance.overlayElement); + expect(spy).toHaveBeenCalledTimes(1); + + instance.detach(); + dispatchKeyboardEvent(document.body, 'keydown', ESCAPE, instance.overlayElement); + + expect(spy).toHaveBeenCalledTimes(1); + }); + + it('should stop emitting events to disposed overlays', () => { + const instance = overlay.create(); + const spy = jasmine.createSpy('keyboard event spy'); + + instance.attach(new ComponentPortal(TestComponent)); + instance.keydownEvents().subscribe(spy); + + dispatchKeyboardEvent(document.body, 'keydown', ESCAPE, instance.overlayElement); + expect(spy).toHaveBeenCalledTimes(1); + + instance.dispose(); + dispatchKeyboardEvent(document.body, 'keydown', ESCAPE, instance.overlayElement); + + expect(spy).toHaveBeenCalledTimes(1); + }); + +}); + + +@Component({ + template: 'Hello' +}) +class TestComponent { } + + +// Create a real (non-test) NgModule as a workaround for +// https://github.com/angular/angular/issues/10760 +@NgModule({ + exports: [TestComponent], + declarations: [TestComponent], + entryComponents: [TestComponent], +}) +class TestComponentModule { } diff --git a/src/cdk/overlay/keyboard/overlay-keyboard-dispatcher.ts b/src/cdk/overlay/keyboard/overlay-keyboard-dispatcher.ts new file mode 100644 index 000000000000..d8597a46c6f2 --- /dev/null +++ b/src/cdk/overlay/keyboard/overlay-keyboard-dispatcher.ts @@ -0,0 +1,115 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Injectable, Inject, InjectionToken, Optional, SkipSelf, OnDestroy} from '@angular/core'; +import {OverlayRef} from '../overlay-ref'; +import {Subscription} from 'rxjs/Subscription'; +import {filter} from 'rxjs/operators/filter'; +import {fromEvent} from 'rxjs/observable/fromEvent'; +import {DOCUMENT} from '@angular/common'; + +/** + * Service for dispatching keyboard events that land on the body to appropriate overlay ref, + * if any. It maintains a list of attached overlays to determine best suited overlay based + * on event target and order of overlay opens. + */ +@Injectable() +export class OverlayKeyboardDispatcher implements OnDestroy { + + /** Currently attached overlays in the order they were attached. */ + _attachedOverlays: OverlayRef[] = []; + + private _keydownEventSubscription: Subscription | null; + + constructor(@Inject(DOCUMENT) private _document: any) {} + + ngOnDestroy() { + this._unsubscribeFromKeydownEvents(); + } + + /** Add a new overlay to the list of attached overlay refs. */ + add(overlayRef: OverlayRef): void { + // Lazily start dispatcher once first overlay is added + if (!this._keydownEventSubscription) { + this._subscribeToKeydownEvents(); + } + + this._attachedOverlays.push(overlayRef); + } + + /** Remove an overlay from the list of attached overlay refs. */ + remove(overlayRef: OverlayRef): void { + const index = this._attachedOverlays.indexOf(overlayRef); + + if (index > -1) { + this._attachedOverlays.splice(index, 1); + } + + // Remove the global listener once there are no more overlays. + if (this._attachedOverlays.length === 0) { + this._unsubscribeFromKeydownEvents(); + } + } + + /** + * Subscribe to keydown events that land on the body and dispatch those + * events to the appropriate overlay. + */ + private _subscribeToKeydownEvents(): void { + const bodyKeydownEvents = fromEvent(this._document.body, 'keydown'); + + this._keydownEventSubscription = bodyKeydownEvents.pipe( + filter(() => !!this._attachedOverlays.length) + ).subscribe(event => { + // Dispatch keydown event to correct overlay reference + this._selectOverlayFromEvent(event)._keydownEvents.next(event); + }); + } + + /** Removes the global keydown subscription. */ + private _unsubscribeFromKeydownEvents(): void { + if (this._keydownEventSubscription) { + this._keydownEventSubscription.unsubscribe(); + this._keydownEventSubscription = null; + } + } + + /** Select the appropriate overlay from a keydown event. */ + private _selectOverlayFromEvent(event: KeyboardEvent): OverlayRef { + // Check if any overlays contain the event + const targetedOverlay = this._attachedOverlays.find(overlay => { + return overlay.overlayElement === event.target || + overlay.overlayElement.contains(event.target as HTMLElement); + }); + + // Use that overlay if it exists, otherwise choose the most recently attached one + return targetedOverlay || this._attachedOverlays[this._attachedOverlays.length - 1]; + } + +} + +/** @docs-private */ +export function OVERLAY_KEYBOARD_DISPATCHER_PROVIDER_FACTORY( + dispatcher: OverlayKeyboardDispatcher, _document: any) { + return dispatcher || new OverlayKeyboardDispatcher(_document); +} + +/** @docs-private */ +export const OVERLAY_KEYBOARD_DISPATCHER_PROVIDER = { + // If there is already an OverlayKeyboardDispatcher available, use that. + // Otherwise, provide a new one. + provide: OverlayKeyboardDispatcher, + deps: [ + [new Optional(), new SkipSelf(), OverlayKeyboardDispatcher], + + // Coerce to `InjectionToken` so that the `deps` match the "shape" + // of the type expected by Angular + DOCUMENT as InjectionToken + ], + useFactory: OVERLAY_KEYBOARD_DISPATCHER_PROVIDER_FACTORY +}; diff --git a/src/cdk/overlay/overlay-state.ts b/src/cdk/overlay/overlay-config.ts similarity index 80% rename from src/cdk/overlay/overlay-state.ts rename to src/cdk/overlay/overlay-config.ts index faba40d05881..ed12730a7fff 100644 --- a/src/cdk/overlay/overlay-state.ts +++ b/src/cdk/overlay/overlay-config.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -12,16 +12,13 @@ import {ScrollStrategy} from './scroll/scroll-strategy'; import {NoopScrollStrategy} from './scroll/noop-scroll-strategy'; -/** - * OverlayState is a bag of values for either the initial configuration or current state of an - * overlay. - */ -export class OverlayState { +/** Initial configuration used when creating an overlay. */ +export class OverlayConfig { /** Strategy with which to position the overlay. */ - positionStrategy: PositionStrategy; + positionStrategy?: PositionStrategy; /** Strategy to be used when handling scroll events while the overlay is open. */ - scrollStrategy: ScrollStrategy = new NoopScrollStrategy(); + scrollStrategy?: ScrollStrategy = new NoopScrollStrategy(); /** Custom class to add to the overlay pane. */ panelClass?: string | string[] = ''; @@ -53,8 +50,9 @@ export class OverlayState { /** The direction of the text in the overlay panel. */ direction?: Direction = 'ltr'; - // TODO(jelbourn): configuration still to add - // - focus trap - // - disable pointer events - // - z-index + constructor(config?: OverlayConfig) { + if (config) { + Object.keys(config).forEach(key => this[key] = config[key]); + } + } } diff --git a/src/lib/core/overlay/overlay-container.spec.ts b/src/cdk/overlay/overlay-container.spec.ts similarity index 72% rename from src/lib/core/overlay/overlay-container.spec.ts rename to src/cdk/overlay/overlay-container.spec.ts index 218f3f358826..f1a6fa063aba 100644 --- a/src/lib/core/overlay/overlay-container.spec.ts +++ b/src/cdk/overlay/overlay-container.spec.ts @@ -1,6 +1,6 @@ import {async, inject, TestBed} from '@angular/core/testing'; import {Component, NgModule, ViewChild, ViewContainerRef} from '@angular/core'; -import {PortalModule, TemplatePortalDirective} from '../portal/portal-directives'; +import {PortalModule, CdkPortal} from '@angular/cdk/portal'; import {Overlay, OverlayContainer, OverlayModule} from './index'; describe('OverlayContainer', () => { @@ -26,6 +26,10 @@ describe('OverlayContainer', () => { overlayContainer = oc; })); + afterEach(() => { + overlayContainer.ngOnDestroy(); + }); + it('should remove the overlay container element from the DOM on destruction', () => { const fixture = TestBed.createComponent(TestComponentWithTemplatePortals); @@ -43,6 +47,19 @@ describe('OverlayContainer', () => { expect(document.querySelector('.cdk-overlay-container')) .toBeNull('Expected the overlay container *not* to be in the DOM after destruction'); }); + + it('should add and remove css classes from the container element', () => { + overlayContainer.getContainerElement().classList.add('commander-shepard'); + + const containerElement = document.querySelector('.cdk-overlay-container')!; + expect(containerElement.classList.contains('commander-shepard')) + .toBe(true, 'Expected the overlay container to have class "commander-shepard"'); + + overlayContainer.getContainerElement().classList.remove('commander-shepard'); + + expect(containerElement.classList.contains('commander-shepard')) + .toBe(false, 'Expected the overlay container not to have class "commander-shepard"'); + }); }); /** Test-bed component that contains a TempatePortal and an ElementRef. */ @@ -51,7 +68,7 @@ describe('OverlayContainer', () => { providers: [Overlay], }) class TestComponentWithTemplatePortals { - @ViewChild(TemplatePortalDirective) templatePortal: TemplatePortalDirective; + @ViewChild(CdkPortal) templatePortal: CdkPortal; constructor(public viewContainerRef: ViewContainerRef) { } } diff --git a/src/cdk/overlay/overlay-container.ts b/src/cdk/overlay/overlay-container.ts index 048e1d1adcde..5579b73dc632 100644 --- a/src/cdk/overlay/overlay-container.ts +++ b/src/cdk/overlay/overlay-container.ts @@ -1,41 +1,21 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import {Injectable, Optional, SkipSelf, OnDestroy} from '@angular/core'; +import {Injectable, InjectionToken, Inject, Optional, SkipSelf, OnDestroy} from '@angular/core'; +import {DOCUMENT} from '@angular/common'; -/** - * The OverlayContainer is the container in which all overlays will load. - * It should be provided in the root component to ensure it is properly shared. - */ +/** Container inside which all overlays will render. */ @Injectable() export class OverlayContainer implements OnDestroy { protected _containerElement: HTMLElement; - private _themeClass: string; - - /** - * Base theme to be applied to all overlay-based components. - */ - get themeClass(): string { return this._themeClass; } - set themeClass(value: string) { - if (this._containerElement) { - if (this._themeClass) { - this._containerElement.classList.remove(this._themeClass); - } - - if (value) { - this._containerElement.classList.add(value); - } - } - - this._themeClass = value; - } + constructor(@Inject(DOCUMENT) private _document: any) {} ngOnDestroy() { if (this._containerElement && this._containerElement.parentNode) { @@ -44,7 +24,7 @@ export class OverlayContainer implements OnDestroy { } /** - * This method returns the overlay container element. It will lazily + * This method returns the overlay container element. It will lazily * create the element the first time it is called to facilitate using * the container in non-browser environments. * @returns the container element @@ -59,27 +39,27 @@ export class OverlayContainer implements OnDestroy { * with the 'cdk-overlay-container' class on the document body. */ protected _createContainer(): void { - let container = document.createElement('div'); - container.classList.add('cdk-overlay-container'); + const container = this._document.createElement('div'); - if (this._themeClass) { - container.classList.add(this._themeClass); - } - - document.body.appendChild(container); + container.classList.add('cdk-overlay-container'); + this._document.body.appendChild(container); this._containerElement = container; } } /** @docs-private */ -export function OVERLAY_CONTAINER_PROVIDER_FACTORY(parentContainer: OverlayContainer) { - return parentContainer || new OverlayContainer(); +export function OVERLAY_CONTAINER_PROVIDER_FACTORY(parentContainer: OverlayContainer, + _document: any) { + return parentContainer || new OverlayContainer(_document); } /** @docs-private */ export const OVERLAY_CONTAINER_PROVIDER = { // If there is already an OverlayContainer available, use that. Otherwise, provide a new one. provide: OverlayContainer, - deps: [[new Optional(), new SkipSelf(), OverlayContainer]], + deps: [ + [new Optional(), new SkipSelf(), OverlayContainer], + DOCUMENT as InjectionToken // We need to use the InjectionToken somewhere to keep TS happy + ], useFactory: OVERLAY_CONTAINER_PROVIDER_FACTORY }; diff --git a/src/cdk/overlay/overlay-directives.spec.ts b/src/cdk/overlay/overlay-directives.spec.ts index e0876038d708..ae5238af90c5 100644 --- a/src/cdk/overlay/overlay-directives.spec.ts +++ b/src/cdk/overlay/overlay-directives.spec.ts @@ -1,16 +1,17 @@ import {Component, ViewChild} from '@angular/core'; import {By} from '@angular/platform-browser'; -import {ComponentFixture, TestBed, async} from '@angular/core/testing'; +import {ComponentFixture, TestBed, async, inject} from '@angular/core/testing'; import {Directionality} from '@angular/cdk/bidi'; import {dispatchKeyboardEvent} from '@angular/cdk/testing'; import {ESCAPE} from '@angular/cdk/keycodes'; -import {ConnectedOverlayDirective, OverlayModule, OverlayOrigin} from './index'; +import {CdkConnectedOverlay, OverlayModule, CdkOverlayOrigin} from './index'; import {OverlayContainer} from './overlay-container'; import {ConnectedPositionStrategy} from './position/connected-position-strategy'; import {ConnectedOverlayPositionChange} from './position/connected-position'; describe('Overlay directives', () => { + let overlayContainer: OverlayContainer; let overlayContainerElement: HTMLElement; let fixture: ComponentFixture; let dir: {value: string}; @@ -20,12 +21,8 @@ describe('Overlay directives', () => { imports: [OverlayModule], declarations: [ConnectedOverlayDirectiveTest, ConnectedOverlayPropertyInitOrder], providers: [ - {provide: OverlayContainer, useFactory: () => { - overlayContainerElement = document.createElement('div'); - return {getContainerElement: () => overlayContainerElement}; - }}, {provide: Directionality, useFactory: () => { - return dir = { value: 'ltr' }; + return dir = {value: 'ltr'}; }} ], }); @@ -36,6 +33,15 @@ describe('Overlay directives', () => { fixture.detectChanges(); }); + beforeEach(inject([OverlayContainer], (oc: OverlayContainer) => { + overlayContainer = oc; + overlayContainerElement = oc.getContainerElement(); + })); + + afterEach(() => { + overlayContainer.ngOnDestroy(); + }); + /** Returns the current open overlay pane element. */ function getPaneElement() { return overlayContainerElement.querySelector('.cdk-overlay-pane') as HTMLElement; @@ -76,7 +82,7 @@ describe('Overlay directives', () => { let overlayDirective = testComponent.connectedOverlayDirective; let strategy = - overlayDirective.overlayRef.getState().positionStrategy; + overlayDirective.overlayRef.getConfig().positionStrategy; expect(strategy instanceof ConnectedPositionStrategy).toBe(true); let positions = strategy.positions; @@ -135,6 +141,15 @@ describe('Overlay directives', () => { const pane = overlayContainerElement.children[0] as HTMLElement; expect(pane.style.width).toEqual('250px'); + + fixture.componentInstance.isOpen = false; + fixture.detectChanges(); + + fixture.componentInstance.width = 500; + fixture.componentInstance.isOpen = true; + fixture.detectChanges(); + + expect(pane.style.width).toEqual('500px'); }); it('should set the height', () => { @@ -144,6 +159,15 @@ describe('Overlay directives', () => { const pane = overlayContainerElement.children[0] as HTMLElement; expect(pane.style.height).toEqual('100vh'); + + fixture.componentInstance.isOpen = false; + fixture.detectChanges(); + + fixture.componentInstance.height = '50vh'; + fixture.componentInstance.isOpen = true; + fixture.detectChanges(); + + expect(pane.style.height).toEqual('50vh'); }); it('should set the min width', () => { @@ -153,6 +177,15 @@ describe('Overlay directives', () => { const pane = overlayContainerElement.children[0] as HTMLElement; expect(pane.style.minWidth).toEqual('250px'); + + fixture.componentInstance.isOpen = false; + fixture.detectChanges(); + + fixture.componentInstance.minWidth = 500; + fixture.componentInstance.isOpen = true; + fixture.detectChanges(); + + expect(pane.style.minWidth).toEqual('500px'); }); it('should set the min height', () => { @@ -162,6 +195,15 @@ describe('Overlay directives', () => { const pane = overlayContainerElement.children[0] as HTMLElement; expect(pane.style.minHeight).toEqual('500px'); + + fixture.componentInstance.isOpen = false; + fixture.detectChanges(); + + fixture.componentInstance.minHeight = 500; + fixture.componentInstance.isOpen = true; + fixture.detectChanges(); + + expect(pane.style.minHeight).toEqual('500px'); }); it('should create the backdrop if designated', () => { @@ -324,7 +366,7 @@ class ConnectedOverlayDirectiveTest { detachHandler = jasmine.createSpy('detachHandler'); attachResult: HTMLElement; - @ViewChild(ConnectedOverlayDirective) connectedOverlayDirective: ConnectedOverlayDirective; + @ViewChild(CdkConnectedOverlay) connectedOverlayDirective: CdkConnectedOverlay; } @Component({ @@ -333,7 +375,7 @@ class ConnectedOverlayDirectiveTest { Menu content`, }) class ConnectedOverlayPropertyInitOrder { - @ViewChild(ConnectedOverlayDirective) connectedOverlayDirective: ConnectedOverlayDirective; - @ViewChild('trigger') trigger: OverlayOrigin; + @ViewChild(CdkConnectedOverlay) connectedOverlayDirective: CdkConnectedOverlay; + @ViewChild('trigger') trigger: CdkOverlayOrigin; } diff --git a/src/cdk/overlay/overlay-directives.ts b/src/cdk/overlay/overlay-directives.ts index 5ab882e57d74..ac698541ccb7 100644 --- a/src/cdk/overlay/overlay-directives.ts +++ b/src/cdk/overlay/overlay-directives.ts @@ -1,11 +1,15 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ +import {Direction, Directionality} from '@angular/cdk/bidi'; +import {coerceBooleanProperty} from '@angular/cdk/coercion'; +import {ESCAPE} from '@angular/cdk/keycodes'; +import {TemplatePortal} from '@angular/cdk/portal'; import { Directive, ElementRef, @@ -17,28 +21,21 @@ import { OnDestroy, Optional, Output, - Renderer2, SimpleChanges, TemplateRef, ViewContainerRef, } from '@angular/core'; -import {Direction, Directionality} from '@angular/cdk/bidi'; -import {coerceBooleanProperty} from '@angular/cdk/coercion'; -import {ESCAPE} from '@angular/cdk/keycodes'; -import {TemplatePortal} from '@angular/cdk/portal'; +import {Subscription} from 'rxjs/Subscription'; import {Overlay} from './overlay'; +import {OverlayConfig} from './overlay-config'; import {OverlayRef} from './overlay-ref'; -import {OverlayState} from './overlay-state'; import { - // This import is only used to define a generic type. The current TypeScript version incorrectly - // considers such imports as unused (https://github.com/Microsoft/TypeScript/issues/14953) - // tslint:disable-next-line:no-unused-variable ConnectedOverlayPositionChange, ConnectionPositionPair, } from './position/connected-position'; import {ConnectedPositionStrategy} from './position/connected-position-strategy'; import {RepositionScrollStrategy, ScrollStrategy} from './scroll/index'; -import {Subscription} from 'rxjs/Subscription'; +import {DOCUMENT} from '@angular/common'; /** Default set of positions for the overlay. Follows the behavior of a dropdown. */ @@ -52,24 +49,23 @@ const defaultPositionList = [ ]; /** Injection token that determines the scroll handling while the connected overlay is open. */ -export const MD_CONNECTED_OVERLAY_SCROLL_STRATEGY = - new InjectionToken<() => ScrollStrategy>('md-connected-overlay-scroll-strategy'); +export const CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY = + new InjectionToken<() => ScrollStrategy>('cdk-connected-overlay-scroll-strategy'); /** @docs-private */ -export function MD_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER_FACTORY(overlay: Overlay): +export function CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER_FACTORY(overlay: Overlay): () => RepositionScrollStrategy { return () => overlay.scrollStrategies.reposition(); } /** @docs-private */ -export const MD_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER = { - provide: MD_CONNECTED_OVERLAY_SCROLL_STRATEGY, +export const CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER = { + provide: CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY, deps: [Overlay], - useFactory: MD_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER_FACTORY, + useFactory: CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER_FACTORY, }; - /** * Directive applied to an element to make it usable as an origin for an Overlay using a * ConnectedPositionStrategy. @@ -78,12 +74,13 @@ export const MD_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER = { selector: '[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]', exportAs: 'cdkOverlayOrigin', }) -export class OverlayOrigin { - constructor(public elementRef: ElementRef) { } +export class CdkOverlayOrigin { + constructor( + /** Reference to the element on which the directive is applied. */ + public elementRef: ElementRef) { } } - /** * Directive to facilitate declarative creation of an Overlay using a ConnectedPositionStrategy. */ @@ -91,19 +88,18 @@ export class OverlayOrigin { selector: '[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]', exportAs: 'cdkConnectedOverlay' }) -export class ConnectedOverlayDirective implements OnDestroy, OnChanges { +export class CdkConnectedOverlay implements OnDestroy, OnChanges { private _overlayRef: OverlayRef; private _templatePortal: TemplatePortal; private _hasBackdrop = false; - private _backdropSubscription: Subscription | null; - private _positionSubscription: Subscription; + private _backdropSubscription = Subscription.EMPTY; + private _positionSubscription = Subscription.EMPTY; private _offsetX: number = 0; private _offsetY: number = 0; private _position: ConnectedPositionStrategy; - private _escapeListener: Function; /** Origin for the connected overlay. */ - @Input('cdkConnectedOverlayOrigin') origin: OverlayOrigin; + @Input('cdkConnectedOverlayOrigin') origin: CdkOverlayOrigin; /** Registered connected position pairs. */ @Input('cdkConnectedOverlayPositions') positions: ConnectionPositionPair[]; @@ -157,8 +153,8 @@ export class ConnectedOverlayDirective implements OnDestroy, OnChanges { /** @deprecated */ @Input('origin') - get _deprecatedOrigin(): OverlayOrigin { return this.origin; } - set _deprecatedOrigin(_origin: OverlayOrigin) { this.origin = _origin; } + get _deprecatedOrigin(): CdkOverlayOrigin { return this.origin; } + set _deprecatedOrigin(_origin: CdkOverlayOrigin) { this.origin = _origin; } /** @deprecated */ @Input('positions') @@ -233,11 +229,11 @@ export class ConnectedOverlayDirective implements OnDestroy, OnChanges { constructor( private _overlay: Overlay, - private _renderer: Renderer2, templateRef: TemplateRef, viewContainerRef: ViewContainerRef, - @Inject(MD_CONNECTED_OVERLAY_SCROLL_STRATEGY) private _scrollStrategy, - @Optional() private _dir: Directionality) { + @Inject(CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY) private _scrollStrategy, + @Optional() private _dir: Directionality, + @Optional() @Inject(DOCUMENT) private _document: any) { this._templatePortal = new TemplatePortal(templateRef, viewContainerRef); } @@ -271,9 +267,33 @@ export class ConnectedOverlayDirective implements OnDestroy, OnChanges { } /** Builds the overlay config based on the directive's inputs */ - private _buildConfig(): OverlayState { - let overlayConfig = new OverlayState(); + private _buildConfig(): OverlayConfig { + const positionStrategy = this._position = this._createPositionStrategy(); + const overlayConfig = new OverlayConfig({ + positionStrategy, + scrollStrategy: this.scrollStrategy, + hasBackdrop: this.hasBackdrop + }); + + this._configureSize(overlayConfig); + overlayConfig.hasBackdrop = this.hasBackdrop; + + if (this.backdropClass) { + overlayConfig.backdropClass = this.backdropClass; + } + + this._configureSize(overlayConfig); + + this._position = this._createPositionStrategy() as ConnectedPositionStrategy; + overlayConfig.positionStrategy = this._position; + overlayConfig.scrollStrategy = this.scrollStrategy; + + return overlayConfig; + } + + /** Configure the overlay size based on the directive's inputs */ + private _configureSize(overlayConfig: OverlayState): void { if (this.width || this.width === 0) { overlayConfig.width = this.width; } @@ -295,12 +315,6 @@ export class ConnectedOverlayDirective implements OnDestroy, OnChanges { if (this.backdropClass) { overlayConfig.backdropClass = this.backdropClass; } - - this._position = this._createPositionStrategy() as ConnectedPositionStrategy; - overlayConfig.positionStrategy = this._position; - overlayConfig.scrollStrategy = this.scrollStrategy; - - return overlayConfig; } /** Returns the position strategy of the overlay to be set on the overlay config */ @@ -335,11 +349,16 @@ export class ConnectedOverlayDirective implements OnDestroy, OnChanges { private _attachOverlay() { if (!this._overlayRef) { this._createOverlay(); + } else { + // Update the overlay size, in case the directive's inputs have changed + const overlayState = new OverlayState(); + this._configureSize(overlayState); + this._overlayRef.updateSize(overlayState); } this._position.withDirection(this.dir); - this._overlayRef.getState().direction = this.dir; - this._initEscapeListener(); + this._overlayRef.setDirection(this.dir); + this._document.addEventListener('keydown', this._escapeListener); if (!this._overlayRef.hasAttached()) { this._overlayRef.attach(this._templatePortal); @@ -360,14 +379,8 @@ export class ConnectedOverlayDirective implements OnDestroy, OnChanges { this.detach.emit(); } - if (this._backdropSubscription) { - this._backdropSubscription.unsubscribe(); - this._backdropSubscription = null; - } - - if (this._escapeListener) { - this._escapeListener(); - } + this._backdropSubscription.unsubscribe(); + this._document.removeEventListener('keydown', this._escapeListener); } /** Destroys the overlay created by this directive. */ @@ -376,25 +389,15 @@ export class ConnectedOverlayDirective implements OnDestroy, OnChanges { this._overlayRef.dispose(); } - if (this._backdropSubscription) { - this._backdropSubscription.unsubscribe(); - } - - if (this._positionSubscription) { - this._positionSubscription.unsubscribe(); - } - - if (this._escapeListener) { - this._escapeListener(); - } + this._backdropSubscription.unsubscribe(); + this._positionSubscription.unsubscribe(); + this._document.removeEventListener('keydown', this._escapeListener); } - /** Sets the event listener that closes the overlay when pressing Escape. */ - private _initEscapeListener() { - this._escapeListener = this._renderer.listen('document', 'keydown', (event: KeyboardEvent) => { - if (event.keyCode === ESCAPE) { - this._detachOverlay(); - } - }); + /** Event listener that will close the overlay when the user presses escape. */ + private _escapeListener = (event: KeyboardEvent) => { + if (event.keyCode === ESCAPE) { + this._detachOverlay(); + } } } diff --git a/src/cdk/overlay/overlay-module.ts b/src/cdk/overlay/overlay-module.ts new file mode 100644 index 000000000000..ada0dbe2437b --- /dev/null +++ b/src/cdk/overlay/overlay-module.ts @@ -0,0 +1,39 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {BidiModule} from '@angular/cdk/bidi'; +import {PortalModule} from '@angular/cdk/portal'; +import {ScrollDispatchModule, VIEWPORT_RULER_PROVIDER} from '@angular/cdk/scrolling'; +import {NgModule, Provider} from '@angular/core'; +import {Overlay} from './overlay'; +import {OVERLAY_CONTAINER_PROVIDER} from './overlay-container'; +import { + CdkConnectedOverlay, + CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER, + CdkOverlayOrigin, +} from './overlay-directives'; +import {OverlayPositionBuilder} from './position/overlay-position-builder'; +import {OVERLAY_KEYBOARD_DISPATCHER_PROVIDER} from './keyboard/overlay-keyboard-dispatcher'; +import {ScrollStrategyOptions} from './scroll/scroll-strategy-options'; + +export const OVERLAY_PROVIDERS: Provider[] = [ + Overlay, + OverlayPositionBuilder, + OVERLAY_KEYBOARD_DISPATCHER_PROVIDER, + VIEWPORT_RULER_PROVIDER, + OVERLAY_CONTAINER_PROVIDER, + CDK_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER, +]; + +@NgModule({ + imports: [BidiModule, PortalModule, ScrollDispatchModule], + exports: [CdkConnectedOverlay, CdkOverlayOrigin, ScrollDispatchModule], + declarations: [CdkConnectedOverlay, CdkOverlayOrigin], + providers: [OVERLAY_PROVIDERS, ScrollStrategyOptions], +}) +export class OverlayModule {} diff --git a/src/cdk/overlay/overlay-ref.ts b/src/cdk/overlay/overlay-ref.ts index 765187848ec4..c1be7f8b78b2 100644 --- a/src/cdk/overlay/overlay-ref.ts +++ b/src/cdk/overlay/overlay-ref.ts @@ -1,35 +1,49 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import {NgZone} from '@angular/core'; -import {PortalHost, Portal} from '@angular/cdk/portal'; -import {OverlayState} from './overlay-state'; +import {Direction} from '@angular/cdk/bidi'; +import {ComponentPortal, Portal, PortalOutlet, TemplatePortal} from '@angular/cdk/portal'; +import {ComponentRef, EmbeddedViewRef, NgZone} from '@angular/core'; import {Observable} from 'rxjs/Observable'; +import {take} from 'rxjs/operators/take'; import {Subject} from 'rxjs/Subject'; +import {OverlayKeyboardDispatcher} from './keyboard/overlay-keyboard-dispatcher'; +import {OverlayConfig} from './overlay-config'; +/** An object where all of its properties cannot be written. */ +export type ImmutableObject = { + readonly [P in keyof T]: T[P]; +}; + /** * Reference to an overlay that has been created with the Overlay service. * Used to manipulate or dispose of said overlay. */ -export class OverlayRef implements PortalHost { +export class OverlayRef implements PortalOutlet { private _backdropElement: HTMLElement | null = null; private _backdropClick: Subject = new Subject(); private _attachments = new Subject(); private _detachments = new Subject(); + /** Stream of keydown events dispatched to this overlay. */ + _keydownEvents = new Subject(); + constructor( - private _portalHost: PortalHost, + private _portalOutlet: PortalOutlet, private _pane: HTMLElement, - private _state: OverlayState, - private _ngZone: NgZone) { + private _config: ImmutableObject, + private _ngZone: NgZone, + private _keyboardDispatcher: OverlayKeyboardDispatcher) { - _state.scrollStrategy.attach(this); + if (_config.scrollStrategy) { + _config.scrollStrategy.attach(this); + } } /** The overlay's HTML element */ @@ -37,152 +51,204 @@ export class OverlayRef implements PortalHost { return this._pane; } + attach(portal: ComponentPortal): ComponentRef; + attach(portal: TemplatePortal): EmbeddedViewRef; + attach(portal: any): any; + /** - * Attaches the overlay to a portal instance and adds the backdrop. + * Attaches content, given via a Portal, to the overlay. + * If the overlay is configured to have a backdrop, it will be created. + * * @param portal Portal instance to which to attach the overlay. * @returns The portal attachment result. */ attach(portal: Portal): any { - let attachResult = this._portalHost.attach(portal); + let attachResult = this._portalOutlet.attach(portal); - if (this._state.positionStrategy) { - this._state.positionStrategy.attach(this); + if (this._config.positionStrategy) { + this._config.positionStrategy.attach(this); } - // Update the pane element with the given state configuration. + // Update the pane element with the given configuration. this._updateStackingOrder(); - this.updateSize(); - this.updateDirection(); - this.updatePosition(); - this._state.scrollStrategy.enable(); + this._updateElementSize(); + this._updateElementDirection(); + + if (this._config.scrollStrategy) { + this._config.scrollStrategy.enable(); + } + + // Update the position once the zone is stable so that the overlay will be fully rendered + // before attempting to position it, as the position may depend on the size of the rendered + // content. + this._ngZone.onStable.asObservable().pipe(take(1)).subscribe(() => { + this.updatePosition(); + }); // Enable pointer events for the overlay pane element. this._togglePointerEvents(true); - if (this._state.hasBackdrop) { + if (this._config.hasBackdrop) { this._attachBackdrop(); } - if (this._state.panelClass) { + if (this._config.panelClass) { // We can't do a spread here, because IE doesn't support setting multiple classes. - if (Array.isArray(this._state.panelClass)) { - this._state.panelClass.forEach(cls => this._pane.classList.add(cls)); + if (Array.isArray(this._config.panelClass)) { + this._config.panelClass.forEach(cls => this._pane.classList.add(cls)); } else { - this._pane.classList.add(this._state.panelClass); + this._pane.classList.add(this._config.panelClass); } } // Only emit the `attachments` event once all other setup is done. this._attachments.next(); + // Track this overlay by the keyboard dispatcher + this._keyboardDispatcher.add(this); + return attachResult; } /** * Detaches an overlay from a portal. - * @returns Resolves when the overlay has been detached. + * @returns The portal detachment result. */ - detach(): Promise { + detach(): any { + if (!this.hasAttached()) { + return; + } + this.detachBackdrop(); // When the overlay is detached, the pane element should disable pointer events. // This is necessary because otherwise the pane element will cover the page and disable // pointer events therefore. Depends on the position strategy and the applied pane boundaries. this._togglePointerEvents(false); - this._state.scrollStrategy.disable(); - let detachmentResult = this._portalHost.detach(); + if (this._config.positionStrategy && this._config.positionStrategy.detach) { + this._config.positionStrategy.detach(); + } + + if (this._config.scrollStrategy) { + this._config.scrollStrategy.disable(); + } + + const detachmentResult = this._portalOutlet.detach(); // Only emit after everything is detached. this._detachments.next(); + // Remove this overlay from keyboard dispatcher tracking + this._keyboardDispatcher.remove(this); + return detachmentResult; } - /** - * Cleans up the overlay from the DOM. - */ + /** Cleans up the overlay from the DOM. */ dispose(): void { - if (this._state.positionStrategy) { - this._state.positionStrategy.dispose(); + const isAttached = this.hasAttached(); + + if (this._config.positionStrategy) { + this._config.positionStrategy.dispose(); + } + + if (this._config.scrollStrategy) { + this._config.scrollStrategy.disable(); } - this._state.scrollStrategy.disable(); this.detachBackdrop(); - this._portalHost.dispose(); + this._keyboardDispatcher.remove(this); + this._portalOutlet.dispose(); this._attachments.complete(); this._backdropClick.complete(); - this._detachments.next(); + this._keydownEvents.complete(); + + if (isAttached) { + this._detachments.next(); + } + this._detachments.complete(); } - /** - * Checks whether the overlay has been attached. - */ + /** Whether the overlay has attached content. */ hasAttached(): boolean { - return this._portalHost.hasAttached(); + return this._portalOutlet.hasAttached(); } - /** - * Returns an observable that emits when the backdrop has been clicked. - */ + /** Gets an observable that emits when the backdrop has been clicked. */ backdropClick(): Observable { return this._backdropClick.asObservable(); } - /** Returns an observable that emits when the overlay has been attached. */ + /** Gets an observable that emits when the overlay has been attached. */ attachments(): Observable { return this._attachments.asObservable(); } - /** Returns an observable that emits when the overlay has been detached. */ + /** Gets an observable that emits when the overlay has been detached. */ detachments(): Observable { return this._detachments.asObservable(); } - /** - * Gets the current state config of the overlay. - */ - getState(): OverlayState { - return this._state; + /** Gets an observable of keydown events targeted to this overlay. */ + keydownEvents(): Observable { + return this._keydownEvents.asObservable(); + } + + /** Gets the the current overlay configuration, which is immutable. */ + getConfig(): OverlayConfig { + return this._config; } /** Updates the position of the overlay based on the position strategy. */ updatePosition() { - if (this._state.positionStrategy) { - this._state.positionStrategy.apply(); + if (this._config.positionStrategy) { + this._config.positionStrategy.apply(); } } + /** Update the size properties of the overlay. */ + updateSize(sizeConfig: OverlaySizeConfig) { + this._config = {...this._config, ...sizeConfig}; + this._updateElementSize(); + } + + /** Sets the LTR/RTL direction for the overlay. */ + setDirection(dir: Direction) { + this._config = {...this._config, direction: dir}; + this._updateElementDirection(); + } + /** Updates the text direction of the overlay panel. */ - private updateDirection() { - this._pane.setAttribute('dir', this._state.direction!); + private _updateElementDirection() { + this._pane.setAttribute('dir', this._config.direction!); } - /** Updates the size of the overlay based on the overlay config. */ - updateSize() { - if (this._state.width || this._state.width === 0) { - this._pane.style.width = formatCssUnit(this._state.width); + /** Updates the size of the overlay element based on the overlay config. */ + private _updateElementSize() { + if (this._config.width || this._config.width === 0) { + this._pane.style.width = formatCssUnit(this._config.width); } - if (this._state.height || this._state.height === 0) { - this._pane.style.height = formatCssUnit(this._state.height); + if (this._config.height || this._config.height === 0) { + this._pane.style.height = formatCssUnit(this._config.height); } - if (this._state.minWidth || this._state.minWidth === 0) { - this._pane.style.minWidth = formatCssUnit(this._state.minWidth); + if (this._config.minWidth || this._config.minWidth === 0) { + this._pane.style.minWidth = formatCssUnit(this._config.minWidth); } - if (this._state.minHeight || this._state.minHeight === 0) { - this._pane.style.minHeight = formatCssUnit(this._state.minHeight); + if (this._config.minHeight || this._config.minHeight === 0) { + this._pane.style.minHeight = formatCssUnit(this._config.minHeight); } - if (this._state.maxWidth || this._state.maxWidth === 0) { - this._pane.style.maxWidth = formatCssUnit(this._state.maxWidth); + if (this._config.maxWidth || this._config.maxWidth === 0) { + this._pane.style.maxWidth = formatCssUnit(this._config.maxWidth); } - if (this._state.maxHeight || this._state.maxHeight === 0) { - this._pane.style.maxHeight = formatCssUnit(this._state.maxHeight); + if (this._config.maxHeight || this._config.maxHeight === 0) { + this._pane.style.maxHeight = formatCssUnit(this._config.maxHeight); } } @@ -196,8 +262,8 @@ export class OverlayRef implements PortalHost { this._backdropElement = document.createElement('div'); this._backdropElement.classList.add('cdk-overlay-backdrop'); - if (this._state.backdropClass) { - this._backdropElement.classList.add(this._state.backdropClass); + if (this._config.backdropClass) { + this._backdropElement.classList.add(this._config.backdropClass); } // Insert the backdrop before the pane in the DOM order, @@ -209,10 +275,12 @@ export class OverlayRef implements PortalHost { this._backdropElement.addEventListener('click', () => this._backdropClick.next(null)); // Add class to fade-in the backdrop after one frame. - requestAnimationFrame(() => { - if (this._backdropElement) { - this._backdropElement.classList.add('cdk-overlay-backdrop-showing'); - } + this._ngZone.runOutsideAngular(() => { + requestAnimationFrame(() => { + if (this._backdropElement) { + this._backdropElement.classList.add('cdk-overlay-backdrop-showing'); + } + }); }); } @@ -250,8 +318,8 @@ export class OverlayRef implements PortalHost { backdropToDetach.classList.remove('cdk-overlay-backdrop-showing'); - if (this._state.backdropClass) { - backdropToDetach.classList.remove(this._state.backdropClass); + if (this._config.backdropClass) { + backdropToDetach.classList.remove(this._config.backdropClass); } backdropToDetach.addEventListener('transitionend', finishDetach); @@ -273,3 +341,14 @@ export class OverlayRef implements PortalHost { function formatCssUnit(value: number | string) { return typeof value === 'string' ? value as string : `${value}px`; } + + +/** Size properties for an overlay. */ +export interface OverlaySizeConfig { + width?: number | string; + height?: number | string; + minWidth?: number | string; + minHeight?: number | string; + maxWidth?: number | string; + maxHeight?: number | string; +} diff --git a/src/cdk/overlay/overlay.md b/src/cdk/overlay/overlay.md new file mode 100644 index 000000000000..756ff1f0e26e --- /dev/null +++ b/src/cdk/overlay/overlay.md @@ -0,0 +1,86 @@ +The `overlay` package provides a way to open floating panels on the screen. + +### Creating overlays +Calling `overlay.create()` will return an `OverlayRef` instance. This instance is a handle for +managing that specific overlay. + +The `OverlayRef` _is_ a `PortalOutlet`- once created, content can be added by attaching a `Portal`. +See the documentation on portals for further information. +```ts +const overlayRef = overlay.create(); +const userProfilePortal = new ComponentPortal(UserProfile); +overlayRef.attach(userProfilePortal); +``` + +### Configuring an overlay +When creating an overlay, an optional configuration object can be provided. +```ts +const overlayRef = overlay.create({ + height: '400px', + width: '600px', +}); +``` + +The full set of configuration options can be found in the API documentation. + +#### Position strategies +The `positionStrategy` configuration option determines how the overlay will be positioned on-screen. +There are two position strategies available as part of the library: `GlobalPositionStrategy` and +`ConnectedPositionStrategy`. + +`GlobalPositionStrategy` is used for overlays that require a specific position in the viewport, +unrelated to other elements. This is commonly used for modal dialogs and application-level +notifications. + +`ConnectedPositionStrategy` is used for overlays that are positioned relative to some other "origin" +element on the page. This is commonly used for menus, pickers, and tooltips. When using the +connected strategy, a set of preferred positions is provided; the "best" position will be selected +based on how well the overlay would fit within the viewport. + +A custom position strategy can be created by implementing the `PositionStrategy` interface. +Each `PositionStrategy` defines an `apply` method that is called whenever the overlay's position +should be updated. A custom position strategy can additionally expose any other APIs necessary as +related to the positioning of the overlay element. + + +#### Scroll strategies +The `scrollStrategy` configuration option determines how the overlay will react to scrolling outside +the overlay element. There are four scroll strategies available as part of the library. + +`NoopScrollStrategy` is the default option. This strategy does nothing. + +`CloseScrollStrategy` will automatically close the overlay when scrolling occurs. + +`BlockScrollStrategy` will block page scrolling while the overlay is open. Note that some +applications may implement special or customized page scrolling; if the `BlockScrollStrategy` +conflicts with this kind of situation, it can be overriden by re-providing `BlockScrollStrategy` +with a custom implementation. + +`RepositionScrollStrategy` will re-position the overlay element on scroll. Note that this will have +some performance impact on scrolling- users should weigh this cost in the context of each specific +application. + + +A custom scroll strategy can be created by implementing the `ScrollStrategy` interface. Each +strategy will typically inject `ScrollDispatcher` (from `@angular/cdk/scrolling`) to be notified +of when scrolling takes place. See the documentation for `ScrollDispatcher` for more information +on how scroll events are detected and dispatched. + +### The overlay container +The `OverlayContainer` provides a handle to the container element in which all individual overlay +elements are rendered. By default, the overlay container is appended directly to the document body +so that an overlay is never clipped by an `overflow: hidden` parent. + +#### Full-screen overlays +The `FullscreenOverlayContainer` is an alternative to `OverlayContainer` that supports correct +displaying of overlay elements in +[fullscreen mode](https://developer.mozilla.org/en-US/docs/Web/API/Element/requestFullScreen). + +`FullscreenOverlayContainer` can be enabled by providing it in your `NgModule`: +```ts +@NgModule({ + providers: [{provide: OverlayContainer, useClass: FullscreenOverlayContainer}], + // ... +}) +export class MyModule { } +``` diff --git a/src/cdk/overlay/overlay.spec.ts b/src/cdk/overlay/overlay.spec.ts index f5551047e967..01fc5400ed3a 100644 --- a/src/cdk/overlay/overlay.spec.ts +++ b/src/cdk/overlay/overlay.spec.ts @@ -1,17 +1,17 @@ -import {async, ComponentFixture, inject, TestBed} from '@angular/core/testing'; +import {async, fakeAsync, tick, ComponentFixture, inject, TestBed} from '@angular/core/testing'; import {Component, NgModule, ViewChild, ViewContainerRef} from '@angular/core'; import { ComponentPortal, PortalModule, TemplatePortal, - TemplatePortalDirective + CdkPortal } from '@angular/cdk/portal'; import { Overlay, OverlayContainer, OverlayModule, OverlayRef, - OverlayState, + OverlayConfig, PositionStrategy, ScrollStrategy, } from './index'; @@ -22,23 +22,19 @@ describe('Overlay', () => { let componentPortal: ComponentPortal; let templatePortal: TemplatePortal; let overlayContainerElement: HTMLElement; + let overlayContainer: OverlayContainer; let viewContainerFixture: ComponentFixture; beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [OverlayModule, PortalModule, OverlayTestModule], - providers: [{ - provide: OverlayContainer, - useFactory: () => { - overlayContainerElement = document.createElement('div'); - return {getContainerElement: () => overlayContainerElement}; - } - }] + imports: [OverlayModule, PortalModule, OverlayTestModule] }).compileComponents(); })); - beforeEach(inject([Overlay], (o: Overlay) => { + beforeEach(inject([Overlay, OverlayContainer], (o: Overlay, oc: OverlayContainer) => { overlay = o; + overlayContainer = oc; + overlayContainerElement = oc.getContainerElement(); let fixture = TestBed.createComponent(TestComponentWithTemplatePortals); fixture.detectChanges(); @@ -47,6 +43,10 @@ describe('Overlay', () => { viewContainerFixture = fixture; })); + afterEach(() => { + overlayContainer.ngOnDestroy(); + }); + it('should load a component into an overlay', () => { let overlayRef = overlay.create(); overlayRef.attach(componentPortal); @@ -107,7 +107,7 @@ describe('Overlay', () => { expect(overlayContainerElement.textContent).toBe(''); }); - it('should ensure that the most-recently-attached overlay is on top', () => { + it('should ensure that the most-recently-attached overlay is on top', (() => { let pizzaOverlayRef = overlay.create(); let cakeOverlayRef = overlay.create(); @@ -130,13 +130,12 @@ describe('Overlay', () => { .toBeTruthy('Expected pizza to still be on the bottom.'); expect(cakeOverlayRef.overlayElement.nextSibling) .toBeFalsy('Expected cake to still be on top.'); - }); + })); it('should set the direction', () => { - const state = new OverlayState(); - state.direction = 'rtl'; + const config = new OverlayConfig({direction: 'rtl'}); - overlay.create(state).attach(componentPortal); + overlay.create(config).attach(componentPortal); const pane = overlayContainerElement.children[0] as HTMLElement; expect(pane.getAttribute('dir')).toEqual('rtl'); @@ -153,11 +152,8 @@ describe('Overlay', () => { }); it('should emit the attachment event after everything is added to the DOM', () => { - let state = new OverlayState(); - - state.hasBackdrop = true; - - let overlayRef = overlay.create(state); + let config = new OverlayConfig({hasBackdrop: true}); + let overlayRef = overlay.create(config); overlayRef.attachments().subscribe(() => { expect(overlayContainerElement.querySelector('pizza')) @@ -181,6 +177,26 @@ describe('Overlay', () => { expect(spy).toHaveBeenCalled(); }); + it('should not emit to the detach stream if the overlay has not been attached', () => { + let overlayRef = overlay.create(); + let spy = jasmine.createSpy('detachments spy'); + + overlayRef.detachments().subscribe(spy); + overlayRef.detach(); + + expect(spy).not.toHaveBeenCalled(); + }); + + it('should not emit to the detach stream on dispose if the overlay was not attached', () => { + let overlayRef = overlay.create(); + let spy = jasmine.createSpy('detachments spy'); + + overlayRef.detachments().subscribe(spy); + overlayRef.dispose(); + + expect(spy).not.toHaveBeenCalled(); + }); + it('should emit the detachment event after the overlay is removed from the DOM', () => { let overlayRef = overlay.create(); @@ -224,99 +240,101 @@ describe('Overlay', () => { }); describe('positioning', () => { - let state: OverlayState; + let config: OverlayConfig; beforeEach(() => { - state = new OverlayState(); + config = new OverlayConfig(); }); - it('should apply the positioning strategy', () => { - state.positionStrategy = new FakePositionStrategy(); + it('should apply the positioning strategy', fakeAsync(() => { + config.positionStrategy = new FakePositionStrategy(); - overlay.create(state).attach(componentPortal); + overlay.create(config).attach(componentPortal); + viewContainerFixture.detectChanges(); + tick(); expect(overlayContainerElement.querySelectorAll('.fake-positioned').length).toBe(1); - }); + })); }); describe('size', () => { - let state: OverlayState; + let config: OverlayConfig; beforeEach(() => { - state = new OverlayState(); + config = new OverlayConfig(); }); it('should apply the width set in the config', () => { - state.width = 500; + config.width = 500; - overlay.create(state).attach(componentPortal); + overlay.create(config).attach(componentPortal); const pane = overlayContainerElement.children[0] as HTMLElement; expect(pane.style.width).toEqual('500px'); }); it('should support using other units if a string width is provided', () => { - state.width = '200%'; + config.width = '200%'; - overlay.create(state).attach(componentPortal); + overlay.create(config).attach(componentPortal); const pane = overlayContainerElement.children[0] as HTMLElement; expect(pane.style.width).toEqual('200%'); }); it('should apply the height set in the config', () => { - state.height = 500; + config.height = 500; - overlay.create(state).attach(componentPortal); + overlay.create(config).attach(componentPortal); const pane = overlayContainerElement.children[0] as HTMLElement; expect(pane.style.height).toEqual('500px'); }); it('should support using other units if a string height is provided', () => { - state.height = '100vh'; + config.height = '100vh'; - overlay.create(state).attach(componentPortal); + overlay.create(config).attach(componentPortal); const pane = overlayContainerElement.children[0] as HTMLElement; expect(pane.style.height).toEqual('100vh'); }); it('should apply the min width set in the config', () => { - state.minWidth = 200; + config.minWidth = 200; - overlay.create(state).attach(componentPortal); + overlay.create(config).attach(componentPortal); const pane = overlayContainerElement.children[0] as HTMLElement; expect(pane.style.minWidth).toEqual('200px'); }); it('should apply the min height set in the config', () => { - state.minHeight = 500; + config.minHeight = 500; - overlay.create(state).attach(componentPortal); + overlay.create(config).attach(componentPortal); const pane = overlayContainerElement.children[0] as HTMLElement; expect(pane.style.minHeight).toEqual('500px'); }); it('should apply the max width set in the config', () => { - state.maxWidth = 200; + config.maxWidth = 200; - overlay.create(state).attach(componentPortal); + overlay.create(config).attach(componentPortal); const pane = overlayContainerElement.children[0] as HTMLElement; expect(pane.style.maxWidth).toEqual('200px'); }); it('should apply the max height set in the config', () => { - state.maxHeight = 500; + config.maxHeight = 500; - overlay.create(state).attach(componentPortal); + overlay.create(config).attach(componentPortal); const pane = overlayContainerElement.children[0] as HTMLElement; expect(pane.style.maxHeight).toEqual('500px'); }); it('should support zero widths and heights', () => { - state.width = 0; - state.height = 0; + config.width = 0; + config.height = 0; - overlay.create(state).attach(componentPortal); + overlay.create(config).attach(componentPortal); const pane = overlayContainerElement.children[0] as HTMLElement; expect(pane.style.width).toEqual('0px'); expect(pane.style.height).toEqual('0px'); @@ -324,10 +342,10 @@ describe('Overlay', () => { }); describe('backdrop', () => { - let config: OverlayState; + let config: OverlayConfig; beforeEach(() => { - config = new OverlayState(); + config = new OverlayConfig(); config.hasBackdrop = true; }); @@ -415,9 +433,8 @@ describe('Overlay', () => { describe('panelClass', () => { it('should apply a custom overlay pane class', () => { - const config = new OverlayState(); + const config = new OverlayConfig({panelClass: 'custom-panel-class'}); - config.panelClass = 'custom-panel-class'; overlay.create(config).attach(componentPortal); viewContainerFixture.detectChanges(); @@ -426,9 +443,8 @@ describe('Overlay', () => { }); it('should be able to apply multiple classes', () => { - const config = new OverlayState(); + const config = new OverlayConfig({panelClass: ['custom-class-one', 'custom-class-two']}); - config.panelClass = ['custom-class-one', 'custom-class-two']; overlay.create(config).attach(componentPortal); viewContainerFixture.detectChanges(); @@ -441,12 +457,12 @@ describe('Overlay', () => { describe('scroll strategy', () => { let fakeScrollStrategy: FakeScrollStrategy; - let config: OverlayState; + let config: OverlayConfig; let overlayRef: OverlayRef; beforeEach(() => { - config = new OverlayState(); - fakeScrollStrategy = config.scrollStrategy = new FakeScrollStrategy(); + fakeScrollStrategy = new FakeScrollStrategy(); + config = new OverlayConfig({scrollStrategy: fakeScrollStrategy}); overlayRef = overlay.create(config); }); @@ -475,44 +491,6 @@ describe('Overlay', () => { }); }); -describe('OverlayContainer theming', () => { - let overlayContainer: OverlayContainer; - let overlayContainerElement: HTMLElement; - - beforeEach(async(() => { - TestBed.configureTestingModule({ imports: [OverlayContainerThemingTestModule] }); - TestBed.compileComponents(); - })); - - beforeEach(inject([OverlayContainer], (o: OverlayContainer) => { - overlayContainer = o; - overlayContainerElement = overlayContainer.getContainerElement(); - })); - - afterEach(() => { - overlayContainerElement.parentNode!.removeChild(overlayContainerElement); - }); - - it('should be able to set a theme on the overlay container', () => { - overlayContainer.themeClass = 'my-theme'; - expect(overlayContainerElement.classList).toContain('my-theme'); - }); - - it('should clear any previously-set themes when a new theme is set', () => { - overlayContainer.themeClass = 'initial-theme'; - expect(overlayContainerElement.classList).toContain('initial-theme'); - - overlayContainer.themeClass = 'new-theme'; - expect(overlayContainerElement.classList).not.toContain('initial-theme'); - expect(overlayContainerElement.classList).toContain('new-theme'); - }); - - it('should not throw when switching from a blank theme', () => { - overlayContainer.themeClass = ''; - expect(() => overlayContainer.themeClass = 'new-theme').not.toThrow(); - }); -}); - /** Simple component for testing ComponentPortal. */ @Component({ selector: 'pizza', @@ -524,7 +502,7 @@ class PizzaMsg { } /** Test-bed component that contains a TempatePortal and an ElementRef. */ @Component({template: `Cake`}) class TestComponentWithTemplatePortals { - @ViewChild(TemplatePortalDirective) templatePortal: TemplatePortalDirective; + @ViewChild(CdkPortal) templatePortal: CdkPortal; constructor(public viewContainerRef: ViewContainerRef) { } } @@ -540,12 +518,6 @@ const TEST_COMPONENTS = [PizzaMsg, TestComponentWithTemplatePortals]; }) class OverlayTestModule { } -/** Component for testing the overlay container theming. */ -@NgModule({ - imports: [OverlayModule, PortalModule], -}) -class OverlayContainerThemingTestModule { } - class FakePositionStrategy implements PositionStrategy { element: HTMLElement; diff --git a/src/cdk/overlay/overlay.ts b/src/cdk/overlay/overlay.ts index 3af90a695d1b..c6e75901f13e 100644 --- a/src/cdk/overlay/overlay.ts +++ b/src/cdk/overlay/overlay.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -12,20 +12,23 @@ import { ApplicationRef, Injector, NgZone, + Inject, } from '@angular/core'; -import {DomPortalHost} from '@angular/cdk/portal'; -import {OverlayState} from './overlay-state'; +import {DomPortalOutlet} from '@angular/cdk/portal'; +import {OverlayConfig} from './overlay-config'; import {OverlayRef} from './overlay-ref'; import {OverlayPositionBuilder} from './position/overlay-position-builder'; +import {OverlayKeyboardDispatcher} from './keyboard/overlay-keyboard-dispatcher'; import {OverlayContainer} from './overlay-container'; import {ScrollStrategyOptions} from './scroll/index'; +import {DOCUMENT} from '@angular/common'; /** Next overlay unique ID. */ let nextUniqueId = 0; -/** The default state for newly created overlays. */ -let defaultState = new OverlayState(); +/** The default config for newly created overlays. */ +let defaultConfig = new OverlayConfig(); /** @@ -34,32 +37,37 @@ let defaultState = new OverlayState(); * selects, etc. can all be built using overlays. The service should primarily be used by authors * of re-usable components rather than developers building end-user applications. * - * An overlay *is* a PortalHost, so any kind of Portal can be loaded into one. + * An overlay *is* a PortalOutlet, so any kind of Portal can be loaded into one. */ @Injectable() export class Overlay { - constructor(public scrollStrategies: ScrollStrategyOptions, + constructor( + /** Scrolling strategies that can be used when creating an overlay. */ + public scrollStrategies: ScrollStrategyOptions, private _overlayContainer: OverlayContainer, private _componentFactoryResolver: ComponentFactoryResolver, private _positionBuilder: OverlayPositionBuilder, + private _keyboardDispatcher: OverlayKeyboardDispatcher, private _appRef: ApplicationRef, private _injector: Injector, - private _ngZone: NgZone) { } + private _ngZone: NgZone, + @Inject(DOCUMENT) private _document: any) { } /** * Creates an overlay. - * @param state State to apply to the overlay. + * @param config Configuration applied to the overlay. * @returns Reference to the created overlay. */ - create(state: OverlayState = defaultState): OverlayRef { + create(config: OverlayConfig = defaultConfig): OverlayRef { const pane = this._createPaneElement(); - const portalHost = this._createPortalHost(pane); - return new OverlayRef(portalHost, pane, state, this._ngZone); + const portalOutlet = this._createPortalOutlet(pane); + return new OverlayRef(portalOutlet, pane, config, this._ngZone, this._keyboardDispatcher); } /** - * Returns a position builder that can be used, via fluent API, + * Gets a position builder that can be used, via fluent API, * to construct and configure a position strategy. + * @returns An overlay position builder. */ position(): OverlayPositionBuilder { return this._positionBuilder; @@ -70,7 +78,7 @@ export class Overlay { * @returns Newly-created pane element */ private _createPaneElement(): HTMLElement { - let pane = document.createElement('div'); + const pane = this._document.createElement('div'); pane.id = `cdk-overlay-${nextUniqueId++}`; pane.classList.add('cdk-overlay-pane'); @@ -80,11 +88,12 @@ export class Overlay { } /** - * Create a DomPortalHost into which the overlay content can be loaded. - * @param pane The DOM element to turn into a portal host. - * @returns A portal host for the given DOM element. + * Create a DomPortalOutlet into which the overlay content can be loaded. + * @param pane The DOM element to turn into a portal outlet. + * @returns A portal outlet for the given DOM element. */ - private _createPortalHost(pane: HTMLElement): DomPortalHost { - return new DomPortalHost(pane, this._componentFactoryResolver, this._appRef, this._injector); + private _createPortalOutlet(pane: HTMLElement): DomPortalOutlet { + return new DomPortalOutlet(pane, this._componentFactoryResolver, this._appRef, this._injector); } + } diff --git a/src/cdk/overlay/position/connected-position-strategy.spec.ts b/src/cdk/overlay/position/connected-position-strategy.spec.ts index 0ba80be29cd5..b08b6ea53005 100644 --- a/src/cdk/overlay/position/connected-position-strategy.spec.ts +++ b/src/cdk/overlay/position/connected-position-strategy.spec.ts @@ -1,13 +1,17 @@ import {ElementRef} from '@angular/core'; import {TestBed, inject} from '@angular/core/testing'; -import {ConnectedPositionStrategy} from './connected-position-strategy'; -import {ViewportRuler, VIEWPORT_RULER_PROVIDER} from '@angular/cdk/scrolling'; import {OverlayPositionBuilder} from './overlay-position-builder'; -import {ConnectedOverlayPositionChange} from './connected-position'; -import {Scrollable} from '@angular/cdk/scrolling'; +import {CdkScrollable} from '@angular/cdk/scrolling'; import {Subscription} from 'rxjs/Subscription'; import {ScrollDispatchModule} from '@angular/cdk/scrolling'; -import {OverlayRef} from '../overlay-ref'; +import { + OverlayModule, + Overlay, + OverlayRef, + OverlayContainer, + ConnectedPositionStrategy, + ConnectedOverlayPositionChange, +} from '../index'; // Default width and height of the overlay and origin panels throughout these tests. @@ -19,17 +23,23 @@ const DEFAULT_WIDTH = 60; // for tests on CI (both SauceLabs and Browserstack). describe('ConnectedPositionStrategy', () => { + let positionBuilder: OverlayPositionBuilder; + let overlayContainer: OverlayContainer; + let overlayContainerElement: HTMLElement; + + beforeEach(() => { + TestBed.configureTestingModule({imports: [ScrollDispatchModule, OverlayModule]}); + + inject([Overlay, OverlayContainer], (overlay: Overlay, oc: OverlayContainer) => { + positionBuilder = overlay.position(); + overlayContainer = oc; + overlayContainerElement = oc.getContainerElement(); + })(); + }); - let viewportRuler: ViewportRuler; - - beforeEach(() => TestBed.configureTestingModule({ - imports: [ScrollDispatchModule], - providers: [VIEWPORT_RULER_PROVIDER] - })); - - beforeEach(inject([ViewportRuler], (_ruler: ViewportRuler) => { - viewportRuler = _ruler; - })); + afterEach(() => { + overlayContainer.ngOnDestroy(); + }); describe('with origin on document body', () => { const ORIGIN_HEIGHT = DEFAULT_HEIGHT; @@ -39,10 +49,8 @@ describe('ConnectedPositionStrategy', () => { let originElement: HTMLElement; let overlayElement: HTMLElement; - let overlayContainerElement: HTMLElement; let strategy: ConnectedPositionStrategy; let fakeElementRef: ElementRef; - let positionBuilder: OverlayPositionBuilder; let originRect: ClientRect | null; let originCenterX: number | null; @@ -51,19 +59,14 @@ describe('ConnectedPositionStrategy', () => { beforeEach(() => { // The origin and overlay elements need to be in the document body in order to have geometry. originElement = createPositionedBlockElement(); - overlayContainerElement = createOverlayContainer(); overlayElement = createPositionedBlockElement(); document.body.appendChild(originElement); - document.body.appendChild(overlayContainerElement); overlayContainerElement.appendChild(overlayElement); - - fakeElementRef = new FakeElementRef(originElement); - positionBuilder = new OverlayPositionBuilder(viewportRuler); + fakeElementRef = new ElementRef(originElement); }); afterEach(() => { document.body.removeChild(originElement); - document.body.removeChild(overlayContainerElement); // Reset the origin geometry after each test so we don't accidently keep state between tests. originRect = null; @@ -177,8 +180,6 @@ describe('ConnectedPositionStrategy', () => { }); it('should reposition the overlay if it would go off the bottom of the screen', () => { - positionBuilder = new OverlayPositionBuilder(viewportRuler); - originElement.style.bottom = '25px'; originElement.style.left = '200px'; originRect = originElement.getBoundingClientRect(); @@ -200,8 +201,6 @@ describe('ConnectedPositionStrategy', () => { }); it('should reposition the overlay if it would go off the right of the screen', () => { - positionBuilder = new OverlayPositionBuilder(viewportRuler); - originElement.style.top = '200px'; originElement.style.right = '25px'; originRect = originElement.getBoundingClientRect(); @@ -224,8 +223,6 @@ describe('ConnectedPositionStrategy', () => { }); it('should recalculate and set the last position with recalculateLastPosition()', () => { - positionBuilder = new OverlayPositionBuilder(viewportRuler); - // Push the trigger down so the overlay doesn't have room to open on the bottom. originElement.style.bottom = '25px'; originRect = originElement.getBoundingClientRect(); @@ -254,8 +251,6 @@ describe('ConnectedPositionStrategy', () => { }); it('should default to the initial position, if no positions fit in the viewport', () => { - positionBuilder = new OverlayPositionBuilder(viewportRuler); - // Make the origin element taller than the viewport. originElement.style.height = '1000px'; originElement.style.top = '0'; @@ -326,10 +321,33 @@ describe('ConnectedPositionStrategy', () => { expect(Math.floor(overlayRect.left)).toBe(Math.floor(originRect.left)); }); + it('should allow for the fallback positions to specify their own offsets', () => { + originElement.style.bottom = '0'; + originElement.style.left = '50%'; + originElement.style.position = 'fixed'; + originRect = originElement.getBoundingClientRect(); + strategy = positionBuilder + .connectedTo( + fakeElementRef, + {originX: 'start', originY: 'top'}, + {overlayX: 'start', overlayY: 'top'}) + .withFallbackPosition( + {originX: 'start', originY: 'top'}, + {overlayX: 'start', overlayY: 'bottom'}, + -100, -100); + + strategy.withOffsetY(50).withOffsetY(50); + strategy.attach(fakeOverlayRef(overlayElement)); + strategy.apply(); + + let overlayRect = overlayElement.getBoundingClientRect(); + expect(Math.floor(overlayRect.bottom)).toBe(Math.floor(originRect.top - 100)); + expect(Math.floor(overlayRect.left)).toBe(Math.floor(originRect.left - 100)); + }); + }); it('should emit onPositionChange event when position changes', () => { - positionBuilder = new OverlayPositionBuilder(viewportRuler); originElement.style.top = '200px'; originElement.style.right = '25px'; @@ -366,7 +384,6 @@ describe('ConnectedPositionStrategy', () => { }); it('should emit the onPositionChange event even if none of the positions fit', () => { - positionBuilder = new OverlayPositionBuilder(viewportRuler); originElement.style.bottom = '25px'; originElement.style.right = '25px'; @@ -390,8 +407,6 @@ describe('ConnectedPositionStrategy', () => { }); it('should pick the fallback position that shows the largest area of the element', () => { - positionBuilder = new OverlayPositionBuilder(viewportRuler); - originElement.style.top = '200px'; originElement.style.right = '25px'; originRect = originElement.getBoundingClientRect(); @@ -416,6 +431,28 @@ describe('ConnectedPositionStrategy', () => { expect(Math.floor(overlayRect.left)).toBe(Math.floor(originRect.left)); }); + it('should re-use the preferred position when re-applying while locked in', () => { + strategy = positionBuilder.connectedTo( + fakeElementRef, + {originX: 'end', originY: 'center'}, + {overlayX: 'start', overlayY: 'center'}) + .withLockedPosition(true) + .withFallbackPosition( + {originX: 'start', originY: 'bottom'}, + {overlayX: 'end', overlayY: 'top'}); + + const recalcSpy = spyOn(strategy, 'recalculateLastPosition'); + + strategy.attach(fakeOverlayRef(overlayElement)); + strategy.apply(); + + expect(recalcSpy).not.toHaveBeenCalled(); + + strategy.apply(); + + expect(recalcSpy).toHaveBeenCalled(); + }); + /** * Run all tests for connecting the overlay to the origin such that first preferred * position does not go off-screen. We do this because there are several cases where we @@ -512,7 +549,6 @@ describe('ConnectedPositionStrategy', () => { describe('onPositionChange with scrollable view properties', () => { let overlayElement: HTMLElement; - let overlayContainerElement: HTMLElement; let strategy: ConnectedPositionStrategy; let scrollable: HTMLDivElement; @@ -522,9 +558,7 @@ describe('ConnectedPositionStrategy', () => { beforeEach(() => { // Set up the overlay - overlayContainerElement = createOverlayContainer(); overlayElement = createPositionedBlockElement(); - document.body.appendChild(overlayContainerElement); overlayContainerElement.appendChild(overlayElement); // Set up the origin @@ -537,15 +571,14 @@ describe('ConnectedPositionStrategy', () => { scrollable.appendChild(originElement); // Create a strategy with knowledge of the scrollable container - let positionBuilder = new OverlayPositionBuilder(viewportRuler); - let fakeElementRef = new FakeElementRef(originElement); + let fakeElementRef = new ElementRef(originElement); strategy = positionBuilder.connectedTo( fakeElementRef, {originX: 'start', originY: 'bottom'}, {overlayX: 'start', overlayY: 'top'}); strategy.withScrollableContainers([ - new Scrollable(new FakeElementRef(scrollable), null!, null!, null!)]); + new CdkScrollable(new ElementRef(scrollable), null!, null!)]); strategy.attach(fakeOverlayRef(overlayElement)); positionChangeHandler = jasmine.createSpy('positionChangeHandler'); onPositionChangeSubscription = strategy.onPositionChange.subscribe(positionChangeHandler); @@ -554,7 +587,6 @@ describe('ConnectedPositionStrategy', () => { afterEach(() => { onPositionChangeSubscription.unsubscribe(); document.body.removeChild(scrollable); - document.body.removeChild(overlayContainerElement); }); it('should not have origin or overlay clipped or out of view without scroll', () => { @@ -616,27 +648,20 @@ describe('ConnectedPositionStrategy', () => { describe('positioning properties', () => { let originElement: HTMLElement; let overlayElement: HTMLElement; - let overlayContainerElement: HTMLElement; let strategy: ConnectedPositionStrategy; let fakeElementRef: ElementRef; - let positionBuilder: OverlayPositionBuilder; beforeEach(() => { // The origin and overlay elements need to be in the document body in order to have geometry. originElement = createPositionedBlockElement(); - overlayContainerElement = createOverlayContainer(); overlayElement = createPositionedBlockElement(); document.body.appendChild(originElement); - document.body.appendChild(overlayContainerElement); overlayContainerElement.appendChild(overlayElement); - - fakeElementRef = new FakeElementRef(originElement); - positionBuilder = new OverlayPositionBuilder(viewportRuler); + fakeElementRef = new ElementRef(originElement); }); afterEach(() => { document.body.removeChild(originElement); - document.body.removeChild(overlayContainerElement); }); describe('in ltr', () => { @@ -744,13 +769,6 @@ function createBlockElement() { return element; } -/** Creates the wrapper for all of the overlays. */ -function createOverlayContainer() { - let element = document.createElement('div'); - element.classList.add('cdk-overlay-container'); - return element; -} - /** Creates an overflow container with a set height and width with margin. */ function createOverflowContainerElement() { let element = document.createElement('div'); @@ -762,12 +780,6 @@ function createOverflowContainerElement() { return element; } - -/** Fake implementation of ElementRef that is just a simple container for nativeElement. */ -class FakeElementRef implements ElementRef { - constructor(public nativeElement: HTMLElement) { } -} - function fakeOverlayRef(overlayElement: HTMLElement) { return {overlayElement} as OverlayRef; } diff --git a/src/cdk/overlay/position/connected-position-strategy.ts b/src/cdk/overlay/position/connected-position-strategy.ts index 101e8c23bdf3..6f106c2ade40 100644 --- a/src/cdk/overlay/position/connected-position-strategy.ts +++ b/src/cdk/overlay/position/connected-position-strategy.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -17,8 +17,9 @@ import { ScrollingVisibility, } from './connected-position'; import {Subject} from 'rxjs/Subject'; +import {Subscription} from 'rxjs/Subscription'; import {Observable} from 'rxjs/Observable'; -import {Scrollable} from '@angular/cdk/scrolling'; +import {CdkScrollable} from '@angular/cdk/scrolling'; import {isElementScrolledOutsideView, isElementClippedByScrolling} from './scroll-clip'; import {OverlayRef} from '../overlay-ref'; @@ -35,6 +36,7 @@ export class ConnectedPositionStrategy implements PositionStrategy { /** The overlay to which this strategy is attached. */ private _overlayRef: OverlayRef; + /** Layout direction of the position strategy. */ private _dir = 'ltr'; /** The offset in pixels for the overlay connection point on the x-axis */ @@ -44,7 +46,10 @@ export class ConnectedPositionStrategy implements PositionStrategy { private _offsetY: number = 0; /** The Scrollable containers used to check scrollable view properties on position change. */ - private scrollables: Scrollable[] = []; + private scrollables: CdkScrollable[] = []; + + /** Subscription to viewport resize events. */ + private _resizeSubscription = Subscription.EMPTY; /** Whether the we're dealing with an RTL context */ get _isRtl() { @@ -63,8 +68,13 @@ export class ConnectedPositionStrategy implements PositionStrategy { /** The last position to have been calculated as the best fit position. */ private _lastConnectedPosition: ConnectionPositionPair; - _onPositionChange: - Subject = new Subject(); + /** Whether the position strategy is applied currently. */ + private _applied = false; + + /** Whether the overlay position is locked. */ + private _positionLocked = false; + + private _onPositionChange = new Subject(); /** Emits an event when the connection point changes. */ get onPositionChange(): Observable { @@ -72,43 +82,64 @@ export class ConnectedPositionStrategy implements PositionStrategy { } constructor( + originPos: OriginConnectionPosition, + overlayPos: OverlayConnectionPosition, private _connectedTo: ElementRef, - private _originPos: OriginConnectionPosition, - private _overlayPos: OverlayConnectionPosition, - private _viewportRuler: ViewportRuler) { + private _viewportRuler: ViewportRuler, + private _document: any) { this._origin = this._connectedTo.nativeElement; - this.withFallbackPosition(_originPos, _overlayPos); + this.withFallbackPosition(originPos, overlayPos); } /** Ordered list of preferred positions, from most to least desirable. */ - get positions() { + get positions(): ConnectionPositionPair[] { return this._preferredPositions; } + /** Attach this position strategy to an overlay. */ attach(overlayRef: OverlayRef): void { this._overlayRef = overlayRef; this._pane = overlayRef.overlayElement; + this._resizeSubscription.unsubscribe(); + this._resizeSubscription = this._viewportRuler.change().subscribe(() => this.apply()); + } + + /** Disposes all resources used by the position strategy. */ + dispose() { + this._applied = false; + this._resizeSubscription.unsubscribe(); } - /** Performs any cleanup after the element is destroyed. */ - dispose() { } + /** @docs-private */ + detach() { + this._applied = false; + this._resizeSubscription.unsubscribe(); + } /** * Updates the position of the overlay element, using whichever preferred position relative * to the origin fits on-screen. * @docs-private - * - * @returns Resolves when the styles have been applied. */ apply(): void { + // If the position has been applied already (e.g. when the overlay was opened) and the + // consumer opted into locking in the position, re-use the old position, in order to + // prevent the overlay from jumping around. + if (this._applied && this._positionLocked && this._lastConnectedPosition) { + this.recalculateLastPosition(); + return; + } + + this._applied = true; + // We need the bounding rects for the origin and the overlay to determine how to position // the overlay relative to the origin. const element = this._pane; const originRect = this._origin.getBoundingClientRect(); const overlayRect = element.getBoundingClientRect(); - // We use the viewport rect to determine whether a position would go off-screen. - const viewportRect = this._viewportRuler.getViewportRect(); + // We use the viewport size to determine whether a position would go off-screen. + const viewportSize = this._viewportRuler.getViewportSize(); // Fallback point if none of the fallbacks fit into the viewport. let fallbackPoint: OverlayPoint | undefined; @@ -120,7 +151,7 @@ export class ConnectedPositionStrategy implements PositionStrategy { // Get the (x, y) point of connection on the origin, and then use that to get the // (top, left) coordinate for the overlay at `pos`. let originPoint = this._getOriginConnectionPoint(originRect, pos); - let overlayPoint = this._getOverlayPoint(originPoint, overlayRect, viewportRect, pos); + let overlayPoint = this._getOverlayPoint(originPoint, overlayRect, viewportSize, pos); // If the overlay in the calculated position fits on-screen, put it there and we're done. if (overlayPoint.fitsInViewport) { @@ -142,18 +173,23 @@ export class ConnectedPositionStrategy implements PositionStrategy { } /** - * This re-aligns the overlay element with the trigger in its last calculated position, + * Re-positions the overlay element with the trigger in its last calculated position, * even if a position higher in the "preferred positions" list would now fit. This * allows one to re-align the panel without changing the orientation of the panel. */ recalculateLastPosition(): void { + // If the overlay has never been positioned before, do nothing. + if (!this._lastConnectedPosition) { + return; + } + const originRect = this._origin.getBoundingClientRect(); const overlayRect = this._pane.getBoundingClientRect(); - const viewportRect = this._viewportRuler.getViewportRect(); + const viewportSize = this._viewportRuler.getViewportSize(); const lastPosition = this._lastConnectedPosition || this._preferredPositions[0]; let originPoint = this._getOriginConnectionPoint(originRect, lastPosition); - let overlayPoint = this._getOverlayPoint(originPoint, overlayRect, viewportRect, lastPosition); + let overlayPoint = this._getOverlayPoint(originPoint, overlayRect, viewportSize, lastPosition); this._setElementPosition(this._pane, overlayRect, overlayPoint, lastPosition); } @@ -162,7 +198,7 @@ export class ConnectedPositionStrategy implements PositionStrategy { * on reposition we can evaluate if it or the overlay has been clipped or outside view. Every * Scrollable must be an ancestor element of the strategy's origin element. */ - withScrollableContainers(scrollables: Scrollable[]) { + withScrollableContainers(scrollables: CdkScrollable[]) { this.scrollables = scrollables; } @@ -173,8 +209,12 @@ export class ConnectedPositionStrategy implements PositionStrategy { */ withFallbackPosition( originPos: OriginConnectionPosition, - overlayPos: OverlayConnectionPosition): this { - this._preferredPositions.push(new ConnectionPositionPair(originPos, overlayPos)); + overlayPos: OverlayConnectionPosition, + offsetX?: number, + offsetY?: number): this { + + const position = new ConnectionPositionPair(originPos, overlayPos, offsetX, offsetY); + this._preferredPositions.push(position); return this; } @@ -205,6 +245,17 @@ export class ConnectedPositionStrategy implements PositionStrategy { return this; } + /** + * Sets whether the overlay's position should be locked in after it is positioned + * initially. When an overlay is locked in, it won't attempt to reposition itself + * when the position is re-applied (e.g. when the user scrolls away). + * @param isLocked Whether the overlay should locked in. + */ + withLockedPosition(isLocked: boolean): this { + this._positionLocked = isLocked; + return this; + } + /** * Gets the horizontal (x) "start" dimension based on whether the overlay is in an RTL context. * @param rect @@ -257,7 +308,7 @@ export class ConnectedPositionStrategy implements PositionStrategy { private _getOverlayPoint( originPoint: Point, overlayRect: ClientRect, - viewportRect: ClientRect, + viewportSize: {width: number; height: number}, pos: ConnectionPositionPair): OverlayPoint { // Calculate the (overlayStartX, overlayStartY), the start of the potential overlay position // relative to the origin point. @@ -277,15 +328,19 @@ export class ConnectedPositionStrategy implements PositionStrategy { overlayStartY = pos.overlayY == 'top' ? 0 : -overlayRect.height; } + // The (x, y) offsets of the overlay based on the current position. + let offsetX = typeof pos.offsetX === 'undefined' ? this._offsetX : pos.offsetX; + let offsetY = typeof pos.offsetY === 'undefined' ? this._offsetY : pos.offsetY; + // The (x, y) coordinates of the overlay. - let x = originPoint.x + overlayStartX + this._offsetX; - let y = originPoint.y + overlayStartY + this._offsetY; + let x = originPoint.x + overlayStartX + offsetX; + let y = originPoint.y + overlayStartY + offsetY; // How much the overlay would overflow at this position, on each side. let leftOverflow = 0 - x; - let rightOverflow = (x + overlayRect.width) - viewportRect.width; + let rightOverflow = (x + overlayRect.width) - viewportSize.width; let topOverflow = 0 - y; - let bottomOverflow = (y + overlayRect.height) - viewportRect.height; + let bottomOverflow = (y + overlayRect.height) - viewportSize.height; // Visible parts of the element on each axis. let visibleWidth = this._subtractOverflows(overlayRect.width, leftOverflow, rightOverflow); @@ -331,7 +386,7 @@ export class ConnectedPositionStrategy implements PositionStrategy { // from the bottom of the viewport rather than the top. let y = verticalStyleProperty === 'top' ? overlayPoint.y : - document.documentElement.clientHeight - (overlayPoint.y + overlayRect.height); + this._document.documentElement.clientHeight - (overlayPoint.y + overlayRect.height); // We want to set either `left` or `right` based on whether the overlay wants to appear "before" // or "after" the origin, which determines the direction in which the element will expand. @@ -348,7 +403,7 @@ export class ConnectedPositionStrategy implements PositionStrategy { // from the right edge of the viewport rather than the left edge. let x = horizontalStyleProperty === 'left' ? overlayPoint.x : - document.documentElement.clientWidth - (overlayPoint.x + overlayRect.width); + this._document.documentElement.clientWidth - (overlayPoint.x + overlayRect.width); // Reset any existing styles. This is necessary in case the preferred position has diff --git a/src/cdk/overlay/position/connected-position.ts b/src/cdk/overlay/position/connected-position.ts index e9affad91dba..0b8d8a9c6173 100644 --- a/src/cdk/overlay/position/connected-position.ts +++ b/src/cdk/overlay/position/connected-position.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -28,12 +28,21 @@ export interface OverlayConnectionPosition { /** The points of the origin element and the overlay element to connect. */ export class ConnectionPositionPair { + /** X-axis attachment point for connected overlay origin. Can be 'start', 'end', or 'center'. */ originX: HorizontalConnectionPos; + /** Y-axis attachment point for connected overlay origin. Can be 'top', 'bottom', or 'center'. */ originY: VerticalConnectionPos; + /** X-axis attachment point for connected overlay. Can be 'start', 'end', or 'center'. */ overlayX: HorizontalConnectionPos; + /** Y-axis attachment point for connected overlay. Can be 'top', 'bottom', or 'center'. */ overlayY: VerticalConnectionPos; - constructor(origin: OriginConnectionPosition, overlay: OverlayConnectionPosition) { + constructor( + origin: OriginConnectionPosition, + overlay: OverlayConnectionPosition, + public offsetX?: number, + public offsetY?: number) { + this.originX = origin.originX; this.originY = origin.originY; this.overlayX = overlay.overlayX; @@ -63,6 +72,8 @@ export class ConnectionPositionPair { * | Scrollable | * | | * -------------------------- + * + * @docs-private */ export class ScrollingVisibility { isOriginClipped: boolean; @@ -73,6 +84,9 @@ export class ScrollingVisibility { /** The change event emitted by the strategy when a fallback position is used. */ export class ConnectedOverlayPositionChange { - constructor(public connectionPair: ConnectionPositionPair, - @Optional() public scrollableViewProperties: ScrollingVisibility) {} + constructor( + /** The position used as a result of this change. */ + public connectionPair: ConnectionPositionPair, + /** @docs-private */ + @Optional() public scrollableViewProperties: ScrollingVisibility) {} } diff --git a/src/cdk/overlay/position/global-position-strategy.spec.ts b/src/cdk/overlay/position/global-position-strategy.spec.ts index 104757e17838..4c804683993c 100644 --- a/src/cdk/overlay/position/global-position-strategy.spec.ts +++ b/src/cdk/overlay/position/global-position-strategy.spec.ts @@ -1,6 +1,5 @@ -import {fakeAsync, flushMicrotasks, inject} from '@angular/core/testing'; -import {GlobalPositionStrategy} from './global-position-strategy'; -import {OverlayRef} from '../overlay-ref'; +import {TestBed, inject} from '@angular/core/testing'; +import {OverlayModule, Overlay, OverlayRef, GlobalPositionStrategy} from '../index'; describe('GlobalPositonStrategy', () => { @@ -8,8 +7,13 @@ describe('GlobalPositonStrategy', () => { let strategy: GlobalPositionStrategy; beforeEach(() => { + TestBed.configureTestingModule({imports: [OverlayModule]}); + + inject([Overlay], (overlay: Overlay) => { + strategy = overlay.position().global(); + })(); + element = document.createElement('div'); - strategy = new GlobalPositionStrategy(); document.body.appendChild(element); strategy.attach({overlayElement: element} as OverlayRef); }); @@ -19,11 +23,9 @@ describe('GlobalPositonStrategy', () => { strategy.dispose(); }); - it('should position the element to the (top, left) with an offset', fakeAsyncTest(() => { + it('should position the element to the (top, left) with an offset', () => { strategy.top('10px').left('40px').apply(); - flushMicrotasks(); - let elementStyle = element.style; let parentStyle = (element.parentNode as HTMLElement).style; @@ -34,13 +36,11 @@ describe('GlobalPositonStrategy', () => { expect(parentStyle.justifyContent).toBe('flex-start'); expect(parentStyle.alignItems).toBe('flex-start'); - })); + }); - it('should position the element to the (bottom, right) with an offset', fakeAsyncTest(() => { + it('should position the element to the (bottom, right) with an offset', () => { strategy.bottom('70px').right('15em').apply(); - flushMicrotasks(); - let elementStyle = element.style; let parentStyle = (element.parentNode as HTMLElement).style; @@ -51,14 +51,11 @@ describe('GlobalPositonStrategy', () => { expect(parentStyle.justifyContent).toBe('flex-end'); expect(parentStyle.alignItems).toBe('flex-end'); - })); + }); - it('should overwrite previously applied positioning', fakeAsyncTest(() => { + it('should overwrite previously applied positioning', () => { strategy.centerHorizontally().centerVertically().apply(); - flushMicrotasks(); - strategy.top('10px').left('40%').apply(); - flushMicrotasks(); let elementStyle = element.style; let parentStyle = (element.parentNode as HTMLElement).style; @@ -73,8 +70,6 @@ describe('GlobalPositonStrategy', () => { strategy.bottom('70px').right('15em').apply(); - flushMicrotasks(); - expect(element.style.marginTop).toBe(''); expect(element.style.marginLeft).toBe(''); expect(element.style.marginBottom).toBe('70px'); @@ -82,24 +77,20 @@ describe('GlobalPositonStrategy', () => { expect(parentStyle.justifyContent).toBe('flex-end'); expect(parentStyle.alignItems).toBe('flex-end'); - })); + }); - it('should center the element', fakeAsyncTest(() => { + it('should center the element', () => { strategy.centerHorizontally().centerVertically().apply(); - flushMicrotasks(); - let parentStyle = (element.parentNode as HTMLElement).style; expect(parentStyle.justifyContent).toBe('center'); expect(parentStyle.alignItems).toBe('center'); - })); + }); - it('should center the element with an offset', fakeAsyncTest(() => { + it('should center the element with an offset', () => { strategy.centerHorizontally('10px').centerVertically('15px').apply(); - flushMicrotasks(); - let elementStyle = element.style; let parentStyle = (element.parentNode as HTMLElement).style; @@ -108,74 +99,56 @@ describe('GlobalPositonStrategy', () => { expect(parentStyle.justifyContent).toBe('center'); expect(parentStyle.alignItems).toBe('center'); - })); + }); - it('should make the element position: static', fakeAsyncTest(() => { + it('should make the element position: static', () => { strategy.apply(); - flushMicrotasks(); - expect(element.style.position).toBe('static'); - })); + }); - it('should wrap the element in a `cdk-global-overlay-wrapper`', fakeAsyncTest(() => { + it('should wrap the element in a `cdk-global-overlay-wrapper`', () => { strategy.apply(); - flushMicrotasks(); - let parent = element.parentNode as HTMLElement; expect(parent.classList.contains('cdk-global-overlay-wrapper')).toBe(true); - })); + }); - it('should remove the parent wrapper from the DOM', fakeAsync(() => { + it('should remove the parent wrapper from the DOM', () => { strategy.apply(); - flushMicrotasks(); - expect(document.body.contains(element.parentNode!)).toBe(true); strategy.dispose(); expect(document.body.contains(element.parentNode!)).toBe(false); - })); + }); - it('should set the element width', fakeAsync(() => { + it('should set the element width', () => { strategy.width('100px').apply(); - flushMicrotasks(); - expect(element.style.width).toBe('100px'); - })); + }); - it('should set the element height', fakeAsync(() => { + it('should set the element height', () => { strategy.height('100px').apply(); - flushMicrotasks(); - expect(element.style.height).toBe('100px'); - })); + }); - it('should reset the horizontal position and offset when the width is 100%', fakeAsync(() => { + it('should reset the horizontal position and offset when the width is 100%', () => { strategy.centerHorizontally().width('100%').apply(); - flushMicrotasks(); - expect(element.style.marginLeft).toBe('0px'); expect((element.parentNode as HTMLElement).style.justifyContent).toBe('flex-start'); - })); + }); - it('should reset the vertical position and offset when the height is 100%', fakeAsync(() => { + it('should reset the vertical position and offset when the height is 100%', () => { strategy.centerVertically().height('100%').apply(); - flushMicrotasks(); - expect(element.style.marginTop).toBe('0px'); expect((element.parentNode as HTMLElement).style.alignItems).toBe('flex-start'); - })); + }); }); - -function fakeAsyncTest(fn: () => void) { - return inject([], fakeAsync(fn)); -} diff --git a/src/cdk/overlay/position/global-position-strategy.ts b/src/cdk/overlay/position/global-position-strategy.ts index 69bcf5338576..d5b3fce50105 100644 --- a/src/cdk/overlay/position/global-position-strategy.ts +++ b/src/cdk/overlay/position/global-position-strategy.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -33,6 +33,8 @@ export class GlobalPositionStrategy implements PositionStrategy { /* A lazily-created wrapper for the overlay element that is used as a flex container. */ private _wrapper: HTMLElement | null = null; + constructor(private _document: any) {} + attach(overlayRef: OverlayRef): void { this._overlayRef = overlayRef; } @@ -41,7 +43,7 @@ export class GlobalPositionStrategy implements PositionStrategy { * Sets the top position of the overlay. Clears any previously set vertical position. * @param value New top offset. */ - top(value = ''): this { + top(value: string = ''): this { this._bottomOffset = ''; this._topOffset = value; this._alignItems = 'flex-start'; @@ -52,7 +54,7 @@ export class GlobalPositionStrategy implements PositionStrategy { * Sets the left position of the overlay. Clears any previously set horizontal position. * @param value New left offset. */ - left(value = ''): this { + left(value: string = ''): this { this._rightOffset = ''; this._leftOffset = value; this._justifyContent = 'flex-start'; @@ -63,7 +65,7 @@ export class GlobalPositionStrategy implements PositionStrategy { * Sets the bottom position of the overlay. Clears any previously set vertical position. * @param value New bottom offset. */ - bottom(value = ''): this { + bottom(value: string = ''): this { this._topOffset = ''; this._bottomOffset = value; this._alignItems = 'flex-end'; @@ -74,7 +76,7 @@ export class GlobalPositionStrategy implements PositionStrategy { * Sets the right position of the overlay. Clears any previously set horizontal position. * @param value New right offset. */ - right(value = ''): this { + right(value: string = ''): this { this._leftOffset = ''; this._rightOffset = value; this._justifyContent = 'flex-end'; @@ -85,7 +87,7 @@ export class GlobalPositionStrategy implements PositionStrategy { * Sets the overlay width and clears any previously set width. * @param value New width for the overlay */ - width(value = ''): this { + width(value: string = ''): this { this._width = value; // When the width is 100%, we should reset the `left` and the offset, @@ -101,7 +103,7 @@ export class GlobalPositionStrategy implements PositionStrategy { * Sets the overlay height and clears any previously set height. * @param value New height for the overlay */ - height(value = ''): this { + height(value: string = ''): this { this._height = value; // When the height is 100%, we should reset the `top` and the offset, @@ -119,7 +121,7 @@ export class GlobalPositionStrategy implements PositionStrategy { * * @param offset Overlay offset from the horizontal center. */ - centerHorizontally(offset = ''): this { + centerHorizontally(offset: string = ''): this { this.left(offset); this._justifyContent = 'center'; return this; @@ -131,7 +133,7 @@ export class GlobalPositionStrategy implements PositionStrategy { * * @param offset Overlay offset from the vertical center. */ - centerVertically(offset = ''): this { + centerVertically(offset: string = ''): this { this.top(offset); this._alignItems = 'center'; return this; @@ -147,10 +149,10 @@ export class GlobalPositionStrategy implements PositionStrategy { const element = this._overlayRef.overlayElement; if (!this._wrapper && element.parentNode) { - this._wrapper = document.createElement('div'); - this._wrapper.classList.add('cdk-global-overlay-wrapper'); - element.parentNode.insertBefore(this._wrapper, element); - this._wrapper.appendChild(element); + this._wrapper = this._document.createElement('div'); + this._wrapper!.classList.add('cdk-global-overlay-wrapper'); + element.parentNode.insertBefore(this._wrapper!, element); + this._wrapper!.appendChild(element); } let styles = element.style; diff --git a/src/cdk/overlay/position/overlay-position-builder.ts b/src/cdk/overlay/position/overlay-position-builder.ts index 1ce90297093d..19059e4ac6ca 100644 --- a/src/cdk/overlay/position/overlay-position-builder.ts +++ b/src/cdk/overlay/position/overlay-position-builder.ts @@ -1,29 +1,30 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import {ElementRef, Injectable} from '@angular/core'; +import {ElementRef, Injectable, Inject} from '@angular/core'; import {ViewportRuler} from '@angular/cdk/scrolling'; import {ConnectedPositionStrategy} from './connected-position-strategy'; import {GlobalPositionStrategy} from './global-position-strategy'; import {OverlayConnectionPosition, OriginConnectionPosition} from './connected-position'; - +import {DOCUMENT} from '@angular/common'; /** Builder for overlay position strategy. */ @Injectable() export class OverlayPositionBuilder { - constructor(private _viewportRuler: ViewportRuler) { } + constructor(private _viewportRuler: ViewportRuler, + @Inject(DOCUMENT) private _document: any) { } /** * Creates a global position strategy. */ global(): GlobalPositionStrategy { - return new GlobalPositionStrategy(); + return new GlobalPositionStrategy(this._document); } /** @@ -36,6 +37,8 @@ export class OverlayPositionBuilder { elementRef: ElementRef, originPos: OriginConnectionPosition, overlayPos: OverlayConnectionPosition): ConnectedPositionStrategy { - return new ConnectedPositionStrategy(elementRef, originPos, overlayPos, this._viewportRuler); + + return new ConnectedPositionStrategy(originPos, overlayPos, elementRef, + this._viewportRuler, this._document); } } diff --git a/src/cdk/overlay/position/position-strategy.ts b/src/cdk/overlay/position/position-strategy.ts index d848f4f4c51c..1b60b9ec4f97 100644 --- a/src/cdk/overlay/position/position-strategy.ts +++ b/src/cdk/overlay/position/position-strategy.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -18,6 +18,9 @@ export interface PositionStrategy { /** Updates the position of the overlay element. */ apply(): void; + /** Called when the overlay is detached. */ + detach?(): void; + /** Cleans up any DOM modifications made by the position strategy, if necessary. */ dispose(): void; } diff --git a/src/cdk/overlay/position/scroll-clip.ts b/src/cdk/overlay/position/scroll-clip.ts index 07b2a20d9dc1..d074ce513c70 100644 --- a/src/cdk/overlay/position/scroll-clip.ts +++ b/src/cdk/overlay/position/scroll-clip.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license diff --git a/src/cdk/overlay/public-api.ts b/src/cdk/overlay/public-api.ts new file mode 100644 index 000000000000..cfed88a95159 --- /dev/null +++ b/src/cdk/overlay/public-api.ts @@ -0,0 +1,32 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +export * from './overlay-config'; +export * from './position/connected-position'; +export * from './scroll/index'; +export * from './overlay-module'; +export {Overlay} from './overlay'; +export {OverlayContainer} from './overlay-container'; +export {CdkOverlayOrigin, CdkConnectedOverlay} from './overlay-directives'; +export {FullscreenOverlayContainer} from './fullscreen-overlay-container'; +export {OverlayRef} from './overlay-ref'; +export {ViewportRuler} from '@angular/cdk/scrolling'; +export {ComponentType} from '@angular/cdk/portal'; +export {OverlayKeyboardDispatcher} from './keyboard/overlay-keyboard-dispatcher'; + +// Export pre-defined position strategies and interface to build custom ones. +export {PositionStrategy} from './position/position-strategy'; +export {GlobalPositionStrategy} from './position/global-position-strategy'; +export {ConnectedPositionStrategy} from './position/connected-position-strategy'; +export {VIEWPORT_RULER_PROVIDER} from '@angular/cdk/scrolling'; + +/** @deprecated Use CdkConnectedOverlay */ +export {CdkConnectedOverlay as ConnectedOverlayDirective} from './overlay-directives'; + +/** @deprecated Use CdkOverlayOrigin */ +export {CdkOverlayOrigin as OverlayOrigin} from './overlay-directives'; diff --git a/src/cdk/overlay/public_api.ts b/src/cdk/overlay/public_api.ts deleted file mode 100644 index f376b761e4ab..000000000000 --- a/src/cdk/overlay/public_api.ts +++ /dev/null @@ -1,55 +0,0 @@ -/** - * @license - * Copyright Google Inc. All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ -import {NgModule, Provider} from '@angular/core'; -import {PortalModule} from '@angular/cdk/portal'; -import {Overlay} from './overlay'; -import {ScrollDispatchModule, VIEWPORT_RULER_PROVIDER} from '@angular/cdk/scrolling'; -import { - ConnectedOverlayDirective, - MD_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER, - OverlayOrigin, -} from './overlay-directives'; -import {OverlayPositionBuilder} from './position/overlay-position-builder'; -import {OVERLAY_CONTAINER_PROVIDER} from './overlay-container'; -import {ScrollStrategyOptions} from './scroll/scroll-strategy-options'; - - -export const OVERLAY_PROVIDERS: Provider[] = [ - Overlay, - OverlayPositionBuilder, - VIEWPORT_RULER_PROVIDER, - OVERLAY_CONTAINER_PROVIDER, - MD_CONNECTED_OVERLAY_SCROLL_STRATEGY_PROVIDER, -]; - -@NgModule({ - imports: [PortalModule, ScrollDispatchModule], - exports: [ConnectedOverlayDirective, OverlayOrigin, ScrollDispatchModule], - declarations: [ConnectedOverlayDirective, OverlayOrigin], - providers: [OVERLAY_PROVIDERS, ScrollStrategyOptions], -}) -export class OverlayModule {} - - -export {Overlay} from './overlay'; -export {OverlayContainer} from './overlay-container'; -export {FullscreenOverlayContainer} from './fullscreen-overlay-container'; -export {OverlayRef} from './overlay-ref'; -export {OverlayState} from './overlay-state'; -export {ConnectedOverlayDirective, OverlayOrigin} from './overlay-directives'; -export {ViewportRuler} from '@angular/cdk/scrolling'; -export {ComponentType} from '@angular/cdk/portal'; - -export * from './position/connected-position'; -export * from './scroll/index'; - -// Export pre-defined position strategies and interface to build custom ones. -export {PositionStrategy} from './position/position-strategy'; -export {GlobalPositionStrategy} from './position/global-position-strategy'; -export {ConnectedPositionStrategy} from './position/connected-position-strategy'; -export {VIEWPORT_RULER_PROVIDER} from '@angular/cdk/scrolling'; diff --git a/src/cdk/overlay/scroll/block-scroll-strategy.spec.ts b/src/cdk/overlay/scroll/block-scroll-strategy.spec.ts index 2be0acd12f7a..60a459609855 100644 --- a/src/cdk/overlay/scroll/block-scroll-strategy.spec.ts +++ b/src/cdk/overlay/scroll/block-scroll-strategy.spec.ts @@ -3,7 +3,7 @@ import {async, inject, TestBed} from '@angular/core/testing'; import {ComponentPortal, PortalModule} from '@angular/cdk/portal'; import {Platform} from '@angular/cdk/platform'; import {ViewportRuler} from '@angular/cdk/scrolling'; -import {Overlay, OverlayContainer, OverlayModule, OverlayRef, OverlayState} from '../index'; +import {Overlay, OverlayContainer, OverlayModule, OverlayRef, OverlayConfig} from '../index'; describe('BlockScrollStrategy', () => { @@ -23,10 +23,9 @@ describe('BlockScrollStrategy', () => { })); beforeEach(inject([Overlay, ViewportRuler], (overlay: Overlay, viewportRuler: ViewportRuler) => { - let overlayState = new OverlayState(); + let overlayConfig = new OverlayConfig({scrollStrategy: overlay.scrollStrategies.block()}); - overlayState.scrollStrategy = overlay.scrollStrategies.block(); - overlayRef = overlay.create(overlayState); + overlayRef = overlay.create(overlayConfig); componentPortal = new ComponentPortal(FocacciaMsg); viewport = viewportRuler; @@ -40,12 +39,12 @@ describe('BlockScrollStrategy', () => { afterEach(inject([OverlayContainer], (container: OverlayContainer) => { overlayRef.dispose(); document.body.removeChild(forceScrollElement); - setScrollPosition(0, 0); + window.scroll(0, 0); container.getContainerElement().parentNode!.removeChild(container.getContainerElement()); })); it('should toggle scroll blocking along the y axis', skipIOS(() => { - setScrollPosition(0, 100); + window.scroll(0, 100); expect(viewport.getViewportScrollPosition().top) .toBe(100, 'Expected viewport to be scrollable initially.'); @@ -53,7 +52,7 @@ describe('BlockScrollStrategy', () => { expect(document.documentElement.style.top) .toBe('-100px', 'Expected element to be offset by the previous scroll amount.'); - setScrollPosition(0, 300); + window.scroll(0, 300); expect(viewport.getViewportScrollPosition().top) .toBe(100, 'Expected the viewport not to scroll.'); @@ -61,7 +60,7 @@ describe('BlockScrollStrategy', () => { expect(viewport.getViewportScrollPosition().top) .toBe(100, 'Expected old scroll position to have bee restored after disabling.'); - setScrollPosition(0, 300); + window.scroll(0, 300); expect(viewport.getViewportScrollPosition().top) .toBe(300, 'Expected user to be able to scroll after disabling.'); })); @@ -71,7 +70,7 @@ describe('BlockScrollStrategy', () => { forceScrollElement.style.height = '100px'; forceScrollElement.style.width = '3000px'; - setScrollPosition(100, 0); + window.scroll(100, 0); expect(viewport.getViewportScrollPosition().left) .toBe(100, 'Expected viewport to be scrollable initially.'); @@ -79,7 +78,7 @@ describe('BlockScrollStrategy', () => { expect(document.documentElement.style.left) .toBe('-100px', 'Expected element to be offset by the previous scroll amount.'); - setScrollPosition(300, 0); + window.scroll(300, 0); expect(viewport.getViewportScrollPosition().left) .toBe(100, 'Expected the viewport not to scroll.'); @@ -87,7 +86,7 @@ describe('BlockScrollStrategy', () => { expect(viewport.getViewportScrollPosition().left) .toBe(100, 'Expected old scroll position to have bee restored after disabling.'); - setScrollPosition(300, 0); + window.scroll(300, 0); expect(viewport.getViewportScrollPosition().left) .toBe(300, 'Expected user to be able to scroll after disabling.'); })); @@ -137,13 +136,33 @@ describe('BlockScrollStrategy', () => { expect(document.documentElement.getBoundingClientRect().width).toBe(previousContentWidth); }); + it('should not clobber user-defined scroll-behavior', skipIOS(() => { + const root = document.documentElement; + const body = document.body; + + root.style['scrollBehavior'] = body.style['scrollBehavior'] = 'smooth'; + + // Get the value via the style declaration in order to + // handle browsers that don't support the property yet. + const initialRootValue = root.style['scrollBehavior']; + const initialBodyValue = root.style['scrollBehavior']; + + overlayRef.attach(componentPortal); + overlayRef.detach(); + + expect(root.style['scrollBehavior']).toBe(initialRootValue); + expect(body.style['scrollBehavior']).toBe(initialBodyValue); + + // Avoid bleeding styles into other tests. + root.style['scrollBehavior'] = body.style['scrollBehavior'] = ''; + })); + /** * Skips the specified test, if it is being executed on iOS. This is necessary, because * programmatic scrolling inside the Karma iframe doesn't work on iOS, which renders these * tests unusable. For example, something as basic as the following won't work: * ``` * window.scroll(0, 100); - * viewport._cacheViewportGeometry(); * expect(viewport.getViewportScrollPosition().top).toBe(100); * ``` * @param spec Test to be executed or skipped. @@ -156,16 +175,6 @@ describe('BlockScrollStrategy', () => { }; } - /** - * Scrolls the viewport and clears the cache. - * @param x Amount to scroll along the x axis. - * @param y Amount to scroll along the y axis. - */ - function setScrollPosition(x: number, y: number) { - window.scroll(x, y); - viewport._cacheViewportGeometry(); - } - }); diff --git a/src/cdk/overlay/scroll/block-scroll-strategy.ts b/src/cdk/overlay/scroll/block-scroll-strategy.ts index 3960e7604912..68e74f04762e 100644 --- a/src/cdk/overlay/scroll/block-scroll-strategy.ts +++ b/src/cdk/overlay/scroll/block-scroll-strategy.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -19,8 +19,10 @@ export class BlockScrollStrategy implements ScrollStrategy { constructor(private _viewportRuler: ViewportRuler) { } + /** Attaches this scroll strategy to an overlay. */ attach() { } + /** Blocks page-level scroll while the attached overlay is open. */ enable() { if (this._canBeEnabled()) { const root = document.documentElement; @@ -40,13 +42,28 @@ export class BlockScrollStrategy implements ScrollStrategy { } } + /** Unblocks page-level scroll while the attached overlay is open. */ disable() { if (this._isEnabled) { + const html = document.documentElement; + const body = document.body; + const previousHtmlScrollBehavior = html.style['scrollBehavior'] || ''; + const previousBodyScrollBehavior = body.style['scrollBehavior'] || ''; + this._isEnabled = false; - document.documentElement.style.left = this._previousHTMLStyles.left; - document.documentElement.style.top = this._previousHTMLStyles.top; - document.documentElement.classList.remove('cdk-global-scrollblock'); + + html.style.left = this._previousHTMLStyles.left; + html.style.top = this._previousHTMLStyles.top; + html.classList.remove('cdk-global-scrollblock'); + + // Disable user-defined smooth scrolling temporarily while we restore the scroll position. + // See https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-behavior + html.style['scrollBehavior'] = body.style['scrollBehavior'] = 'auto'; + window.scroll(this._previousScrollPosition.left, this._previousScrollPosition.top); + + html.style['scrollBehavior'] = previousHtmlScrollBehavior; + body.style['scrollBehavior'] = previousBodyScrollBehavior; } } @@ -59,7 +76,7 @@ export class BlockScrollStrategy implements ScrollStrategy { } const body = document.body; - const viewport = this._viewportRuler.getViewportRect(); + const viewport = this._viewportRuler.getViewportSize(); return body.scrollHeight > viewport.height || body.scrollWidth > viewport.width; } } diff --git a/src/cdk/overlay/scroll/close-scroll-strategy.spec.ts b/src/cdk/overlay/scroll/close-scroll-strategy.spec.ts index 2fc805efe390..503c795e4326 100644 --- a/src/cdk/overlay/scroll/close-scroll-strategy.spec.ts +++ b/src/cdk/overlay/scroll/close-scroll-strategy.spec.ts @@ -1,11 +1,11 @@ import {inject, TestBed, async} from '@angular/core/testing'; -import {NgModule, Component} from '@angular/core'; +import {NgModule, Component, NgZone} from '@angular/core'; import {Subject} from 'rxjs/Subject'; import {ComponentPortal, PortalModule} from '@angular/cdk/portal'; import {ScrollDispatcher} from '@angular/cdk/scrolling'; import { Overlay, - OverlayState, + OverlayConfig, OverlayRef, OverlayModule, OverlayContainer, @@ -21,11 +21,9 @@ describe('CloseScrollStrategy', () => { TestBed.configureTestingModule({ imports: [OverlayModule, PortalModule, OverlayTestModule], providers: [ - {provide: ScrollDispatcher, useFactory: () => { - return {scrolled: (_delay: number, callback: () => any) => { - return scrolledSubject.asObservable().subscribe(callback); - }}; - }} + {provide: ScrollDispatcher, useFactory: () => ({ + scrolled: () => scrolledSubject.asObservable() + })} ] }); @@ -33,9 +31,8 @@ describe('CloseScrollStrategy', () => { })); beforeEach(inject([Overlay], (overlay: Overlay) => { - let overlayState = new OverlayState(); - overlayState.scrollStrategy = overlay.scrollStrategies.close(); - overlayRef = overlay.create(overlayState); + let overlayConfig = new OverlayConfig({scrollStrategy: overlay.scrollStrategies.close()}); + overlayRef = overlay.create(overlayConfig); componentPortal = new ComponentPortal(MozarellaMsg); })); @@ -62,6 +59,17 @@ describe('CloseScrollStrategy', () => { expect(overlayRef.detach).not.toHaveBeenCalled(); }); + it('should detach inside the NgZone', () => { + const spy = jasmine.createSpy('detachment spy'); + const subscription = overlayRef.detachments().subscribe(() => spy(NgZone.isInAngularZone())); + + overlayRef.attach(componentPortal); + scrolledSubject.next(); + + expect(spy).toHaveBeenCalledWith(true); + subscription.unsubscribe(); + }); + }); diff --git a/src/cdk/overlay/scroll/close-scroll-strategy.ts b/src/cdk/overlay/scroll/close-scroll-strategy.ts index 7f6b6bf10d73..970e3afcebe5 100644 --- a/src/cdk/overlay/scroll/close-scroll-strategy.ts +++ b/src/cdk/overlay/scroll/close-scroll-strategy.ts @@ -1,12 +1,13 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import {ScrollStrategy, getMdScrollStrategyAlreadyAttachedError} from './scroll-strategy'; +import {NgZone} from '@angular/core'; +import {ScrollStrategy, getMatScrollStrategyAlreadyAttachedError} from './scroll-strategy'; import {OverlayRef} from '../overlay-ref'; import {Subscription} from 'rxjs/Subscription'; import {ScrollDispatcher} from '@angular/cdk/scrolling'; @@ -19,28 +20,33 @@ export class CloseScrollStrategy implements ScrollStrategy { private _scrollSubscription: Subscription|null = null; private _overlayRef: OverlayRef; - constructor(private _scrollDispatcher: ScrollDispatcher) { } + constructor(private _scrollDispatcher: ScrollDispatcher, private _ngZone: NgZone) { } + /** Attaches this scroll strategy to an overlay. */ attach(overlayRef: OverlayRef) { if (this._overlayRef) { - throw getMdScrollStrategyAlreadyAttachedError(); + throw getMatScrollStrategyAlreadyAttachedError(); } this._overlayRef = overlayRef; } + /** Enables the closing of the attached on scroll. */ enable() { if (!this._scrollSubscription) { - this._scrollSubscription = this._scrollDispatcher.scrolled(0, () => { - if (this._overlayRef.hasAttached()) { - this._overlayRef.detach(); - } - - this.disable(); + this._scrollSubscription = this._scrollDispatcher.scrolled(0).subscribe(() => { + this._ngZone.run(() => { + this.disable(); + + if (this._overlayRef.hasAttached()) { + this._overlayRef.detach(); + } + }); }); } } + /** Disables the closing the attached overlay on scroll. */ disable() { if (this._scrollSubscription) { this._scrollSubscription.unsubscribe(); diff --git a/src/cdk/overlay/scroll/index.ts b/src/cdk/overlay/scroll/index.ts index 1bd3b1b6fbc4..d4dc631d9be7 100644 --- a/src/cdk/overlay/scroll/index.ts +++ b/src/cdk/overlay/scroll/index.ts @@ -1,12 +1,12 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -export {Scrollable, ScrollDispatcher} from '@angular/cdk/scrolling'; +export {CdkScrollable, ScrollDispatcher} from '@angular/cdk/scrolling'; // Export pre-defined scroll strategies and interface to build custom ones. export {ScrollStrategy} from './scroll-strategy'; diff --git a/src/cdk/overlay/scroll/noop-scroll-strategy.ts b/src/cdk/overlay/scroll/noop-scroll-strategy.ts index 64a4bfb1276b..ab9baf6bd633 100644 --- a/src/cdk/overlay/scroll/noop-scroll-strategy.ts +++ b/src/cdk/overlay/scroll/noop-scroll-strategy.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -8,11 +8,12 @@ import {ScrollStrategy} from './scroll-strategy'; -/** - * Scroll strategy that doesn't do anything. - */ +/** Scroll strategy that doesn't do anything. */ export class NoopScrollStrategy implements ScrollStrategy { + /** Does nothing, as this scroll strategy is a no-op. */ enable() { } + /** Does nothing, as this scroll strategy is a no-op. */ disable() { } + /** Does nothing, as this scroll strategy is a no-op. */ attach() { } } diff --git a/src/cdk/overlay/scroll/reposition-scroll-strategy.spec.ts b/src/cdk/overlay/scroll/reposition-scroll-strategy.spec.ts index 01e60314262c..91f7e3a436a7 100644 --- a/src/cdk/overlay/scroll/reposition-scroll-strategy.spec.ts +++ b/src/cdk/overlay/scroll/reposition-scroll-strategy.spec.ts @@ -7,13 +7,14 @@ import { OverlayContainer, OverlayModule, OverlayRef, - OverlayState, + OverlayConfig, ScrollDispatcher, } from '../index'; describe('RepositionScrollStrategy', () => { let overlayRef: OverlayRef; + let overlay: Overlay; let componentPortal: ComponentPortal; let scrolledSubject = new Subject(); @@ -21,21 +22,17 @@ describe('RepositionScrollStrategy', () => { TestBed.configureTestingModule({ imports: [OverlayModule, PortalModule, OverlayTestModule], providers: [ - {provide: ScrollDispatcher, useFactory: () => { - return {scrolled: (_delay: number, callback: () => any) => { - return scrolledSubject.asObservable().subscribe(callback); - }}; - }} + {provide: ScrollDispatcher, useFactory: () => ({ + scrolled: () => scrolledSubject.asObservable() + })} ] }); TestBed.compileComponents(); })); - beforeEach(inject([Overlay], (overlay: Overlay) => { - let overlayState = new OverlayState(); - overlayState.scrollStrategy = overlay.scrollStrategies.reposition(); - overlayRef = overlay.create(overlayState); + beforeEach(inject([Overlay], (o: Overlay) => { + overlay = o; componentPortal = new ComponentPortal(PastaMsg); })); @@ -45,6 +42,11 @@ describe('RepositionScrollStrategy', () => { })); it('should update the overlay position when the page is scrolled', () => { + const overlayConfig = new OverlayConfig({ + scrollStrategy: overlay.scrollStrategies.reposition() + }); + + overlayRef = overlay.create(overlayConfig); overlayRef.attach(componentPortal); spyOn(overlayRef, 'updatePosition'); @@ -56,6 +58,11 @@ describe('RepositionScrollStrategy', () => { }); it('should not be updating the position after the overlay is detached', () => { + const overlayConfig = new OverlayConfig({ + scrollStrategy: overlay.scrollStrategies.reposition() + }); + + overlayRef = overlay.create(overlayConfig); overlayRef.attach(componentPortal); spyOn(overlayRef, 'updatePosition'); @@ -66,6 +73,11 @@ describe('RepositionScrollStrategy', () => { }); it('should not be updating the position after the overlay is destroyed', () => { + const overlayConfig = new OverlayConfig({ + scrollStrategy: overlay.scrollStrategies.reposition() + }); + + overlayRef = overlay.create(overlayConfig); overlayRef.attach(componentPortal); spyOn(overlayRef, 'updatePosition'); @@ -75,6 +87,30 @@ describe('RepositionScrollStrategy', () => { expect(overlayRef.updatePosition).not.toHaveBeenCalled(); }); + it('should be able to close the overlay once it is out of view', () => { + const overlayConfig = new OverlayConfig({ + scrollStrategy: overlay.scrollStrategies.reposition({ + autoClose: true + }) + }); + + overlayRef = overlay.create(overlayConfig); + overlayRef.attach(componentPortal); + spyOn(overlayRef, 'updatePosition'); + spyOn(overlayRef, 'detach'); + spyOn(overlayRef.overlayElement, 'getBoundingClientRect').and.returnValue({ + top: -1000, + bottom: -900, + left: 0, + right: 100, + width: 100, + height: 100 + }); + + scrolledSubject.next(); + expect(overlayRef.detach).toHaveBeenCalledTimes(1); + }); + }); diff --git a/src/cdk/overlay/scroll/reposition-scroll-strategy.ts b/src/cdk/overlay/scroll/reposition-scroll-strategy.ts index 69128998b801..457f649c2844 100644 --- a/src/cdk/overlay/scroll/reposition-scroll-strategy.ts +++ b/src/cdk/overlay/scroll/reposition-scroll-strategy.ts @@ -1,21 +1,27 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ +import {NgZone} from '@angular/core'; import {Subscription} from 'rxjs/Subscription'; -import {ScrollStrategy, getMdScrollStrategyAlreadyAttachedError} from './scroll-strategy'; +import {ScrollStrategy, getMatScrollStrategyAlreadyAttachedError} from './scroll-strategy'; import {OverlayRef} from '../overlay-ref'; -import {ScrollDispatcher} from '@angular/cdk/scrolling'; +import {ScrollDispatcher, ViewportRuler} from '@angular/cdk/scrolling'; +import {isElementScrolledOutsideView} from '../position/scroll-clip'; /** * Config options for the RepositionScrollStrategy. */ export interface RepositionScrollStrategyConfig { + /** Time in milliseconds to throttle the scroll events. */ scrollThrottle?: number; + + /** Whether to close the overlay once the user has scrolled away completely. */ + autoClose?: boolean; } /** @@ -27,26 +33,46 @@ export class RepositionScrollStrategy implements ScrollStrategy { constructor( private _scrollDispatcher: ScrollDispatcher, + private _viewportRuler: ViewportRuler, + private _ngZone: NgZone, private _config?: RepositionScrollStrategyConfig) { } + /** Attaches this scroll strategy to an overlay. */ attach(overlayRef: OverlayRef) { if (this._overlayRef) { - throw getMdScrollStrategyAlreadyAttachedError(); + throw getMatScrollStrategyAlreadyAttachedError(); } this._overlayRef = overlayRef; } + /** Enables repositioning of the attached overlay on scroll. */ enable() { if (!this._scrollSubscription) { - let throttle = this._config ? this._config.scrollThrottle : 0; + const throttle = this._config ? this._config.scrollThrottle : 0; - this._scrollSubscription = this._scrollDispatcher.scrolled(throttle, () => { + this._scrollSubscription = this._scrollDispatcher.scrolled(throttle).subscribe(() => { this._overlayRef.updatePosition(); + + // TODO(crisbeto): make `close` on by default once all components can handle it. + if (this._config && this._config.autoClose) { + const overlayRect = this._overlayRef.overlayElement.getBoundingClientRect(); + const {width, height} = this._viewportRuler.getViewportSize(); + + // TODO(crisbeto): include all ancestor scroll containers here once + // we have a way of exposing the trigger element to the scroll strategy. + const parentRects = [{width, height, bottom: height, right: width, top: 0, left: 0}]; + + if (isElementScrolledOutsideView(overlayRect, parentRects)) { + this.disable(); + this._ngZone.run(() => this._overlayRef.detach()); + } + } }); } } + /** Disables repositioning of the attached overlay on scroll. */ disable() { if (this._scrollSubscription) { this._scrollSubscription.unsubscribe(); diff --git a/src/cdk/overlay/scroll/scroll-strategy-options.ts b/src/cdk/overlay/scroll/scroll-strategy-options.ts index 4b558805fc11..1185da1544e1 100644 --- a/src/cdk/overlay/scroll/scroll-strategy-options.ts +++ b/src/cdk/overlay/scroll/scroll-strategy-options.ts @@ -1,12 +1,12 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import {Injectable} from '@angular/core'; +import {Injectable, NgZone} from '@angular/core'; import {CloseScrollStrategy} from './close-scroll-strategy'; import {NoopScrollStrategy} from './noop-scroll-strategy'; import {BlockScrollStrategy} from './block-scroll-strategy'; @@ -28,13 +28,14 @@ import { export class ScrollStrategyOptions { constructor( private _scrollDispatcher: ScrollDispatcher, - private _viewportRuler: ViewportRuler) { } + private _viewportRuler: ViewportRuler, + private _ngZone: NgZone) { } /** Do nothing on scroll. */ noop = () => new NoopScrollStrategy(); /** Close the overlay as soon as the user scrolls. */ - close = () => new CloseScrollStrategy(this._scrollDispatcher); + close = () => new CloseScrollStrategy(this._scrollDispatcher, this._ngZone); /** Block scrolling. */ block = () => new BlockScrollStrategy(this._viewportRuler); @@ -44,6 +45,6 @@ export class ScrollStrategyOptions { * @param config Configuration to be used inside the scroll strategy. * Allows debouncing the reposition calls. */ - reposition = (config?: RepositionScrollStrategyConfig) => - new RepositionScrollStrategy(this._scrollDispatcher, config) + reposition = (config?: RepositionScrollStrategyConfig) => new RepositionScrollStrategy( + this._scrollDispatcher, this._viewportRuler, this._ngZone, config) } diff --git a/src/cdk/overlay/scroll/scroll-strategy.md b/src/cdk/overlay/scroll/scroll-strategy.md index b39772b0b431..35c259832259 100644 --- a/src/cdk/overlay/scroll/scroll-strategy.md +++ b/src/cdk/overlay/scroll/scroll-strategy.md @@ -7,14 +7,15 @@ recalculate the position, close the overlay, block scrolling, etc. ## Usage To associate an overlay with a scroll strategy, you have to pass in a function, that returns a -scroll strategy, to the `OverlayState`. By default, all overlays will use the `noop` strategy which +scroll strategy, to the `OverlayConfig`. By default, all overlays will use the `noop` strategy which doesn't do anything. The other available strategies are `reposition`, `block` and `close`: ```ts -let overlayState = new OverlayState(); +let overlayConfig = new OverlayConfig({ + scrollStrategy: overlay.scrollStrategies.block() +}); -overlayState.scrollStrategy = overlay.scrollStrategies.block(); -this._overlay.create(overlayState).attach(yourPortal); +this._overlay.create(overlayConfig).attach(yourPortal); ``` ## Creating a custom scroll strategy @@ -34,6 +35,6 @@ export class CustomScrollStrategy implements ScrollStrategy { // your implementation } -overlayState.scrollStrategy = new CustomScrollStrategy(); -this._overlay.create(overlayState).attach(yourPortal); +overlayConfig.scrollStrategy = new CustomScrollStrategy(); +this._overlay.create(overlayConfig).attach(yourPortal); ``` diff --git a/src/cdk/overlay/scroll/scroll-strategy.ts b/src/cdk/overlay/scroll/scroll-strategy.ts index d9a6bf533247..f96d94522773 100644 --- a/src/cdk/overlay/scroll/scroll-strategy.ts +++ b/src/cdk/overlay/scroll/scroll-strategy.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -9,18 +9,22 @@ import {OverlayRef} from '../overlay-ref'; /** - * Describes a strategy that will be used by an overlay - * to handle scroll events while it is open. + * Describes a strategy that will be used by an overlay to handle scroll events while it is open. */ export interface ScrollStrategy { + /** Enable this scroll strategy (called when the attached overlay is attached to a portal). */ enable: () => void; + + /** Disable this scroll strategy (called when the attached overlay is detached from a portal). */ disable: () => void; + + /** Attaches this `ScrollStrategy` to an overlay. */ attach: (overlayRef: OverlayRef) => void; } /** * Returns an error to be thrown when attempting to attach an already-attached scroll strategy. */ -export function getMdScrollStrategyAlreadyAttachedError(): Error { +export function getMatScrollStrategyAlreadyAttachedError(): Error { return Error(`Scroll strategy has already been attached.`); } diff --git a/src/cdk/overlay/tsconfig-build.json b/src/cdk/overlay/tsconfig-build.json index 8b6df58dda2a..254e6404a551 100644 --- a/src/cdk/overlay/tsconfig-build.json +++ b/src/cdk/overlay/tsconfig-build.json @@ -1,7 +1,7 @@ { "extends": "../tsconfig-build", "files": [ - "public_api.ts" + "public-api.ts" ], "angularCompilerOptions": { "annotateForClosureCompiler": true, diff --git a/src/cdk/package.json b/src/cdk/package.json index 3b1a28634a4e..b6345a26b6a7 100644 --- a/src/cdk/package.json +++ b/src/cdk/package.json @@ -3,8 +3,8 @@ "version": "0.0.0-PLACEHOLDER", "description": "Angular Material Component Development Kit", "main": "./bundles/cdk.umd.js", - "module": "./@angular/cdk.es5.js", - "es2015": "./@angular/cdk.js", + "module": "./esm5/cdk.es5.js", + "es2015": "./esm2015/cdk.js", "typings": "./cdk.d.ts", "repository": { "type": "git", @@ -23,8 +23,8 @@ }, "homepage": "https://github.com/angular/material2#readme", "peerDependencies": { - "@angular/core": "^4.3.0", - "@angular/common": "^4.3.0" + "@angular/core": "0.0.0-NG", + "@angular/common": "0.0.0-NG" }, "dependencies": { "tslib": "^1.7.1" diff --git a/src/cdk/platform/BUILD.bazel b/src/cdk/platform/BUILD.bazel new file mode 100644 index 000000000000..1f70a13a27d6 --- /dev/null +++ b/src/cdk/platform/BUILD.bazel @@ -0,0 +1,10 @@ +package(default_visibility=["//visibility:public"]) +load("@angular//:index.bzl", "ng_module") + +ng_module( + name = "platform", + srcs = glob(["**/*.ts"], exclude=["**/*.spec.ts"]), + module_name = "@angular/cdk/platform", + deps = [], + tsconfig = ":tsconfig-build.json", +) diff --git a/src/cdk/platform/features.ts b/src/cdk/platform/features.ts index 67dfeb589e0f..3ea2dad061d7 100644 --- a/src/cdk/platform/features.ts +++ b/src/cdk/platform/features.ts @@ -1,11 +1,32 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ +/** Cached result of whether the user's browser supports passive event listeners. */ +let supportsPassiveEvents: boolean; + +/** + * Checks whether the user's browser supports passive event listeners. + * See: https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md + */ +export function supportsPassiveEventListeners(): boolean { + if (supportsPassiveEvents == null) { + try { + window.addEventListener('test', null!, Object.defineProperty({}, 'passive', { + get: () => supportsPassiveEvents = true + })); + } finally { + supportsPassiveEvents = supportsPassiveEvents || false; + } + } + + return supportsPassiveEvents; +} + /** Cached result Set of input types support by the current browser. */ let supportedInputTypes: Set; diff --git a/src/cdk/platform/index.ts b/src/cdk/platform/index.ts index f93e7c31d564..676ca90f1ffa 100644 --- a/src/cdk/platform/index.ts +++ b/src/cdk/platform/index.ts @@ -1,9 +1,9 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -export * from './public_api'; +export * from './public-api'; diff --git a/src/cdk/platform/public_api.ts b/src/cdk/platform/platform-module.ts similarity index 74% rename from src/cdk/platform/public_api.ts rename to src/cdk/platform/platform-module.ts index ea7a9a4b4cf4..7ab0c2bc3304 100644 --- a/src/cdk/platform/public_api.ts +++ b/src/cdk/platform/platform-module.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -14,7 +14,3 @@ import {Platform} from './platform'; providers: [Platform] }) export class PlatformModule {} - - -export * from './platform'; -export * from './features'; diff --git a/src/cdk/platform/platform.md b/src/cdk/platform/platform.md new file mode 100644 index 000000000000..0ad007d9982d --- /dev/null +++ b/src/cdk/platform/platform.md @@ -0,0 +1,3 @@ +### Platform + +A service for determing the current platform. \ No newline at end of file diff --git a/src/cdk/platform/platform.ts b/src/cdk/platform/platform.ts index 11e12b85eb71..81af1441020f 100755 --- a/src/cdk/platform/platform.ts +++ b/src/cdk/platform/platform.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -15,39 +15,47 @@ const hasV8BreakIterator = (typeof(Intl) !== 'undefined' && (Intl as any).v8Brea /** * Service to detect the current platform by comparing the userAgent strings and * checking browser-specific global properties. - * @docs-private */ @Injectable() export class Platform { + /** Whether the Angular application is being rendered in the browser. */ isBrowser: boolean = typeof document === 'object' && !!document; - /** Layout Engines */ - EDGE = this.isBrowser && /(edge)/i.test(navigator.userAgent); - TRIDENT = this.isBrowser && /(msie|trident)/i.test(navigator.userAgent); + /** Whether the current browser is Microsoft Edge. */ + EDGE: boolean = this.isBrowser && /(edge)/i.test(navigator.userAgent); + /** Whether the current rendering engine is Microsoft Trident. */ + TRIDENT: boolean = this.isBrowser && /(msie|trident)/i.test(navigator.userAgent); + + /** Whether the current rendering engine is Blink. */ // EdgeHTML and Trident mock Blink specific things and need to be excluded from this check. - BLINK = this.isBrowser && + BLINK: boolean = this.isBrowser && (!!((window as any).chrome || hasV8BreakIterator) && !!CSS && !this.EDGE && !this.TRIDENT); + /** Whether the current rendering engine is WebKit. */ // Webkit is part of the userAgent in EdgeHTML, Blink and Trident. Therefore we need to // ensure that Webkit runs standalone and is not used as another engine's base. - WEBKIT = this.isBrowser && + WEBKIT: boolean = this.isBrowser && /AppleWebKit/i.test(navigator.userAgent) && !this.BLINK && !this.EDGE && !this.TRIDENT; - /** Browsers and Platform Types */ - IOS = this.isBrowser && /iPad|iPhone|iPod/.test(navigator.userAgent) && !(window as any).MSStream; + /** Whether the current platform is Apple iOS. */ + IOS: boolean = this.isBrowser && /iPad|iPhone|iPod/.test(navigator.userAgent) && + !(window as any).MSStream; + /** Whether the current browser is Firefox. */ // It's difficult to detect the plain Gecko engine, because most of the browsers identify // them self as Gecko-like browsers and modify the userAgent's according to that. // Since we only cover one explicit Firefox case, we can simply check for Firefox // instead of having an unstable check for Gecko. - FIREFOX = this.isBrowser && /(firefox|minefield)/i.test(navigator.userAgent); + FIREFOX: boolean = this.isBrowser && /(firefox|minefield)/i.test(navigator.userAgent); + /** Whether the current platform is Android. */ // Trident on mobile adds the android platform to the userAgent to trick detections. - ANDROID = this.isBrowser && /android/i.test(navigator.userAgent) && !this.TRIDENT; + ANDROID: boolean = this.isBrowser && /android/i.test(navigator.userAgent) && !this.TRIDENT; + /** Whether the current browser is Safari. */ // Safari browsers will include the Safari keyword in their userAgent. Some browsers may fake // this and just place the Safari keyword in the userAgent. To be more safe about Safari every // Safari browser should also use Webkit as its layout engine. - SAFARI = this.isBrowser && /safari/i.test(navigator.userAgent) && this.WEBKIT; + SAFARI: boolean = this.isBrowser && /safari/i.test(navigator.userAgent) && this.WEBKIT; } diff --git a/src/lib/core/platform/index.ts b/src/cdk/platform/public-api.ts similarity index 68% rename from src/lib/core/platform/index.ts rename to src/cdk/platform/public-api.ts index e8f8a33331cc..18ad6c9e4cec 100644 --- a/src/lib/core/platform/index.ts +++ b/src/cdk/platform/public-api.ts @@ -1,11 +1,11 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -export {PlatformModule} from '@angular/cdk/platform'; export * from './platform'; export * from './features'; +export * from './platform-module'; diff --git a/src/cdk/platform/tsconfig-build.json b/src/cdk/platform/tsconfig-build.json index b5b3db511172..2da6b605cd5a 100644 --- a/src/cdk/platform/tsconfig-build.json +++ b/src/cdk/platform/tsconfig-build.json @@ -1,7 +1,7 @@ { "extends": "../tsconfig-build", "files": [ - "public_api.ts" + "public-api.ts" ], "angularCompilerOptions": { "annotateForClosureCompiler": true, diff --git a/src/cdk/portal/BUILD.bazel b/src/cdk/portal/BUILD.bazel new file mode 100644 index 000000000000..c999ae0ec337 --- /dev/null +++ b/src/cdk/portal/BUILD.bazel @@ -0,0 +1,10 @@ +package(default_visibility=["//visibility:public"]) +load("@angular//:index.bzl", "ng_module") + +ng_module( + name = "portal", + srcs = glob(["**/*.ts"], exclude=["**/*.spec.ts"]), + module_name = "@angular/cdk/portal", + deps = [], + tsconfig = ":tsconfig-build.json", +) diff --git a/src/cdk/portal/dom-portal-host.ts b/src/cdk/portal/dom-portal-outlet.ts similarity index 85% rename from src/cdk/portal/dom-portal-host.ts rename to src/cdk/portal/dom-portal-outlet.ts index 1dcefe35648f..47e155f17e40 100644 --- a/src/cdk/portal/dom-portal-host.ts +++ b/src/cdk/portal/dom-portal-outlet.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -13,16 +13,14 @@ import { ApplicationRef, Injector, } from '@angular/core'; -import {BasePortalHost, ComponentPortal, TemplatePortal} from './portal'; +import {BasePortalOutlet, ComponentPortal, TemplatePortal} from './portal'; /** - * A PortalHost for attaching portals to an arbitrary DOM element outside of the Angular + * A PortalOutlet for attaching portals to an arbitrary DOM element outside of the Angular * application context. - * - * This is the only part of the portal core that directly touches the DOM. */ -export class DomPortalHost extends BasePortalHost { +export class DomPortalOutlet extends BasePortalOutlet { constructor( private _hostDomElement: Element, private _componentFactoryResolver: ComponentFactoryResolver, @@ -34,6 +32,7 @@ export class DomPortalHost extends BasePortalHost { /** * Attach the given ComponentPortal to DOM element using the ComponentFactoryResolver. * @param portal Portal to be attached + * @returns Reference to the created component. */ attachComponentPortal(portal: ComponentPortal): ComponentRef { let componentFactory = this._componentFactoryResolver.resolveComponentFactory(portal.component); @@ -68,6 +67,7 @@ export class DomPortalHost extends BasePortalHost { /** * Attaches a template portal to the DOM as an embedded view. * @param portal Portal to be attached. + * @returns Reference to the created embedded view. */ attachTemplatePortal(portal: TemplatePortal): EmbeddedViewRef { let viewContainer = portal.viewContainerRef; @@ -75,8 +75,9 @@ export class DomPortalHost extends BasePortalHost { viewRef.detectChanges(); // The method `createEmbeddedView` will add the view as a child of the viewContainer. - // But for the DomPortalHost the view can be added everywhere in the DOM (e.g Overlay Container) - // To move the view to the specified host element. We just re-append the existing root nodes. + // But for the DomPortalOutlet the view can be added everywhere in the DOM + // (e.g Overlay Container) To move the view to the specified host element. We just + // re-append the existing root nodes. viewRef.rootNodes.forEach(rootNode => this._hostDomElement.appendChild(rootNode)); this.setDisposeFn((() => { diff --git a/src/cdk/portal/index.ts b/src/cdk/portal/index.ts index 2b504ab757d8..96b6b70a6230 100644 --- a/src/cdk/portal/index.ts +++ b/src/cdk/portal/index.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -8,4 +8,4 @@ -export * from './public_api'; +export * from './public-api'; diff --git a/src/cdk/portal/portal-directives.ts b/src/cdk/portal/portal-directives.ts index a0a30560dfba..935d6e680983 100644 --- a/src/cdk/portal/portal-directives.ts +++ b/src/cdk/portal/portal-directives.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -17,23 +17,18 @@ import { OnDestroy, Input, } from '@angular/core'; -import {Portal, TemplatePortal, ComponentPortal, BasePortalHost} from './portal'; +import {Portal, TemplatePortal, ComponentPortal, BasePortalOutlet} from './portal'; /** * Directive version of a `TemplatePortal`. Because the directive *is* a TemplatePortal, * the directive instance itself can be attached to a host, enabling declarative use of portals. - * - * Usage: - * - *

Hello {{name}}

- *
*/ @Directive({ selector: '[cdk-portal], [cdkPortal], [portal]', exportAs: 'cdkPortal', }) -export class TemplatePortalDirective extends TemplatePortal { +export class CdkPortal extends TemplatePortal { constructor(templateRef: TemplateRef, viewContainerRef: ViewContainerRef) { super(templateRef, viewContainerRef); } @@ -41,17 +36,18 @@ export class TemplatePortalDirective extends TemplatePortal { /** - * Directive version of a PortalHost. Because the directive *is* a PortalHost, portals can be + * Directive version of a PortalOutlet. Because the directive *is* a PortalOutlet, portals can be * directly attached to it, enabling declarative use. * * Usage: - * + * */ @Directive({ - selector: '[cdkPortalHost], [portalHost]', - inputs: ['portal: cdkPortalHost'] + selector: '[cdkPortalOutlet], [cdkPortalHost], [portalHost]', + exportAs: 'cdkPortalOutlet, cdkPortalHost', + inputs: ['portal: cdkPortalOutlet'] }) -export class PortalHostDirective extends BasePortalHost implements OnDestroy { +export class CdkPortalOutlet extends BasePortalOutlet implements OnDestroy { /** The attached portal. */ private _portal: Portal | null = null; @@ -66,7 +62,12 @@ export class PortalHostDirective extends BasePortalHost implements OnDestroy { get _deprecatedPortal() { return this.portal; } set _deprecatedPortal(v) { this.portal = v; } - /** Portal associated with the Portal host. */ + /** @deprecated */ + @Input('cdkPortalHost') + get _deprecatedPortalHost() { return this.portal; } + set _deprecatedPortalHost(v) { this.portal = v; } + + /** Portal associated with the Portal outlet. */ get portal(): Portal | null { return this._portal; } @@ -89,15 +90,16 @@ export class PortalHostDirective extends BasePortalHost implements OnDestroy { } /** - * Attach the given ComponentPortal to this PortalHost using the ComponentFactoryResolver. + * Attach the given ComponentPortal to this PortalOutlet using the ComponentFactoryResolver. * - * @param portal Portal to be attached to the portal host. + * @param portal Portal to be attached to the portal outlet. + * @returns Reference to the created component. */ attachComponentPortal(portal: ComponentPortal): ComponentRef { portal.setAttachedHost(this); // If the portal specifies an origin, use that as the logical location of the component - // in the application tree. Otherwise use the location of this PortalHost. + // in the application tree. Otherwise use the location of this PortalOutlet. let viewContainerRef = portal.viewContainerRef != null ? portal.viewContainerRef : this._viewContainerRef; @@ -117,6 +119,7 @@ export class PortalHostDirective extends BasePortalHost implements OnDestroy { /** * Attach the given TemplatePortal to this PortlHost as an embedded View. * @param portal Portal to be attached. + * @returns Reference to the created embedded view. */ attachTemplatePortal(portal: TemplatePortal): EmbeddedViewRef { portal.setAttachedHost(this); @@ -131,7 +134,7 @@ export class PortalHostDirective extends BasePortalHost implements OnDestroy { @NgModule({ - exports: [TemplatePortalDirective, PortalHostDirective], - declarations: [TemplatePortalDirective, PortalHostDirective], + exports: [CdkPortal, CdkPortalOutlet], + declarations: [CdkPortal, CdkPortalOutlet], }) export class PortalModule {} diff --git a/src/cdk/portal/portal-errors.ts b/src/cdk/portal/portal-errors.ts index b2530e8efa57..45d0c06b95e3 100644 --- a/src/cdk/portal/portal-errors.ts +++ b/src/cdk/portal/portal-errors.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -26,8 +26,8 @@ export function throwPortalAlreadyAttachedError() { * Throws an exception when attempting to attach a portal to an already-disposed host. * @docs-private */ -export function throwPortalHostAlreadyDisposedError() { - throw Error('This PortalHost has already been disposed'); +export function throwPortalOutletAlreadyDisposedError() { + throw Error('This PortalOutlet has already been disposed'); } /** @@ -35,16 +35,16 @@ export function throwPortalHostAlreadyDisposedError() { * @docs-private */ export function throwUnknownPortalTypeError() { - throw Error('Attempting to attach an unknown Portal type. BasePortalHost accepts either ' + - 'a ComponentPortal or a TemplatePortal.'); + throw Error('Attempting to attach an unknown Portal type. BasePortalOutlet accepts either ' + + 'a ComponentPortal or a TemplatePortal.'); } /** * Throws an exception when attempting to attach a portal to a null host. * @docs-private */ -export function throwNullPortalHostError() { - throw Error('Attempting to attach a portal to a null PortalHost'); +export function throwNullPortalOutletError() { + throw Error('Attempting to attach a portal to a null PortalOutlet'); } /** diff --git a/src/lib/core/portal/portal-injector.ts b/src/cdk/portal/portal-injector.ts similarity index 93% rename from src/lib/core/portal/portal-injector.ts rename to src/cdk/portal/portal-injector.ts index 98945fa75299..ca2267f8edc5 100644 --- a/src/lib/core/portal/portal-injector.ts +++ b/src/cdk/portal/portal-injector.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license diff --git a/src/cdk/portal/portal.md b/src/cdk/portal/portal.md index 79910d6c3371..584fafe47219 100644 --- a/src/cdk/portal/portal.md +++ b/src/cdk/portal/portal.md @@ -1,20 +1,22 @@ +The `portals` package provides a flexible system for rendering dynamic content into an application. + ### Portals A `Portal `is a piece of UI that can be dynamically rendered to an open slot on the page. -The "piece of UI" can be either a `Component` or a `TemplateRef` and the "open slot" is -a `PortalHost`. +The "piece of UI" can be either a `Component` or a `TemplateRef` and the "open slot" is +a `PortalOutlet`. -Portals and PortalHosts are low-level building blocks that other concepts, such as overlays, are +Portals and PortalOutlets are low-level building blocks that other concepts, such as overlays, are built upon. ##### `Portal` | Method | Description | | --- | --- | -| `attach(PortalHost): Promise` | Attaches the portal to a host. | +| `attach(PortalOutlet): Promise` | Attaches the portal to a host. | | `detach(): Promise` | Detaches the portal from its host. | | `isAttached: boolean` | Whether the portal is attached. | -##### `PortalHost` +##### `PortalOutlet` | Method | Description | | --- | --- | | `attach(Portal): Promise` | Attaches a portal to the host. | @@ -25,12 +27,12 @@ built upon. #### Portals in practice -##### `TemplatePortalDirective` -Used to get a portal from an ``. `TemplatePortalDirectives` *is* a `Portal`. +##### `CdkPortal` +Used to get a portal from an ``. `CdkPortal` *is* a `Portal`. Usage: ```html - +

The content of this template is captured by the portal.

@@ -43,7 +45,7 @@ Usage: ``` A component can use `@ViewChild` or `@ViewChildren` to get a reference to a -`TemplatePortalDirective`. +`CdkPortal`. ##### `ComponentPortal` Used to create a portal from a component type. When a component is dynamically created using @@ -55,11 +57,11 @@ this.userSettingsPortal = new ComponentPortal(UserSettingsComponent); ``` -##### `PortalHostDirective` -Used to add a portal host to a template. `PortalHostDirective` *is* a `PortalHost`. +##### `CdkPortalOutlet` +Used to add a portal outlet to a template. `CdkPortalOutlet` *is* a `PortalOutlet`. Usage: ```html - + ``` diff --git a/src/cdk/portal/portal.spec.ts b/src/cdk/portal/portal.spec.ts index 61496f5ba45c..69659237f095 100644 --- a/src/cdk/portal/portal.spec.ts +++ b/src/cdk/portal/portal.spec.ts @@ -13,9 +13,9 @@ import { TemplateRef } from '@angular/core'; import {CommonModule} from '@angular/common'; -import {TemplatePortalDirective, PortalHostDirective, PortalModule} from './portal-directives'; +import {CdkPortal, CdkPortalOutlet, PortalModule} from './portal-directives'; import {Portal, ComponentPortal, TemplatePortal} from './portal'; -import {DomPortalHost} from './dom-portal-host'; +import {DomPortalOutlet} from './dom-portal-outlet'; describe('Portals', () => { @@ -28,7 +28,7 @@ describe('Portals', () => { TestBed.compileComponents(); })); - describe('PortalHostDirective', () => { + describe('CdkPortalOutlet', () => { let fixture: ComponentFixture; beforeEach(() => { @@ -71,7 +71,7 @@ describe('Portals', () => { // using TemplatePortal.attach method to set context testAppComponent.selectedPortal = undefined; fixture.detectChanges(); - templatePortal.attach(testAppComponent.portalHost, {$implicit: {status: 'rotten'}}); + templatePortal.attach(testAppComponent.portalOutlet, {$implicit: {status: 'rotten'}}); fixture.detectChanges(); // Expect that the content of the attached portal is present and context given via the // attach method is projected @@ -90,7 +90,7 @@ describe('Portals', () => { // context, the latter should take precedence: testAppComponent.selectedPortal = undefined; fixture.detectChanges(); - templatePortal.attach(testAppComponent.portalHost, {$implicit: {status: 'rotten'}}); + templatePortal.attach(testAppComponent.portalOutlet, {$implicit: {status: 'rotten'}}); fixture.detectChanges(); // Expect that the content of the attached portal is present and and context given via the // attach method is projected and get precedence over constructor context @@ -223,32 +223,32 @@ describe('Portals', () => { testAppComponent.selectedPortal = new ComponentPortal(PizzaMsg); fixture.detectChanges(); - expect(testAppComponent.portalHost.hasAttached()).toBe(true); - expect(testAppComponent.portalHost.portal).toBe(testAppComponent.selectedPortal); + expect(testAppComponent.portalOutlet.hasAttached()).toBe(true); + expect(testAppComponent.portalOutlet.portal).toBe(testAppComponent.selectedPortal); testAppComponent.selectedPortal = null; fixture.detectChanges(); - expect(testAppComponent.portalHost.hasAttached()).toBe(false); - expect(testAppComponent.portalHost.portal).toBeNull(); + expect(testAppComponent.portalOutlet.hasAttached()).toBe(false); + expect(testAppComponent.portalOutlet.portal).toBeNull(); }); it('should set the `portal` when attaching a component portal programmatically', () => { let testAppComponent = fixture.debugElement.componentInstance; let portal = new ComponentPortal(PizzaMsg); - testAppComponent.portalHost.attachComponentPortal(portal); + testAppComponent.portalOutlet.attachComponentPortal(portal); - expect(testAppComponent.portalHost.portal).toBe(portal); + expect(testAppComponent.portalOutlet.portal).toBe(portal); }); it('should set the `portal` when attaching a template portal programmatically', () => { let testAppComponent = fixture.debugElement.componentInstance; fixture.detectChanges(); - testAppComponent.portalHost.attachTemplatePortal(testAppComponent.cakePortal); + testAppComponent.portalOutlet.attachTemplatePortal(testAppComponent.cakePortal); - expect(testAppComponent.portalHost.portal).toBe(testAppComponent.cakePortal); + expect(testAppComponent.portalOutlet.portal).toBe(testAppComponent.cakePortal); }); it('should clear the portal reference on destroy', () => { @@ -257,21 +257,21 @@ describe('Portals', () => { testAppComponent.selectedPortal = new ComponentPortal(PizzaMsg); fixture.detectChanges(); - expect(testAppComponent.portalHost.portal).toBeTruthy(); + expect(testAppComponent.portalOutlet.portal).toBeTruthy(); fixture.destroy(); - expect(testAppComponent.portalHost.portal).toBeNull(); + expect(testAppComponent.portalOutlet.portal).toBeNull(); }); }); - describe('DomPortalHost', () => { + describe('DomPortalOutlet', () => { let componentFactoryResolver: ComponentFactoryResolver; let someViewContainerRef: ViewContainerRef; let someInjector: Injector; let someFixture: ComponentFixture; let someDomElement: HTMLElement; - let host: DomPortalHost; + let host: DomPortalOutlet; let injector: Injector; let appRef: ApplicationRef; @@ -284,7 +284,7 @@ describe('Portals', () => { beforeEach(() => { someDomElement = document.createElement('div'); - host = new DomPortalHost(someDomElement, componentFactoryResolver, appRef, injector); + host = new DomPortalOutlet(someDomElement, componentFactoryResolver, appRef, injector); someFixture = TestBed.createComponent(ArbitraryViewContainerRefComponent); someViewContainerRef = someFixture.componentInstance.viewContainerRef; @@ -397,17 +397,17 @@ describe('Portals', () => { expect(componentInstance instanceof PizzaMsg) .toBe(true, 'Expected a PizzaMsg component to be created'); expect(someDomElement.textContent) - .toContain('Pizza', 'Expected the static string "Pizza" in the DomPortalHost.'); + .toContain('Pizza', 'Expected the static string "Pizza" in the DomPortalOutlet.'); componentInstance.snack = new Chocolate(); someFixture.detectChanges(); expect(someDomElement.textContent) - .toContain('Chocolate', 'Expected the bound string "Chocolate" in the DomPortalHost'); + .toContain('Chocolate', 'Expected the bound string "Chocolate" in the DomPortalOutlet'); host.detach(); expect(someDomElement.innerHTML) - .toBe('', 'Expected the DomPortalHost to be empty after detach'); + .toBe('', 'Expected the DomPortalOutlet to be empty after detach'); }); it('should call the dispose function even if the host has no attached content', () => { @@ -457,12 +457,12 @@ class ArbitraryViewContainerRefComponent { } -/** Test-bed component that contains a portal host and a couple of template portals. */ +/** Test-bed component that contains a portal outlet and a couple of template portals. */ @Component({ selector: 'portal-test', template: `
- +
Cake @@ -480,8 +480,8 @@ class ArbitraryViewContainerRefComponent { `, }) class PortalTestApp { - @ViewChildren(TemplatePortalDirective) portals: QueryList; - @ViewChild(PortalHostDirective) portalHost: PortalHostDirective; + @ViewChildren(CdkPortal) portals: QueryList; + @ViewChild(CdkPortalOutlet) portalOutlet: CdkPortalOutlet; @ViewChild('templateRef', { read: TemplateRef }) templateRef: TemplateRef; selectedPortal: Portal; diff --git a/src/cdk/portal/portal.ts b/src/cdk/portal/portal.ts index 32eb30ec9025..981f12f562e1 100644 --- a/src/cdk/portal/portal.ts +++ b/src/cdk/portal/portal.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -15,30 +15,30 @@ import { Injector } from '@angular/core'; import { - throwNullPortalHostError, + throwNullPortalOutletError, throwPortalAlreadyAttachedError, throwNoPortalAttachedError, throwNullPortalError, - throwPortalHostAlreadyDisposedError, + throwPortalOutletAlreadyDisposedError, throwUnknownPortalTypeError } from './portal-errors'; - +/** Interface that can be used to generically type a class. */ export interface ComponentType { new (...args: any[]): T; } /** * A `Portal` is something that you want to render somewhere else. - * It can be attach to / detached from a `PortalHost`. + * It can be attach to / detached from a `PortalOutlet`. */ export abstract class Portal { - private _attachedHost: PortalHost | null; + private _attachedHost: PortalOutlet | null; /** Attach this portal to a host. */ - attach(host: PortalHost): T { + attach(host: PortalOutlet): T { if (host == null) { - throwNullPortalHostError(); + throwNullPortalOutletError(); } if (host.hasAttached()) { @@ -67,10 +67,10 @@ export abstract class Portal { } /** - * Sets the PortalHost reference without performing `attach()`. This is used directly by - * the PortalHost when it is performing an `attach()` or `detach()`. + * Sets the PortalOutlet reference without performing `attach()`. This is used directly by + * the PortalOutlet when it is performing an `attach()` or `detach()`. */ - setAttachedHost(host: PortalHost | null) { + setAttachedHost(host: PortalOutlet | null) { this._attachedHost = host; } } @@ -85,7 +85,7 @@ export class ComponentPortal extends Portal> { /** * [Optional] Where the attached component should live in Angular's *logical* component tree. - * This is different from where the component *renders*, which is determined by the PortalHost. + * This is different from where the component *renders*, which is determined by the PortalOutlet. * The origin is necessary when the host is outside of the Angular application context. */ viewContainerRef?: ViewContainerRef | null; @@ -130,11 +130,11 @@ export class TemplatePortal extends Portal { } /** - * Attach the the portal to the provided `PortalHost`. + * Attach the the portal to the provided `PortalOutlet`. * When a context is provided it will override the `context` property of the `TemplatePortal` * instance. */ - attach(host: PortalHost, context: C | undefined = this.context): C { + attach(host: PortalOutlet, context: C | undefined = this.context): C { this.context = context; return super.attach(host); } @@ -146,25 +146,27 @@ export class TemplatePortal extends Portal { } -/** - * A `PortalHost` is an space that can contain a single `Portal`. - */ -export interface PortalHost { +/** A `PortalOutlet` is an space that can contain a single `Portal`. */ +export interface PortalOutlet { + /** Attaches a portal to this outlet. */ attach(portal: Portal): any; + /** Detaches the currently attached portal from this outlet. */ detach(): any; + /** Performs cleanup before the outlet is destroyed. */ dispose(): void; + /** Whether there is currently a portal attached to this outlet. */ hasAttached(): boolean; } /** - * Partial implementation of PortalHost that only deals with attaching either a - * ComponentPortal or a TemplatePortal. + * Partial implementation of PortalOutlet that handles attaching + * ComponentPortal and TemplatePortal. */ -export abstract class BasePortalHost implements PortalHost { +export abstract class BasePortalOutlet implements PortalOutlet { /** The portal currently attached to the host. */ private _attachedPortal: Portal | null; @@ -179,6 +181,11 @@ export abstract class BasePortalHost implements PortalHost { return !!this._attachedPortal; } + attach(portal: ComponentPortal): ComponentRef; + attach(portal: TemplatePortal): EmbeddedViewRef; + attach(portal: any): any; + + /** Attaches a portal. */ attach(portal: Portal): any { if (!portal) { throwNullPortalError(); @@ -189,7 +196,7 @@ export abstract class BasePortalHost implements PortalHost { } if (this._isDisposed) { - throwPortalHostAlreadyDisposedError(); + throwPortalOutletAlreadyDisposedError(); } if (portal instanceof ComponentPortal) { @@ -207,6 +214,7 @@ export abstract class BasePortalHost implements PortalHost { abstract attachTemplatePortal(portal: TemplatePortal): EmbeddedViewRef; + /** Detaches a previously attached portal. */ detach(): void { if (this._attachedPortal) { this._attachedPortal.setAttachedHost(null); @@ -216,7 +224,8 @@ export abstract class BasePortalHost implements PortalHost { this._invokeDisposeFn(); } - dispose() { + /** Permanently dispose of this portal host. */ + dispose(): void { if (this.hasAttached()) { this.detach(); } @@ -225,6 +234,7 @@ export abstract class BasePortalHost implements PortalHost { this._isDisposed = true; } + /** @docs-private */ setDisposeFn(fn: () => void) { this._disposeFn = fn; } diff --git a/src/cdk/portal/public-api.ts b/src/cdk/portal/public-api.ts new file mode 100644 index 000000000000..fd47ee6ebf5e --- /dev/null +++ b/src/cdk/portal/public-api.ts @@ -0,0 +1,19 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +export * from './portal'; +export * from './dom-portal-outlet'; +export * from './portal-directives'; +export * from './portal-injector'; + +export {DomPortalOutlet as DomPortalHost} from './dom-portal-outlet'; +export { + CdkPortalOutlet as PortalHostDirective, + CdkPortal as TemplatePortalDirective, +} from './portal-directives'; +export {PortalOutlet as PortalHost, BasePortalOutlet as BasePortalHost} from './portal'; diff --git a/src/cdk/portal/public_api.ts b/src/cdk/portal/public_api.ts deleted file mode 100644 index 3006ee0af57d..000000000000 --- a/src/cdk/portal/public_api.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * @license - * Copyright Google Inc. All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -export * from './portal'; -export * from './dom-portal-host'; -export * from './portal-directives'; diff --git a/src/cdk/portal/tsconfig-build.json b/src/cdk/portal/tsconfig-build.json index c22ae5810fa8..fd3241f76e01 100644 --- a/src/cdk/portal/tsconfig-build.json +++ b/src/cdk/portal/tsconfig-build.json @@ -1,7 +1,7 @@ { "extends": "../tsconfig-build", "files": [ - "public_api.ts" + "public-api.ts" ], "angularCompilerOptions": { "annotateForClosureCompiler": true, diff --git a/src/cdk/public_api.ts b/src/cdk/public-api.ts similarity index 80% rename from src/cdk/public_api.ts rename to src/cdk/public-api.ts index 2a5af4f06cb7..326c704b094c 100644 --- a/src/cdk/public_api.ts +++ b/src/cdk/public-api.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license diff --git a/src/cdk/rxjs/public_api.ts b/src/cdk/rxjs/public_api.ts deleted file mode 100644 index a6f22e575d8e..000000000000 --- a/src/cdk/rxjs/public_api.ts +++ /dev/null @@ -1,10 +0,0 @@ -/** - * @license - * Copyright Google Inc. All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -export * from './rx-chain'; -export * from './rx-operators'; diff --git a/src/cdk/rxjs/rx-chain.spec.ts b/src/cdk/rxjs/rx-chain.spec.ts deleted file mode 100644 index e9bc10ec5401..000000000000 --- a/src/cdk/rxjs/rx-chain.spec.ts +++ /dev/null @@ -1,41 +0,0 @@ -import {Observable} from 'rxjs/Observable'; -import {of as observableOf} from 'rxjs/observable/of'; -import {RxChain, map, filter, first} from './index'; - -describe('RxChain', () => { - it('should call all of the operators in the chain', () => { - let operators = { map, filter, first }; - - spyOn(operators, 'map'); - spyOn(operators, 'filter'); - spyOn(operators, 'first'); - - RxChain.from(observableOf(1, 2, 3)) - .call(operators.map, i => i * 2) - .call(operators.filter, i => i % 2 === 0) - .call(operators.first); - - expect(operators.map).toHaveBeenCalled(); - expect(operators.filter).toHaveBeenCalled(); - expect(operators.first).toHaveBeenCalled(); - }); - - it('should be able to subscribe', () => { - const spy = jasmine.createSpy('subscription spy'); - - RxChain.from(observableOf(1, 2)) - .call(map, i => i * 2) - .call(first) - .subscribe(spy); - - expect(spy).toHaveBeenCalledWith(2); - }); - - it('should be able to return the result observable', () => { - const chain = RxChain.from(observableOf(1, 2)) - .call(map, i => i * 2) - .call(first); - - expect(chain.result() instanceof Observable).toBe(true); - }); -}); diff --git a/src/cdk/rxjs/rx-chain.ts b/src/cdk/rxjs/rx-chain.ts deleted file mode 100644 index 0d6d25e7f190..000000000000 --- a/src/cdk/rxjs/rx-chain.ts +++ /dev/null @@ -1,55 +0,0 @@ -/** - * @license - * Copyright Google Inc. All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {Observable} from 'rxjs/Observable'; -import {Subscription} from 'rxjs/Subscription'; -import {StrictRxChain} from './rx-operators'; - -/** - * Utility class used to chain RxJS operators. - * - * This class is the concrete implementation, but the type used by the user when chaining - * is StrictRxChain. The strict chain enforces types on the operators to the same level as - * the prototype-added equivalents. - */ -export class RxChain { - private constructor(private _context: Observable) { } - - /** - * Starts a new chain and specifies the initial `this` value. - * @param context Initial `this` value for the chain. - */ - static from(context: Observable): StrictRxChain { - return new RxChain(context); - } - - /** - * Invokes an RxJS operator as a part of the chain. - * @param operator Operator to be invoked. - * @param args Arguments to be passed to the operator. - */ - call(operator: Function, ...args: any[]): RxChain { - this._context = operator.call(this._context, ...args); - return this; - } - - /** - * Subscribes to the result of the chain. - * @param fn Callback to be invoked when the result emits a value. - */ - subscribe(fn: (t: T) => void): Subscription { - return this._context.subscribe(fn); - } - - /** - * Returns the result of the chain. - */ - result(): Observable { - return this._context; - } -} diff --git a/src/cdk/rxjs/rx-operators.ts b/src/cdk/rxjs/rx-operators.ts deleted file mode 100644 index a404ac2a47a1..000000000000 --- a/src/cdk/rxjs/rx-operators.ts +++ /dev/null @@ -1,124 +0,0 @@ -/** - * @license - * Copyright Google Inc. All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ - -import {Observable, ObservableInput} from 'rxjs/Observable'; -import {PartialObserver} from 'rxjs/Observer'; -import {Subscription} from 'rxjs/Subscription'; -import {IScheduler} from 'rxjs/Scheduler'; -import {_finally as _finallyOperator} from 'rxjs/operator/finally'; -import {_catch as _catchOperator} from 'rxjs/operator/catch'; -import {_do as _doOperator} from 'rxjs/operator/do'; -import {map as mapOperator} from 'rxjs/operator/map'; -import {filter as filterOperator} from 'rxjs/operator/filter'; -import {share as shareOperator} from 'rxjs/operator/share'; -import {first as firstOperator} from 'rxjs/operator/first'; -import {switchMap as switchMapOperator} from 'rxjs/operator/switchMap'; -import {startWith as startWithOperator} from 'rxjs/operator/startWith'; -import {debounceTime as debounceTimeOperator} from 'rxjs/operator/debounceTime'; -import {auditTime as auditTimeOperator} from 'rxjs/operator/auditTime'; -import {takeUntil as takeUntilOperator} from 'rxjs/operator/takeUntil'; - -/** - * Represents a strongly-typed chain of RxJS operators. - * - * We achieve strict type enforcement on the chained operators by creating types that - * *unambiguously* match specific rxjs operators. These unambiguous types are created by - * intersecting a "brand" to the `typeof` the existing operator. The brand (a class with a private - * member) effectively forces nominal typing for the operators. This allows typescript to understand - * that, for example, `filter` is *`filter`* and not, say, a map of T => boolean. - * - * The downside to this approach is that operators must be imported in their type-coerced form - * rather than from the normal rxjs location. - */ -export interface StrictRxChain { - call(operator: mapOperatorType, - project: (value: T, index: number) => R, thisArg?: any): StrictRxChain; - - call(operator: switchMapOperatorType, - project: (value: T, index: number) => ObservableInput): StrictRxChain; - - call(operator: catchOperatorType, - selector: (err: any, caught: Observable) => ObservableInput): StrictRxChain; - - call(operator: filterOperatorType, - predicate: (value: T, index: number) => boolean, thisArg?: any): StrictRxChain; - - call(operator: shareOperatorType): StrictRxChain; - - call(operator: finallyOperatorType, action: () => void): StrictRxChain; - - call(operator: doOperatorType, next: (x: T) => void, error?: - (e: any) => void, complete?: () => void): StrictRxChain; - - call(operator: doOperatorType, observer: PartialObserver): StrictRxChain; - - call(operator: firstOperatorType, thisArg?: any, defaultValue?: any): StrictRxChain; - - call(operator: firstOperatorType, predicate: (value: T) => boolean): StrictRxChain; - - call(operator: startWithOperatorType, ...args: any[]): StrictRxChain; - - call(operator: debounceTimeOperatorType, dueTime: number, - scheduler?: IScheduler): StrictRxChain; - - call(operator: auditTimeOperatorType, duration: number, - scheduler?: IScheduler): StrictRxChain; - - call(operator: takeUntilOperatorType, notifier: Observable): StrictRxChain; - - subscribe(fn: (t: T) => void): Subscription; - - result(): Observable; -} - - -export class FinallyBrand { private _; } -export class CatchBrand { private _; } -export class DoBrand { private _; } -export class MapBrand { private _; } -export class FilterBrand { private _; } -export class ShareBrand { private _; } -export class FirstBrand { private _; } -export class SwitchMapBrand { private _; } -export class StartWithBrand { private _; } -export class DebounceTimeBrand { private _; } -export class AuditTimeBrand { private _; } -export class TakeUntilBrand { private _; } - - -export type finallyOperatorType = typeof _finallyOperator & FinallyBrand; -export type catchOperatorType = typeof _catchOperator & CatchBrand; -export type doOperatorType = typeof _doOperator & DoBrand; -export type mapOperatorType = typeof mapOperator & MapBrand; -export type filterOperatorType = typeof filterOperator & FilterBrand; -export type shareOperatorType = typeof shareOperator & ShareBrand; -export type firstOperatorType = typeof firstOperator & FirstBrand; -export type switchMapOperatorType = typeof switchMapOperator & SwitchMapBrand; -export type startWithOperatorType = typeof startWithOperator & StartWithBrand; -export type debounceTimeOperatorType = typeof debounceTimeOperator & DebounceTimeBrand; -export type auditTimeOperatorType = typeof auditTimeOperator & AuditTimeBrand; -export type takeUntilOperatorType = typeof takeUntilOperator & TakeUntilBrand; - -// We add `Function` to the type intersection to make this nomically different from -// `finallyOperatorType` while still being structurally the same. Without this, TypeScript tries to -// reduce `typeof _finallyOperator & FinallyBrand` to `finallyOperatorType` and then fails -// because `T` isn't known. -export const finallyOperator = - _finallyOperator as typeof _finallyOperator & FinallyBrand & Function; -export const catchOperator = _catchOperator as typeof _catchOperator & CatchBrand & Function; -export const doOperator = _doOperator as typeof _doOperator & DoBrand & Function; -export const map = mapOperator as typeof mapOperator & MapBrand & Function; -export const filter = filterOperator as typeof filterOperator & FilterBrand & Function; -export const share = shareOperator as typeof shareOperator & ShareBrand & Function; -export const first = firstOperator as typeof firstOperator & FirstBrand & Function; -export const switchMap = switchMapOperator as typeof switchMapOperator & SwitchMapBrand & Function; -export const startWith = startWithOperator as typeof startWithOperator & StartWithBrand & Function; -export const debounceTime = - debounceTimeOperator as typeof debounceTimeOperator & DebounceTimeBrand & Function; -export const auditTime = auditTimeOperator as typeof auditTimeOperator & AuditTimeBrand & Function; -export const takeUntil = takeUntilOperator as typeof takeUntilOperator & TakeUntilBrand & Function; diff --git a/src/cdk/scrolling/BUILD.bazel b/src/cdk/scrolling/BUILD.bazel new file mode 100644 index 000000000000..505982ca2591 --- /dev/null +++ b/src/cdk/scrolling/BUILD.bazel @@ -0,0 +1,12 @@ +package(default_visibility=["//visibility:public"]) +load("@angular//:index.bzl", "ng_module") + +ng_module( + name = "scrolling", + srcs = glob(["**/*.ts"], exclude=["**/*.spec.ts"]), + module_name = "@angular/cdk/scrolling", + deps = [ + "//src/cdk/platform", + ], + tsconfig = ":tsconfig-build.json", +) diff --git a/src/cdk/scrolling/index.ts b/src/cdk/scrolling/index.ts index f93e7c31d564..676ca90f1ffa 100644 --- a/src/cdk/scrolling/index.ts +++ b/src/cdk/scrolling/index.ts @@ -1,9 +1,9 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -export * from './public_api'; +export * from './public-api'; diff --git a/src/cdk/scrolling/public-api.ts b/src/cdk/scrolling/public-api.ts new file mode 100644 index 000000000000..ffbd65d14e06 --- /dev/null +++ b/src/cdk/scrolling/public-api.ts @@ -0,0 +1,12 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +export * from './scroll-dispatcher'; +export * from './scrollable'; +export * from './viewport-ruler'; +export * from './scrolling-module'; diff --git a/src/cdk/scrolling/scroll-dispatcher.spec.ts b/src/cdk/scrolling/scroll-dispatcher.spec.ts index d08dc5f8cf4c..b4c249f4e556 100644 --- a/src/cdk/scrolling/scroll-dispatcher.spec.ts +++ b/src/cdk/scrolling/scroll-dispatcher.spec.ts @@ -1,6 +1,6 @@ import {inject, TestBed, async, fakeAsync, ComponentFixture, tick} from '@angular/core/testing'; import {NgModule, Component, ViewChild, ElementRef} from '@angular/core'; -import {Scrollable, ScrollDispatcher, ScrollDispatchModule} from './public_api'; +import {CdkScrollable, ScrollDispatcher, ScrollDispatchModule} from './public-api'; import {dispatchFakeEvent} from '@angular/cdk/testing'; describe('Scroll Dispatcher', () => { @@ -26,15 +26,15 @@ describe('Scroll Dispatcher', () => { it('should be registered with the scrollable directive with the scroll service', () => { const componentScrollable = fixture.componentInstance.scrollable; - expect(scroll.scrollableReferences.has(componentScrollable)).toBe(true); + expect(scroll.scrollContainers.has(componentScrollable)).toBe(true); }); it('should have the scrollable directive deregistered when the component is destroyed', () => { const componentScrollable = fixture.componentInstance.scrollable; - expect(scroll.scrollableReferences.has(componentScrollable)).toBe(true); + expect(scroll.scrollContainers.has(componentScrollable)).toBe(true); fixture.destroy(); - expect(scroll.scrollableReferences.has(componentScrollable)).toBe(false); + expect(scroll.scrollContainers.has(componentScrollable)).toBe(false); }); it('should notify through the directive and service that a scroll event occurred', @@ -47,12 +47,12 @@ describe('Scroll Dispatcher', () => { // Listen for notifications from scroll service with a throttle of 100ms const throttleTime = 100; const serviceSpy = jasmine.createSpy('service scroll callback'); - scroll.scrolled(throttleTime, serviceSpy); + scroll.scrolled(throttleTime).subscribe(serviceSpy); // Emit a scroll event from the scrolling element in our component. // This event should be picked up by the scrollable directive and notify. // The notification should be picked up by the service. - dispatchFakeEvent(fixture.componentInstance.scrollingElement.nativeElement, 'scroll'); + dispatchFakeEvent(fixture.componentInstance.scrollingElement.nativeElement, 'scroll', false); // The scrollable directive should have notified the service immediately. expect(directiveSpy).toHaveBeenCalled(); @@ -67,36 +67,26 @@ describe('Scroll Dispatcher', () => { })); it('should not execute the global events in the Angular zone', () => { - const spy = jasmine.createSpy('zone unstable callback'); - const subscription = fixture.ngZone!.onUnstable.subscribe(spy); - - scroll.scrolled(0, () => {}); - dispatchFakeEvent(document, 'scroll'); - dispatchFakeEvent(window, 'resize'); + scroll.scrolled(0).subscribe(() => {}); + dispatchFakeEvent(document, 'scroll', false); - expect(spy).not.toHaveBeenCalled(); - subscription.unsubscribe(); + expect(fixture.ngZone!.isStable).toBe(true); }); it('should not execute the scrollable events in the Angular zone', () => { - const spy = jasmine.createSpy('zone unstable callback'); - const subscription = fixture.ngZone!.onUnstable.subscribe(spy); - dispatchFakeEvent(fixture.componentInstance.scrollingElement.nativeElement, 'scroll'); - - expect(spy).not.toHaveBeenCalled(); - subscription.unsubscribe(); + expect(fixture.ngZone!.isStable).toBe(true); }); it('should be able to unsubscribe from the global scrollable', () => { const spy = jasmine.createSpy('global scroll callback'); - const subscription = scroll.scrolled(0, spy); + const subscription = scroll.scrolled(0).subscribe(spy); - dispatchFakeEvent(document, 'scroll'); + dispatchFakeEvent(document, 'scroll', false); expect(spy).toHaveBeenCalledTimes(1); subscription.unsubscribe(); - dispatchFakeEvent(document, 'scroll'); + dispatchFakeEvent(document, 'scroll', false); expect(spy).toHaveBeenCalledTimes(1); }); @@ -105,22 +95,48 @@ describe('Scroll Dispatcher', () => { describe('Nested scrollables', () => { let scroll: ScrollDispatcher; let fixture: ComponentFixture; + let element: ElementRef; beforeEach(inject([ScrollDispatcher], (s: ScrollDispatcher) => { scroll = s; fixture = TestBed.createComponent(NestedScrollingComponent); fixture.detectChanges(); + element = fixture.componentInstance.interestingElement; })); it('should be able to identify the containing scrollables of an element', () => { - const interestingElement = fixture.componentInstance.interestingElement; - const scrollContainers = scroll.getScrollContainers(interestingElement); + const scrollContainers = scroll.getAncestorScrollContainers(element); const scrollableElementIds = scrollContainers.map(scrollable => scrollable.getElementRef().nativeElement.id); expect(scrollableElementIds).toEqual(['scrollable-1', 'scrollable-1a']); }); + + it('should emit when one of the ancestor scrollable containers is scrolled', () => { + const spy = jasmine.createSpy('scroll spy'); + const subscription = scroll.ancestorScrolled(element, 0).subscribe(spy); + const grandparent = fixture.debugElement.nativeElement.querySelector('#scrollable-1'); + + dispatchFakeEvent(grandparent, 'scroll', false); + expect(spy).toHaveBeenCalledTimes(1); + + dispatchFakeEvent(window.document, 'scroll', false); + expect(spy).toHaveBeenCalledTimes(2); + + subscription.unsubscribe(); + }); + + it('should not emit when a non-ancestor is scrolled', () => { + const spy = jasmine.createSpy('scroll spy'); + const subscription = scroll.ancestorScrolled(element, 0).subscribe(spy); + const stranger = fixture.debugElement.nativeElement.querySelector('#scrollable-2'); + + dispatchFakeEvent(stranger, 'scroll', false); + expect(spy).not.toHaveBeenCalled(); + + subscription.unsubscribe(); + }); }); describe('lazy subscription', () => { @@ -133,7 +149,25 @@ describe('Scroll Dispatcher', () => { it('should lazily add global listeners as service subscriptions are added and removed', () => { expect(scroll._globalSubscription).toBeNull('Expected no global listeners on init.'); - const subscription = scroll.scrolled(0, () => {}); + const subscription = scroll.scrolled(0).subscribe(() => {}); + + expect(scroll._globalSubscription).toBeTruthy( + 'Expected global listeners after a subscription has been added.'); + + subscription.unsubscribe(); + + expect(scroll._globalSubscription).toBeNull( + 'Expected global listeners to have been removed after the subscription has stopped.'); + }); + + it('should remove global listeners on unsubscribe, despite any other live scrollables', () => { + const fixture = TestBed.createComponent(NestedScrollingComponent); + fixture.detectChanges(); + + expect(scroll._globalSubscription).toBeNull('Expected no global listeners on init.'); + expect(scroll.scrollContainers.size).toBe(4, 'Expected multiple scrollables'); + + const subscription = scroll.scrolled(0).subscribe(() => {}); expect(scroll._globalSubscription).toBeTruthy( 'Expected global listeners after a subscription has been added.'); @@ -142,6 +176,8 @@ describe('Scroll Dispatcher', () => { expect(scroll._globalSubscription).toBeNull( 'Expected global listeners to have been removed after the subscription has stopped.'); + expect(scroll.scrollContainers.size) + .toBe(4, 'Expected scrollable count to stay the same'); }); }); @@ -153,7 +189,7 @@ describe('Scroll Dispatcher', () => { template: `
` }) class ScrollingComponent { - @ViewChild(Scrollable) scrollable: Scrollable; + @ViewChild(CdkScrollable) scrollable: CdkScrollable; @ViewChild('scrollingElement') scrollingElement: ElementRef; } diff --git a/src/cdk/scrolling/scroll-dispatcher.ts b/src/cdk/scrolling/scroll-dispatcher.ts index 177f830ac2c0..5d5c3949777e 100644 --- a/src/cdk/scrolling/scroll-dispatcher.ts +++ b/src/cdk/scrolling/scroll-dispatcher.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -10,10 +10,12 @@ import {ElementRef, Injectable, NgZone, Optional, SkipSelf} from '@angular/core' import {Platform} from '@angular/cdk/platform'; import {Subject} from 'rxjs/Subject'; import {Subscription} from 'rxjs/Subscription'; +import {Observable} from 'rxjs/Observable'; +import {of as observableOf} from 'rxjs/observable/of'; import {fromEvent} from 'rxjs/observable/fromEvent'; -import {merge} from 'rxjs/observable/merge'; -import {auditTime} from 'rxjs/operator/auditTime'; -import {Scrollable} from './scrollable'; +import {auditTime} from 'rxjs/operators/auditTime'; +import {filter} from 'rxjs/operators/filter'; +import {CdkScrollable} from './scrollable'; /** Time in ms to throttle the scrolling events by default. */ @@ -28,7 +30,7 @@ export class ScrollDispatcher { constructor(private _ngZone: NgZone, private _platform: Platform) { } /** Subject for notifying that a registered scrollable reference element has been scrolled. */ - _scrolled: Subject = new Subject(); + private _scrolled = new Subject(); /** Keeps track of the global `scroll` and `resize` subscriptions. */ _globalSubscription: Subscription | null = null; @@ -40,82 +42,89 @@ export class ScrollDispatcher { * Map of all the scrollable references that are registered with the service and their * scroll event subscriptions. */ - scrollableReferences: Map = new Map(); + scrollContainers: Map = new Map(); /** - * Registers a Scrollable with the service and listens for its scrolled events. When the - * scrollable is scrolled, the service emits the event in its scrolled observable. + * Registers a scrollable instance with the service and listens for its scrolled events. When the + * scrollable is scrolled, the service emits the event to its scrolled observable. * @param scrollable Scrollable instance to be registered. */ - register(scrollable: Scrollable): void { - const scrollSubscription = scrollable.elementScrolled().subscribe(() => this._notify()); + register(scrollable: CdkScrollable): void { + const scrollSubscription = scrollable.elementScrolled() + .subscribe(() => this._scrolled.next(scrollable)); - this.scrollableReferences.set(scrollable, scrollSubscription); + this.scrollContainers.set(scrollable, scrollSubscription); } /** * Deregisters a Scrollable reference and unsubscribes from its scroll event observable. * @param scrollable Scrollable instance to be deregistered. */ - deregister(scrollable: Scrollable): void { - const scrollableReference = this.scrollableReferences.get(scrollable); + deregister(scrollable: CdkScrollable): void { + const scrollableReference = this.scrollContainers.get(scrollable); if (scrollableReference) { scrollableReference.unsubscribe(); - this.scrollableReferences.delete(scrollable); + this.scrollContainers.delete(scrollable); } } /** - * Subscribes to an observable that emits an event whenever any of the registered Scrollable + * Returns an observable that emits an event whenever any of the registered Scrollable * references (or window, document, or body) fire a scrolled event. Can provide a time in ms * to override the default "throttle" time. + * + * **Note:** in order to avoid hitting change detection for every scroll event, + * all of the events emitted from this stream will be run outside the Angular zone. + * If you need to update any data bindings as a result of a scroll event, you have + * to run the callback using `NgZone.run`. */ - scrolled(auditTimeInMs: number = DEFAULT_SCROLL_TIME, callback: () => any): Subscription { - // Scroll events can only happen on the browser, so do nothing if we're not on the browser. - if (!this._platform.isBrowser) { - return Subscription.EMPTY; - } + scrolled(auditTimeInMs: number = DEFAULT_SCROLL_TIME): Observable { + return this._platform.isBrowser ? Observable.create(observer => { + if (!this._globalSubscription) { + this._addGlobalListener(); + } - // In the case of a 0ms delay, use an observable without auditTime - // since it does add a perceptible delay in processing overhead. - let observable = auditTimeInMs > 0 ? - auditTime.call(this._scrolled.asObservable(), auditTimeInMs) : - this._scrolled.asObservable(); - - this._scrolledCount++; - - if (!this._globalSubscription) { - this._globalSubscription = this._ngZone.runOutsideAngular(() => { - return merge( - fromEvent(window.document, 'scroll'), - fromEvent(window, 'resize') - ).subscribe(() => this._notify()); - }); - } + // In the case of a 0ms delay, use an observable without auditTime + // since it does add a perceptible delay in processing overhead. + const subscription = auditTimeInMs > 0 ? + this._scrolled.pipe(auditTime(auditTimeInMs)).subscribe(observer) : + this._scrolled.subscribe(observer); - // Note that we need to do the subscribing from here, in order to be able to remove - // the global event listeners once there are no more subscriptions. - let subscription = observable.subscribe(callback); + this._scrolledCount++; - subscription.add(() => { - this._scrolledCount--; + return () => { + subscription.unsubscribe(); + this._scrolledCount--; - if (this._globalSubscription && !this.scrollableReferences.size && !this._scrolledCount) { - this._globalSubscription.unsubscribe(); - this._globalSubscription = null; - } - }); + if (this._globalSubscription && !this._scrolledCount) { + this._globalSubscription.unsubscribe(); + this._globalSubscription = null; + } + }; + }) : observableOf(); + } - return subscription; + /** + * Returns an observable that emits whenever any of the + * scrollable ancestors of an element are scrolled. + * @param elementRef Element whose ancestors to listen for. + * @param auditTimeInMs Time to throttle the scroll events. + */ + ancestorScrolled(elementRef: ElementRef, auditTimeInMs?: number): Observable { + const ancestors = this.getAncestorScrollContainers(elementRef); + + return this.scrolled(auditTimeInMs).pipe(filter(target => { + return !target || ancestors.indexOf(target) > -1; + })); } /** Returns all registered Scrollables that contain the provided element. */ - getScrollContainers(elementRef: ElementRef): Scrollable[] { - const scrollingContainers: Scrollable[] = []; + getAncestorScrollContainers(elementRef: ElementRef): CdkScrollable[] { + const scrollingContainers: CdkScrollable[] = []; - this.scrollableReferences.forEach((_subscription: Subscription, scrollable: Scrollable) => { - if (this.scrollableContainsElement(scrollable, elementRef)) { + this.scrollContainers.forEach((_subscription: Subscription, scrollable: CdkScrollable) => { + if (this._scrollableContainsElement(scrollable, elementRef)) { scrollingContainers.push(scrollable); } }); @@ -124,7 +133,7 @@ export class ScrollDispatcher { } /** Returns true if the element is contained within the provided Scrollable. */ - scrollableContainsElement(scrollable: Scrollable, elementRef: ElementRef): boolean { + private _scrollableContainsElement(scrollable: CdkScrollable, elementRef: ElementRef): boolean { let element = elementRef.nativeElement; let scrollableElement = scrollable.getElementRef().nativeElement; @@ -137,9 +146,11 @@ export class ScrollDispatcher { return false; } - /** Sends a notification that a scroll event has been fired. */ - _notify() { - this._scrolled.next(); + /** Sets up the global scroll and resize listeners. */ + private _addGlobalListener() { + this._globalSubscription = this._ngZone.runOutsideAngular(() => { + return fromEvent(window.document, 'scroll').subscribe(() => this._scrolled.next()); + }); } } diff --git a/src/cdk/scrolling/scrollable.ts b/src/cdk/scrolling/scrollable.ts index 17e185e75a67..3d9fb8a6d0b5 100644 --- a/src/cdk/scrolling/scrollable.ts +++ b/src/cdk/scrolling/scrollable.ts @@ -1,12 +1,12 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import {Directive, ElementRef, OnInit, OnDestroy, NgZone, Renderer2} from '@angular/core'; +import {Directive, ElementRef, OnInit, OnDestroy, NgZone} from '@angular/core'; import {Observable} from 'rxjs/Observable'; import {Subject} from 'rxjs/Subject'; import {ScrollDispatcher} from './scroll-dispatcher'; @@ -20,20 +20,17 @@ import {ScrollDispatcher} from './scroll-dispatcher'; @Directive({ selector: '[cdk-scrollable], [cdkScrollable]' }) -export class Scrollable implements OnInit, OnDestroy { +export class CdkScrollable implements OnInit, OnDestroy { private _elementScrolled: Subject = new Subject(); - private _scrollListener: Function | null; + private _scrollListener = (event: Event) => this._elementScrolled.next(event); constructor(private _elementRef: ElementRef, private _scroll: ScrollDispatcher, - private _ngZone: NgZone, - private _renderer: Renderer2) {} + private _ngZone: NgZone) {} ngOnInit() { - this._scrollListener = this._ngZone.runOutsideAngular(() => { - return this._renderer.listen(this.getElementRef().nativeElement, 'scroll', (event: Event) => { - this._elementScrolled.next(event); - }); + this._ngZone.runOutsideAngular(() => { + this.getElementRef().nativeElement.addEventListener('scroll', this._scrollListener); }); this._scroll.register(this); @@ -43,8 +40,7 @@ export class Scrollable implements OnInit, OnDestroy { this._scroll.deregister(this); if (this._scrollListener) { - this._scrollListener(); - this._scrollListener = null; + this.getElementRef().nativeElement.removeEventListener('scroll', this._scrollListener); } } diff --git a/src/cdk/scrolling/public_api.ts b/src/cdk/scrolling/scrolling-module.ts similarity index 64% rename from src/cdk/scrolling/public_api.ts rename to src/cdk/scrolling/scrolling-module.ts index 96b6f3a842bf..616f29d07581 100644 --- a/src/cdk/scrolling/public_api.ts +++ b/src/cdk/scrolling/scrolling-module.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -8,17 +8,13 @@ import {NgModule} from '@angular/core'; import {SCROLL_DISPATCHER_PROVIDER} from './scroll-dispatcher'; -import {Scrollable} from './scrollable'; +import {CdkScrollable} from './scrollable'; import {PlatformModule} from '@angular/cdk/platform'; @NgModule({ imports: [PlatformModule], - exports: [Scrollable], - declarations: [Scrollable], + exports: [CdkScrollable], + declarations: [CdkScrollable], providers: [SCROLL_DISPATCHER_PROVIDER], }) export class ScrollDispatchModule {} - -export * from './scroll-dispatcher'; -export * from './scrollable'; -export * from './viewport-ruler'; diff --git a/src/cdk/scrolling/scrolling.md b/src/cdk/scrolling/scrolling.md new file mode 100644 index 000000000000..67adae01ec95 --- /dev/null +++ b/src/cdk/scrolling/scrolling.md @@ -0,0 +1,10 @@ +The `scrolling` package provides helpers for directives that react to scroll events. + +### cdkScrollable and ScrollDispatcher +The `cdkScrollable` directive and the `ScrollDispatcher` service to together to allow components to +react to scrolling in any of its ancestor scrolling containers. + +The `cdkScrollable` directive should be applied to any element that acts as a scrolling container. +This marks the element as a `Scrollable` and registers it with the `ScrollDispatcher`. The +dispatcher, then, allows components to share both event listeners and knowledge of all of the +scrollable containers in the application. diff --git a/src/cdk/scrolling/tsconfig-build.json b/src/cdk/scrolling/tsconfig-build.json index 20b5757fb12c..c15183d45540 100644 --- a/src/cdk/scrolling/tsconfig-build.json +++ b/src/cdk/scrolling/tsconfig-build.json @@ -1,7 +1,7 @@ { "extends": "../tsconfig-build", "files": [ - "public_api.ts" + "public-api.ts" ], "angularCompilerOptions": { "annotateForClosureCompiler": true, diff --git a/src/cdk/scrolling/viewport-ruler.spec.ts b/src/cdk/scrolling/viewport-ruler.spec.ts index 349b00efb186..78546a5489dd 100644 --- a/src/cdk/scrolling/viewport-ruler.spec.ts +++ b/src/cdk/scrolling/viewport-ruler.spec.ts @@ -1,6 +1,7 @@ -import {TestBed, inject} from '@angular/core/testing'; -import {ScrollDispatchModule} from './public_api'; +import {TestBed, inject, fakeAsync, tick} from '@angular/core/testing'; +import {ScrollDispatchModule} from './public-api'; import {ViewportRuler, VIEWPORT_RULER_PROVIDER} from './viewport-ruler'; +import {dispatchFakeEvent} from '@angular/cdk/testing'; // For all tests, we assume the browser window is 1024x786 (outerWidth x outerHeight). @@ -32,6 +33,16 @@ describe('ViewportRuler', () => { scrollTo(0, 0); })); + afterEach(() => { + ruler.ngOnDestroy(); + }); + + it('should get the viewport size', () => { + let size = ruler.getViewportSize(); + expect(size.width).toBe(window.innerWidth); + expect(size.height).toBe(window.innerHeight); + }); + it('should get the viewport bounds when the page is not scrolled', () => { let bounds = ruler.getViewportRect(); expect(bounds.top).toBe(0); @@ -44,8 +55,6 @@ describe('ViewportRuler', () => { document.body.appendChild(veryLargeElement); scrollTo(1500, 2000); - // Force an update of the cached viewport geometries because IE11 emits the scroll event later. - ruler._cacheViewportGeometry(); let bounds = ruler.getViewportRect(); @@ -82,8 +91,6 @@ describe('ViewportRuler', () => { document.body.appendChild(veryLargeElement); scrollTo(1500, 2000); - // Force an update of the cached viewport geometries because IE11 emits the scroll event later. - ruler._cacheViewportGeometry(); // In the iOS simulator (BrowserStack & SauceLabs), adding the content to the // body causes karma's iframe for the test to stretch to fit that content once we attempt to @@ -101,4 +108,37 @@ describe('ViewportRuler', () => { document.body.removeChild(veryLargeElement); }); + + describe('changed event', () => { + it('should dispatch an event when the window is resized', () => { + const spy = jasmine.createSpy('viewport changed spy'); + const subscription = ruler.change(0).subscribe(spy); + + dispatchFakeEvent(window, 'resize'); + expect(spy).toHaveBeenCalled(); + subscription.unsubscribe(); + }); + + it('should dispatch an event when the orientation is changed', () => { + const spy = jasmine.createSpy('viewport changed spy'); + const subscription = ruler.change(0).subscribe(spy); + + dispatchFakeEvent(window, 'orientationchange'); + expect(spy).toHaveBeenCalled(); + subscription.unsubscribe(); + }); + + it('should be able to throttle the callback', fakeAsync(() => { + const spy = jasmine.createSpy('viewport changed spy'); + const subscription = ruler.change(1337).subscribe(spy); + + dispatchFakeEvent(window, 'resize'); + expect(spy).not.toHaveBeenCalled(); + + tick(1337); + + expect(spy).toHaveBeenCalledTimes(1); + subscription.unsubscribe(); + })); + }); }); diff --git a/src/cdk/scrolling/viewport-ruler.ts b/src/cdk/scrolling/viewport-ruler.ts index fbd55ad9e2d6..a1c8435505c4 100644 --- a/src/cdk/scrolling/viewport-ruler.ts +++ b/src/cdk/scrolling/viewport-ruler.ts @@ -1,38 +1,61 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import {Injectable, Optional, SkipSelf} from '@angular/core'; -import {ScrollDispatcher} from './scroll-dispatcher'; +import {Injectable, Optional, SkipSelf, NgZone, OnDestroy} from '@angular/core'; +import {Platform} from '@angular/cdk/platform'; +import {Observable} from 'rxjs/Observable'; +import {fromEvent} from 'rxjs/observable/fromEvent'; +import {merge} from 'rxjs/observable/merge'; +import {auditTime} from 'rxjs/operators/auditTime'; +import {Subscription} from 'rxjs/Subscription'; +import {of as observableOf} from 'rxjs/observable/of'; +/** Time in ms to throttle the resize events by default. */ +export const DEFAULT_RESIZE_TIME = 20; /** * Simple utility for getting the bounds of the browser viewport. * @docs-private */ @Injectable() -export class ViewportRuler { +export class ViewportRuler implements OnDestroy { + /** Cached viewport dimensions. */ + private _viewportSize: {width: number; height: number}; - /** Cached document client rectangle. */ - private _documentRect?: ClientRect; + /** Stream of viewport change events. */ + private _change: Observable; - constructor(scrollDispatcher: ScrollDispatcher) { - // Subscribe to scroll and resize events and update the document rectangle on changes. - scrollDispatcher.scrolled(0, () => this._cacheViewportGeometry()); + /** Subscription to streams that invalidate the cached viewport dimensions. */ + private _invalidateCache: Subscription; + + constructor(platform: Platform, ngZone: NgZone) { + this._change = platform.isBrowser ? ngZone.runOutsideAngular(() => { + return merge(fromEvent(window, 'resize'), fromEvent(window, 'orientationchange')); + }) : observableOf(); + + this._invalidateCache = this.change().subscribe(() => this._updateViewportSize()); } - /** Gets a ClientRect for the viewport's bounds. */ - getViewportRect(documentRect = this._documentRect): ClientRect { - // Cache the document bounding rect so that we don't recompute it for multiple calls. - if (!documentRect) { - this._cacheViewportGeometry(); - documentRect = this._documentRect; + ngOnDestroy() { + this._invalidateCache.unsubscribe(); + } + + /** Returns the viewport's width and height. */ + getViewportSize(): Readonly<{width: number, height: number}> { + if (!this._viewportSize) { + this._updateViewportSize(); } + return {width: this._viewportSize.width, height: this._viewportSize.height}; + } + + /** Gets a ClientRect for the viewport's bounds. */ + getViewportRect(): ClientRect { // Use the document element's bounding rect rather than the window scroll properties // (e.g. pageYOffset, scrollY) due to in issue in Chrome and IE where window scroll // properties and client coordinates (boundingClientRect, clientX/Y, etc.) are in different @@ -42,9 +65,8 @@ export class ViewportRuler { // We use the documentElement instead of the body because, by default (without a css reset) // browsers typically give the document body an 8px margin, which is not included in // getBoundingClientRect(). - const scrollPosition = this.getViewportScrollPosition(documentRect); - const height = window.innerHeight; - const width = window.innerWidth; + const scrollPosition = this.getViewportScrollPosition(); + const {width, height} = this.getViewportSize(); return { top: scrollPosition.top, @@ -56,50 +78,50 @@ export class ViewportRuler { }; } - - /** - * Gets the (top, left) scroll position of the viewport. - * @param documentRect - */ - getViewportScrollPosition(documentRect = this._documentRect) { - // Cache the document bounding rect so that we don't recompute it for multiple calls. - if (!documentRect) { - this._cacheViewportGeometry(); - documentRect = this._documentRect; - } - + /** Gets the (top, left) scroll position of the viewport. */ + getViewportScrollPosition() { // The top-left-corner of the viewport is determined by the scroll position of the document // body, normally just (scrollLeft, scrollTop). However, Chrome and Firefox disagree about // whether `document.body` or `document.documentElement` is the scrolled element, so reading // `scrollTop` and `scrollLeft` is inconsistent. However, using the bounding rect of // `document.documentElement` works consistently, where the `top` and `left` values will // equal negative the scroll position. - const top = -documentRect!.top || document.body.scrollTop || window.scrollY || - document.documentElement.scrollTop || 0; + const documentRect = document.documentElement.getBoundingClientRect(); - const left = -documentRect!.left || document.body.scrollLeft || window.scrollX || + const top = -documentRect.top || document.body.scrollTop || window.scrollY || + document.documentElement.scrollTop || 0; + + const left = -documentRect.left || document.body.scrollLeft || window.scrollX || document.documentElement.scrollLeft || 0; return {top, left}; } - /** Caches the latest client rectangle of the document element. */ - _cacheViewportGeometry() { - this._documentRect = document.documentElement.getBoundingClientRect(); + /** + * Returns a stream that emits whenever the size of the viewport changes. + * @param throttle Time in milliseconds to throttle the stream. + */ + change(throttleTime: number = DEFAULT_RESIZE_TIME): Observable { + return throttleTime > 0 ? this._change.pipe(auditTime(throttleTime)) : this._change; } + /** Updates the cached viewport size. */ + private _updateViewportSize() { + this._viewportSize = {width: window.innerWidth, height: window.innerHeight}; + } } /** @docs-private */ export function VIEWPORT_RULER_PROVIDER_FACTORY(parentRuler: ViewportRuler, - scrollDispatcher: ScrollDispatcher) { - return parentRuler || new ViewportRuler(scrollDispatcher); + platform: Platform, + ngZone: NgZone) { + return parentRuler || new ViewportRuler(platform, ngZone); } /** @docs-private */ export const VIEWPORT_RULER_PROVIDER = { // If there is already a ViewportRuler available, use that. Otherwise, provide a new one. provide: ViewportRuler, - deps: [[new Optional(), new SkipSelf(), ViewportRuler], ScrollDispatcher], + deps: [[new Optional(), new SkipSelf(), ViewportRuler], Platform, NgZone], useFactory: VIEWPORT_RULER_PROVIDER_FACTORY }; diff --git a/src/cdk/scrolling/viewport.md b/src/cdk/scrolling/viewport.md new file mode 100644 index 000000000000..023088a5552a --- /dev/null +++ b/src/cdk/scrolling/viewport.md @@ -0,0 +1,3 @@ +### Viewport + +A utility for getting the bounds of the browser viewport. \ No newline at end of file diff --git a/src/cdk/stepper/BUILD.bazel b/src/cdk/stepper/BUILD.bazel new file mode 100644 index 000000000000..29671b76ae03 --- /dev/null +++ b/src/cdk/stepper/BUILD.bazel @@ -0,0 +1,14 @@ +package(default_visibility=["//visibility:public"]) +load("@angular//:index.bzl", "ng_module") + +ng_module( + name = "stepper", + srcs = glob(["**/*.ts"], exclude=["**/*.spec.ts"]), + module_name = "@angular/cdk/stepper", + deps = [ + "//src/cdk/bidi", + "//src/cdk/coercion", + "//src/cdk/keycodes", + ], + tsconfig = ":tsconfig-build.json", +) diff --git a/src/cdk/rxjs/index.ts b/src/cdk/stepper/index.ts similarity index 66% rename from src/cdk/rxjs/index.ts rename to src/cdk/stepper/index.ts index 2b504ab757d8..676ca90f1ffa 100644 --- a/src/cdk/rxjs/index.ts +++ b/src/cdk/stepper/index.ts @@ -1,11 +1,9 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ - - -export * from './public_api'; +export * from './public-api'; diff --git a/src/cdk/stepper/public-api.ts b/src/cdk/stepper/public-api.ts new file mode 100644 index 000000000000..5ff98ead2f60 --- /dev/null +++ b/src/cdk/stepper/public-api.ts @@ -0,0 +1,12 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +export * from './stepper'; +export * from './step-label'; +export * from './stepper-button'; +export * from './stepper-module'; diff --git a/src/cdk/stepper/step-label.ts b/src/cdk/stepper/step-label.ts new file mode 100644 index 000000000000..a7f048239890 --- /dev/null +++ b/src/cdk/stepper/step-label.ts @@ -0,0 +1,16 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Directive, TemplateRef} from '@angular/core'; + +@Directive({ + selector: '[cdkStepLabel]', +}) +export class CdkStepLabel { + constructor(/** @docs-private */ public template: TemplateRef) { } +} diff --git a/src/cdk/stepper/step.html b/src/cdk/stepper/step.html new file mode 100644 index 000000000000..cd48c06b9917 --- /dev/null +++ b/src/cdk/stepper/step.html @@ -0,0 +1 @@ + diff --git a/src/cdk/stepper/stepper-button.ts b/src/cdk/stepper/stepper-button.ts new file mode 100644 index 000000000000..21b944435e45 --- /dev/null +++ b/src/cdk/stepper/stepper-button.ts @@ -0,0 +1,28 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Directive} from '@angular/core'; +import {CdkStepper} from './stepper'; + +/** Button that moves to the next step in a stepper workflow. */ +@Directive({ + selector: 'button[cdkStepperNext]', + host: {'(click)': '_stepper.next()'} +}) +export class CdkStepperNext { + constructor(public _stepper: CdkStepper) { } +} + +/** Button that moves to the previous step in a stepper workflow. */ +@Directive({ + selector: 'button[cdkStepperPrevious]', + host: {'(click)': '_stepper.previous()'} +}) +export class CdkStepperPrevious { + constructor(public _stepper: CdkStepper) { } +} diff --git a/src/cdk/stepper/stepper-module.ts b/src/cdk/stepper/stepper-module.ts new file mode 100644 index 000000000000..f0a596c7eb64 --- /dev/null +++ b/src/cdk/stepper/stepper-module.ts @@ -0,0 +1,21 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {NgModule} from '@angular/core'; +import {CdkStepper, CdkStep} from './stepper'; +import {CommonModule} from '@angular/common'; +import {CdkStepLabel} from './step-label'; +import {CdkStepperNext, CdkStepperPrevious} from './stepper-button'; +import {BidiModule} from '@angular/cdk/bidi'; + +@NgModule({ + imports: [BidiModule, CommonModule], + exports: [CdkStep, CdkStepper, CdkStepLabel, CdkStepperNext, CdkStepperPrevious], + declarations: [CdkStep, CdkStepper, CdkStepLabel, CdkStepperNext, CdkStepperPrevious] +}) +export class CdkStepperModule {} diff --git a/src/cdk/stepper/stepper.md b/src/cdk/stepper/stepper.md new file mode 100644 index 000000000000..6e5fe2f7f511 --- /dev/null +++ b/src/cdk/stepper/stepper.md @@ -0,0 +1,59 @@ +CDK stepper provides a foundation upon which more concrete stepper varities can be built. A +stepper is a wizard-like workflow that divides content into logical steps + +### Behavior captured by CdkStepper +The base CDK version of the stepper primarily manages which step is active. This includes handling +keyboard interactions and exposing an API for advancing or rewinding through the workflow. + +#### Linear stepper +A stepper marked as `linear` requires the user to complete previous steps before proceeding. +For each step, the `stepControl` attribute can be set to the top level +`AbstractControl` that is used to check the validity of the step. + +There are two possible approaches. One is using a single form for stepper, and the other is +using a different form for each step. + +#### Using a single form for the entire stepper +When using a single form for the stepper, any intermediate next/previous buttons within the steps +must be set to `type="button"` in order to prevent submission of the form before all steps are +complete. + +#### Using a form for each individual step +When using a form for each step, the workflow is advanced whenever one of the forms is submitted. + +### Types of steps + +#### Optional step +If completion of a step in linear stepper is not required, then the `optional` attribute can be set +on `CdkStep` in a `linear` stepper. + +#### Editable step +By default, steps are editable, which means users can return to previously completed steps and +edit their responses. `editable="true"` can be set on `CdkStep` to change the default. + +#### Completed step +By default, the `completed` attribute of a step returns `true` if the step is valid (in case of +linear stepper) and the user has interacted with the step. The user, however, can also override +this default `completed` behavior by setting the `completed` attribute as needed. + +### Stepper buttons +There are two button directives to support navigation between different steps: +`CdkStepperNext` and `CdkStepperPrevious`. When placed inside of a step, these will automatically +add click handlers to advance or rewind the workflow, respectively. + +### Keyboard interaction +- LEFT_ARROW: Focuses the previous step header +- RIGHT_ARROW: Focuses the next step header +- ENTER, SPACE: Selects the step that the focus is currently on +- TAB: Focuses the next tabbable element +- TAB+SHIFT: Focuses the previous tabbable element + +### Accessibility +The CDK stepper is treated as a tabbed view for accessibility purposes, so it is given +`role="tablist"` by default. The header of step that can be clicked to select the step +is given `role="tab"`, and the content that can be expanded upon selection is given +`role="tabpanel"`. `aria-selected` attribute of step header and `aria-expanded` attribute of +step content is automatically set based on step selection change. + +The stepper and each step should be given a meaningful label via `aria-label` or `aria-labelledby`. + diff --git a/src/cdk/stepper/stepper.ts b/src/cdk/stepper/stepper.ts new file mode 100644 index 000000000000..e842ff3a7b02 --- /dev/null +++ b/src/cdk/stepper/stepper.ts @@ -0,0 +1,308 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import { + ContentChildren, + EventEmitter, + Input, + Output, + QueryList, + Directive, + ElementRef, + Component, + ContentChild, + ViewChild, + TemplateRef, + ViewEncapsulation, + Optional, + Inject, + forwardRef, + ChangeDetectionStrategy, + ChangeDetectorRef, + OnChanges, + OnDestroy +} from '@angular/core'; +import {LEFT_ARROW, RIGHT_ARROW, ENTER, SPACE} from '@angular/cdk/keycodes'; +import {CdkStepLabel} from './step-label'; +import {coerceBooleanProperty} from '@angular/cdk/coercion'; +import {AbstractControl} from '@angular/forms'; +import {Direction, Directionality} from '@angular/cdk/bidi'; +import {Subject} from 'rxjs/Subject'; + +/** Used to generate unique ID for each stepper component. */ +let nextId = 0; + +/** + * Position state of the content of each step in stepper that is used for transitioning + * the content into correct position upon step selection change. + */ +export type StepContentPositionState = 'previous' | 'current' | 'next'; + +/** Change event emitted on selection changes. */ +export class StepperSelectionEvent { + /** Index of the step now selected. */ + selectedIndex: number; + + /** Index of the step previously selected. */ + previouslySelectedIndex: number; + + /** The step instance now selected. */ + selectedStep: CdkStep; + + /** The step instance previously selected. */ + previouslySelectedStep: CdkStep; +} + +@Component({ + moduleId: module.id, + selector: 'cdk-step', + exportAs: 'cdkStep', + templateUrl: 'step.html', + encapsulation: ViewEncapsulation.None, + preserveWhitespaces: false, + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class CdkStep implements OnChanges { + /** Template for step label if it exists. */ + @ContentChild(CdkStepLabel) stepLabel: CdkStepLabel; + + /** Template for step content. */ + @ViewChild(TemplateRef) content: TemplateRef; + + /** The top level abstract control of the step. */ + @Input() stepControl: AbstractControl; + + /** Whether user has seen the expanded step content or not. */ + interacted = false; + + /** Label of the step. */ + @Input() label: string; + + /** Whether the user can return to this step once it has been marked as complted. */ + @Input() + get editable(): boolean { return this._editable; } + set editable(value: boolean) { + this._editable = coerceBooleanProperty(value); + } + private _editable = true; + + /** Whether the completion of step is optional. */ + @Input() + get optional(): boolean { return this._optional; } + set optional(value: boolean) { + this._optional = coerceBooleanProperty(value); + } + private _optional = false; + + /** Whether step is marked as completed. */ + @Input() + get completed(): boolean { + return this._customCompleted == null ? this._defaultCompleted : this._customCompleted; + } + set completed(value: boolean) { + this._customCompleted = coerceBooleanProperty(value); + } + private _customCompleted: boolean | null = null; + + private get _defaultCompleted() { + return this.stepControl ? this.stepControl.valid && this.interacted : this.interacted; + } + + constructor(@Inject(forwardRef(() => CdkStepper)) private _stepper: CdkStepper) { } + + /** Selects this step component. */ + select(): void { + this._stepper.selected = this; + } + + ngOnChanges() { + // Since basically all inputs of the MdStep get proxied through the view down to the + // underlying MdStepHeader, we have to make sure that change detection runs correctly. + this._stepper._stateChanged(); + } +} + +@Directive({ + selector: '[cdkStepper]', + exportAs: 'cdkStepper', +}) +export class CdkStepper implements OnDestroy { + /** Emits when the component is destroyed. */ + protected _destroyed = new Subject(); + + /** The list of step components that the stepper is holding. */ + @ContentChildren(CdkStep) _steps: QueryList; + + /** The list of step headers of the steps in the stepper. */ + _stepHeader: QueryList; + + /** Whether the validity of previous steps should be checked or not. */ + @Input() + get linear(): boolean { return this._linear; } + set linear(value: boolean) { this._linear = coerceBooleanProperty(value); } + private _linear = false; + + /** The index of the selected step. */ + @Input() + get selectedIndex() { return this._selectedIndex; } + set selectedIndex(index: number) { + if (this._steps) { + if (this._anyControlsInvalid(index) || index < this._selectedIndex && + !this._steps.toArray()[index].editable) { + // remove focus from clicked step header if the step is not able to be selected + this._stepHeader.toArray()[index].nativeElement.blur(); + } else if (this._selectedIndex != index) { + this._emitStepperSelectionEvent(index); + this._focusIndex = this._selectedIndex; + } + } else { + this._selectedIndex = this._focusIndex = index; + } + } + private _selectedIndex: number = 0; + + /** The step that is selected. */ + @Input() + get selected() { return this._steps.toArray()[this.selectedIndex]; } + set selected(step: CdkStep) { + this.selectedIndex = this._steps.toArray().indexOf(step); + } + + /** Event emitted when the selected step has changed. */ + @Output() selectionChange = new EventEmitter(); + + /** The index of the step that the focus can be set. */ + _focusIndex: number = 0; + + /** Used to track unique ID for each stepper component. */ + _groupId: number; + + constructor( + @Optional() private _dir: Directionality, + private _changeDetectorRef: ChangeDetectorRef) { + this._groupId = nextId++; + } + + ngOnDestroy() { + this._destroyed.next(); + this._destroyed.complete(); + } + + /** Selects and focuses the next step in list. */ + next(): void { + this.selectedIndex = Math.min(this._selectedIndex + 1, this._steps.length - 1); + } + + /** Selects and focuses the previous step in list. */ + previous(): void { + this.selectedIndex = Math.max(this._selectedIndex - 1, 0); + } + + /** Returns a unique id for each step label element. */ + _getStepLabelId(i: number): string { + return `cdk-step-label-${this._groupId}-${i}`; + } + + /** Returns unique id for each step content element. */ + _getStepContentId(i: number): string { + return `cdk-step-content-${this._groupId}-${i}`; + } + + /** Marks the component to be change detected. */ + _stateChanged() { + this._changeDetectorRef.markForCheck(); + } + + /** Returns position state of the step with the given index. */ + _getAnimationDirection(index: number): StepContentPositionState { + const position = index - this._selectedIndex; + if (position < 0) { + return this._layoutDirection() === 'rtl' ? 'next' : 'previous'; + } else if (position > 0) { + return this._layoutDirection() === 'rtl' ? 'previous' : 'next'; + } + return 'current'; + } + + /** Returns the type of icon to be displayed. */ + _getIndicatorType(index: number): 'number' | 'edit' | 'done' { + const step = this._steps.toArray()[index]; + if (!step.completed || this._selectedIndex == index) { + return 'number'; + } else { + return step.editable ? 'edit' : 'done'; + } + } + + private _emitStepperSelectionEvent(newIndex: number): void { + const stepsArray = this._steps.toArray(); + this.selectionChange.emit({ + selectedIndex: newIndex, + previouslySelectedIndex: this._selectedIndex, + selectedStep: stepsArray[newIndex], + previouslySelectedStep: stepsArray[this._selectedIndex], + }); + this._selectedIndex = newIndex; + this._stateChanged(); + } + + _onKeydown(event: KeyboardEvent) { + switch (event.keyCode) { + case RIGHT_ARROW: + if (this._layoutDirection() === 'rtl') { + this._focusPreviousStep(); + } else { + this._focusNextStep(); + } + break; + case LEFT_ARROW: + if (this._layoutDirection() === 'rtl') { + this._focusNextStep(); + } else { + this._focusPreviousStep(); + } + break; + case SPACE: + case ENTER: + this.selectedIndex = this._focusIndex; + break; + default: + // Return to avoid calling preventDefault on keys that are not explicitly handled. + return; + } + event.preventDefault(); + } + + private _focusNextStep() { + this._focusStep((this._focusIndex + 1) % this._steps.length); + } + + private _focusPreviousStep() { + this._focusStep((this._focusIndex + this._steps.length - 1) % this._steps.length); + } + + private _focusStep(index: number) { + this._focusIndex = index; + this._stepHeader.toArray()[this._focusIndex].nativeElement.focus(); + } + + private _anyControlsInvalid(index: number): boolean { + const steps = this._steps.toArray(); + + steps[this._selectedIndex].interacted = true; + + if (this._linear && index >= 0) { + return steps.slice(0, index).some(step => step.stepControl && step.stepControl.invalid); + } + return false; + } + + private _layoutDirection(): Direction { + return this._dir && this._dir.value === 'rtl' ? 'rtl' : 'ltr'; + } +} diff --git a/src/cdk/stepper/tsconfig-build.json b/src/cdk/stepper/tsconfig-build.json new file mode 100644 index 000000000000..8088edebe7be --- /dev/null +++ b/src/cdk/stepper/tsconfig-build.json @@ -0,0 +1,14 @@ +{ + "extends": "../tsconfig-build", + "files": [ + "public-api.ts", + "../typings.d.ts" + ], + "angularCompilerOptions": { + "annotateForClosureCompiler": true, + "strictMetadataEmit": true, + "flatModuleOutFile": "index.js", + "flatModuleId": "@angular/cdk/stepper", + "skipTemplateCodegen": true + } +} diff --git a/src/cdk/stepper/typings.d.ts b/src/cdk/stepper/typings.d.ts new file mode 100644 index 000000000000..ce4ae9b66cf0 --- /dev/null +++ b/src/cdk/stepper/typings.d.ts @@ -0,0 +1 @@ +declare var module: {id: string}; diff --git a/src/cdk/table/BUILD.bazel b/src/cdk/table/BUILD.bazel new file mode 100644 index 000000000000..e4ca2a79723b --- /dev/null +++ b/src/cdk/table/BUILD.bazel @@ -0,0 +1,12 @@ +package(default_visibility=["//visibility:public"]) +load("@angular//:index.bzl", "ng_module") + +ng_module( + name = "table", + srcs = glob(["**/*.ts"], exclude=["**/*.spec.ts"]), + module_name = "@angular/cdk/table", + deps = [ + "//src/cdk/collections", + ], + tsconfig = ":tsconfig-build.json", +) diff --git a/src/cdk/table/cell.ts b/src/cdk/table/cell.ts index 994156fbf448..5c5edba081a0 100644 --- a/src/cdk/table/cell.ts +++ b/src/cdk/table/cell.ts @@ -1,12 +1,12 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import {ContentChild, Directive, ElementRef, Input, Renderer2, TemplateRef} from '@angular/core'; +import {ContentChild, Directive, ElementRef, Input, TemplateRef} from '@angular/core'; /** * Cell definition for a CDK table. @@ -14,7 +14,7 @@ import {ContentChild, Directive, ElementRef, Input, Renderer2, TemplateRef} from */ @Directive({selector: '[cdkCellDef]'}) export class CdkCellDef { - constructor(public template: TemplateRef) { } + constructor(/** @docs-private */ public template: TemplateRef) { } } /** @@ -23,7 +23,7 @@ export class CdkCellDef { */ @Directive({selector: '[cdkHeaderCellDef]'}) export class CdkHeaderCellDef { - constructor(public template: TemplateRef) { } + constructor(/** @docs-private */ public template: TemplateRef) { } } /** @@ -64,8 +64,8 @@ export class CdkColumnDef { }, }) export class CdkHeaderCell { - constructor(columnDef: CdkColumnDef, elementRef: ElementRef, renderer: Renderer2) { - renderer.addClass(elementRef.nativeElement, `cdk-column-${columnDef.cssClassFriendlyName}`); + constructor(columnDef: CdkColumnDef, elementRef: ElementRef) { + elementRef.nativeElement.classList.add(`cdk-column-${columnDef.cssClassFriendlyName}`); } } @@ -78,7 +78,7 @@ export class CdkHeaderCell { }, }) export class CdkCell { - constructor(columnDef: CdkColumnDef, elementRef: ElementRef, renderer: Renderer2) { - renderer.addClass(elementRef.nativeElement, `cdk-column-${columnDef.cssClassFriendlyName}`); + constructor(columnDef: CdkColumnDef, elementRef: ElementRef) { + elementRef.nativeElement.classList.add(`cdk-column-${columnDef.cssClassFriendlyName}`); } } diff --git a/src/cdk/table/index.ts b/src/cdk/table/index.ts index 2b504ab757d8..96b6b70a6230 100644 --- a/src/cdk/table/index.ts +++ b/src/cdk/table/index.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -8,4 +8,4 @@ -export * from './public_api'; +export * from './public-api'; diff --git a/src/cdk/table/public-api.ts b/src/cdk/table/public-api.ts new file mode 100644 index 000000000000..5ea6957e7ecc --- /dev/null +++ b/src/cdk/table/public-api.ts @@ -0,0 +1,15 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +export * from './table'; +export * from './cell'; +export * from './row'; +export * from './table-module'; + +/** Re-export DataSource for a more intuitive experience for users of just the table. */ +export {DataSource} from '@angular/cdk/collections'; diff --git a/src/cdk/table/row.ts b/src/cdk/table/row.ts index 1c521e64846e..76bb6c377eb5 100644 --- a/src/cdk/table/row.ts +++ b/src/cdk/table/row.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -21,7 +21,7 @@ import { import {CdkCellDef} from './cell'; /** - * The row template that can be used by the md-table. Should not be used outside of the + * The row template that can be used by the mat-table. Should not be used outside of the * material library. */ export const CDK_ROW_TEMPLATE = ``; @@ -37,14 +37,14 @@ export abstract class BaseRowDef { /** Differ used to check if any changes were made to the columns. */ protected _columnsDiffer: IterableDiffer; - constructor(public template: TemplateRef, + constructor(/** @docs-private */ public template: TemplateRef, protected _differs: IterableDiffers) { } ngOnChanges(changes: SimpleChanges): void { // Create a new columns differ if one does not yet exist. Initialize it based on initial value - // of the columns property. - const columns = changes['columns'].currentValue; - if (!this._columnsDiffer && columns) { + // of the columns property or an empty array if none is provided. + const columns = changes['columns'].currentValue || []; + if (!this._columnsDiffer) { this._columnsDiffer = this._differs.find(columns).create(); this._columnsDiffer.diff(columns); } @@ -75,13 +75,22 @@ export class CdkHeaderRowDef extends BaseRowDef { /** * Data row definition for the CDK table. - * Captures the header row's template and other row properties such as the columns to display. + * Captures the header row's template and other row properties such as the columns to display and + * a when predicate that describes when this row should be used. */ @Directive({ selector: '[cdkRowDef]', - inputs: ['columns: cdkRowDefColumns'], + inputs: ['columns: cdkRowDefColumns', 'when: cdkRowDefWhen'], }) -export class CdkRowDef extends BaseRowDef { +export class CdkRowDef extends BaseRowDef { + /** + * Function that should return true if this row template should be used for the provided index + * and row data. If left undefined, this row will be considered the default row template to use + * when no other when functions return true for the data. + * For every row, there must be at least one when function that passes or an undefined to default. + */ + when: (index: number, rowData: T) => boolean; + // TODO(andrewseguin): Add an input for providing a switch function to determine // if this template should be used. constructor(template: TemplateRef, _differs: IterableDiffers) { @@ -132,7 +141,7 @@ export class CdkCellOutlet { * a handle to provide that component's cells and context. After init, the CdkCellOutlet will * construct the cells with the provided context. */ - static mostRecentCellOutlet: CdkCellOutlet; + static mostRecentCellOutlet: CdkCellOutlet | null = null; constructor(public _viewContainer: ViewContainerRef) { CdkCellOutlet.mostRecentCellOutlet = this; @@ -141,6 +150,7 @@ export class CdkCellOutlet { /** Header template container that contains the cell outlet. Adds the right class and role. */ @Component({ + moduleId: module.id, selector: 'cdk-header-row', template: CDK_ROW_TEMPLATE, host: { @@ -149,11 +159,13 @@ export class CdkCellOutlet { }, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, + preserveWhitespaces: false, }) export class CdkHeaderRow { } /** Data row template container that contains the cell outlet. Adds the right class and role. */ @Component({ + moduleId: module.id, selector: 'cdk-row', template: CDK_ROW_TEMPLATE, host: { @@ -162,5 +174,6 @@ export class CdkHeaderRow { } }, changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, + preserveWhitespaces: false, }) export class CdkRow { } diff --git a/src/cdk/table/table-errors.ts b/src/cdk/table/table-errors.ts index 8a667cf926e0..080250c69beb 100644 --- a/src/cdk/table/table-errors.ts +++ b/src/cdk/table/table-errors.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -12,7 +12,7 @@ * @docs-private */ export function getTableUnknownColumnError(id: string) { - return Error(`cdk-table: Could not find column with id "${id}".`); + return Error(`Could not find column with id "${id}".`); } /** @@ -20,5 +20,30 @@ export function getTableUnknownColumnError(id: string) { * @docs-private */ export function getTableDuplicateColumnNameError(name: string) { - return Error(`cdk-table: Duplicate column definition name provided: "${name}".`); + return Error(`Duplicate column definition name provided: "${name}".`); +} + +/** + * Returns an error to be thrown when there are multiple rows that are missing a when function. + * @docs-private + */ +export function getTableMultipleDefaultRowDefsError() { + return Error(`There can only be one default row without a when predicate function.`); +} + +/** + * Returns an error to be thrown when there are no matching row defs for a particular set of data. + * @docs-private + */ +export function getTableMissingMatchingRowDefError() { + return Error(`Could not find a matching row definition for the provided row data.`); +} + +/** + * Returns an error to be thrown when there is no row definitions present in the content. + * @docs-private + */ +export function getTableMissingRowDefsError() { + return Error('Missing definitions for header and row, ' + + 'cannot determine which columns should be rendered.'); } diff --git a/src/cdk/table/public_api.ts b/src/cdk/table/table-module.ts similarity index 77% rename from src/cdk/table/public_api.ts rename to src/cdk/table/table-module.ts index 24a36e8ddd94..7bc9f1da81b2 100644 --- a/src/cdk/table/public_api.ts +++ b/src/cdk/table/table-module.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -12,13 +12,6 @@ import {HeaderRowPlaceholder, RowPlaceholder, CdkTable} from './table'; import {CdkCellOutlet, CdkHeaderRow, CdkHeaderRowDef, CdkRow, CdkRowDef} from './row'; import {CdkColumnDef, CdkHeaderCellDef, CdkHeaderCell, CdkCell, CdkCellDef} from './cell'; -export * from './table'; -export * from './cell'; -export * from './row'; - -/** Re-export DataSource for a more intuitive experience for users of just the table. */ -export {DataSource} from '@angular/cdk/collections'; - const EXPORTED_DECLARATIONS = [ CdkTable, CdkRowDef, diff --git a/src/cdk/table/table.spec.ts b/src/cdk/table/table.spec.ts index a07b92833488..25ea69b40465 100644 --- a/src/cdk/table/table.spec.ts +++ b/src/cdk/table/table.spec.ts @@ -3,11 +3,17 @@ import {Component, ViewChild} from '@angular/core'; import {CdkTable} from './table'; import {CollectionViewer, DataSource} from '@angular/cdk/collections'; import {BehaviorSubject} from 'rxjs/BehaviorSubject'; -import {Observable} from 'rxjs/Observable'; import {combineLatest} from 'rxjs/observable/combineLatest'; import {CdkTableModule} from './index'; -import {map} from 'rxjs/operator/map'; -import {getTableDuplicateColumnNameError, getTableUnknownColumnError} from './table-errors'; +import {Observable} from 'rxjs/Observable'; +import {map} from 'rxjs/operators/map'; +import { + getTableDuplicateColumnNameError, + getTableMissingMatchingRowDefError, + getTableMissingRowDefsError, + getTableMultipleDefaultRowDefsError, + getTableUnknownColumnError +} from './table-errors'; describe('CdkTable', () => { let fixture: ComponentFixture; @@ -30,6 +36,12 @@ describe('CdkTable', () => { DuplicateColumnDefNameCdkTableApp, MissingColumnDefCdkTableApp, CrazyColumnNameCdkTableApp, + UndefinedColumnsCdkTableApp, + WhenRowCdkTableApp, + WhenRowWithoutDefaultCdkTableApp, + WhenRowMultipleDefaultsCdkTableApp, + MissingRowDefsCdkTableApp, + BooleanRowCdkTableApp ], }).compileComponents(); })); @@ -54,7 +66,7 @@ describe('CdkTable', () => { it('with a rendered header with the right number of header cells', () => { const header = getHeaderRow(tableElement); - expect(header).not.toBe(undefined); + expect(header).toBeTruthy(); expect(header.classList).toContain('customHeaderRowClass'); expect(getHeaderCells(tableElement).length).toBe(component.columnsToRender.length); }); @@ -98,6 +110,21 @@ describe('CdkTable', () => { }); }); + it('should render cells even if row data is falsy', () => { + const booleanRowCdkTableAppFixture = TestBed.createComponent(BooleanRowCdkTableApp); + const booleanRowCdkTableElement = + booleanRowCdkTableAppFixture.nativeElement.querySelector('cdk-table'); + booleanRowCdkTableAppFixture.detectChanges(); + + expectTableToMatchContent(booleanRowCdkTableElement, [ + [''], // Header row + ['false'], // Data rows + ['true'], + ['false'], + ['true'], + ]); + }); + it('should be able to apply class-friendly css class names for the column cells', () => { const crazyColumnNameAppFixture = TestBed.createComponent(CrazyColumnNameCdkTableApp); const crazyColumnNameTableElement = @@ -133,6 +160,26 @@ describe('CdkTable', () => { .toThrowError(getTableUnknownColumnError('column_a').message); }); + it('should throw an error if the row definitions are missing', () => { + expect(() => TestBed.createComponent(MissingRowDefsCdkTableApp).detectChanges()) + .toThrowError(getTableMissingRowDefsError().message); + }); + + it('should not throw an error if columns are undefined on initialization', () => { + const undefinedColumnsFixture = TestBed.createComponent(UndefinedColumnsCdkTableApp); + undefinedColumnsFixture.detectChanges(); + + tableElement = undefinedColumnsFixture.nativeElement.querySelector('cdk-table'); + + expect(getHeaderRow(tableElement)).toBeNull('Should be no header without cells'); + + // Rows should be empty since there are no columns to display. + const rows = getRows(tableElement); + expect(rows[0].textContent).toBe(''); + expect(rows[1].textContent).toBe(''); + expect(rows[2].textContent).toBe(''); + }); + it('should be able to dynamically add/remove column definitions', () => { const dynamicColumnDefFixture = TestBed.createComponent(DynamicColumnDefinitionsCdkTableApp); dynamicColumnDefFixture.detectChanges(); @@ -186,6 +233,34 @@ describe('CdkTable', () => { }); }); + describe('using when predicate', () => { + it('should be able to display different row templates based on the row data', () => { + let whenFixture = TestBed.createComponent(WhenRowCdkTableApp); + whenFixture.detectChanges(); + + let data = whenFixture.componentInstance.dataSource.data; + expectTableToMatchContent(whenFixture.nativeElement.querySelector('cdk-table'), [ + ['Column A', 'Column B', 'Column C'], + [data[0].a, data[0].b, data[0].c], + ['index_1_special_row'], + ['c3_special_row'], + [data[3].a, data[3].b, data[3].c], + ]); + }); + + it('should error if there is row data that does not have a matching row template', () => { + let whenFixture = TestBed.createComponent(WhenRowWithoutDefaultCdkTableApp); + expect(() => whenFixture.detectChanges()) + .toThrowError(getTableMissingMatchingRowDefError().message); + }); + + it('should error if there are multiple rows that do not have a when function', () => { + let whenFixture = TestBed.createComponent(WhenRowMultipleDefaultsCdkTableApp); + expect(() => whenFixture.detectChanges()) + .toThrowError(getTableMultipleDefaultRowDefsError().message); + }); + }); + it('should use differ to add/remove/move rows', () => { // Each row receives an attribute 'initialIndex' the element's original place getRows(tableElement).forEach((row: Element, index: number) => { @@ -339,6 +414,24 @@ describe('CdkTable', () => { expect(changedRows[1].getAttribute('initialIndex')).toBe('1'); expect(changedRows[2].getAttribute('initialIndex')).toBe(null); }); + + it('should change row implicit data even when trackBy finds no changes', () => { + createTestComponentWithTrackyByTable('index'); + const firstRow = getRows(tableElement)[0]; + expect(firstRow.textContent!.trim()).toBe('a_1 b_1'); + expect(firstRow.getAttribute('initialIndex')).toBe('0'); + mutateData(); + + // Change each item reference to show that the trackby is checking the index. + // Otherwise this would cause them all to be removed/added. + trackByComponent.dataSource.data = trackByComponent.dataSource.data + .map(item => ({a: item.a, b: item.b, c: item.c})); + + // Expect the rows were given the right implicit data even though the rows were not moved. + trackByFixture.detectChanges(); + expect(firstRow.textContent!.trim()).toBe('a_2 b_2'); + expect(firstRow.getAttribute('initialIndex')).toBe('0'); + }); }); it('should match the right table content with dynamic data', () => { @@ -375,7 +468,7 @@ describe('CdkTable', () => { dynamicDataSourceFixture.detectChanges(); // Expect that the component has no data source and the table element reflects empty data. - expect(component.dataSource).toBe(undefined); + expect(component.dataSource).toBeUndefined(); expectTableToMatchContent(tableElement, [ ['Column A'] ]); @@ -395,7 +488,7 @@ describe('CdkTable', () => { ]); // Remove the data source and check to make sure the table is empty again. - component.dataSource = null; + component.dataSource = undefined; dynamicDataSourceFixture.detectChanges(); // Expect that the old data source has been disconnected. @@ -543,10 +636,10 @@ class FakeDataSource extends DataSource { for (let i = 0; i < 3; i++) { this.addData(); } } - connect(collectionViewer: CollectionViewer): Observable { + connect(collectionViewer: CollectionViewer) { this.isConnected = true; const streams = [this._dataChange, collectionViewer.viewChange]; - return map.call(combineLatest(streams), ([data]) => data); + return combineLatest(streams).pipe(map(([data]) => data)); } disconnect() { @@ -567,6 +660,16 @@ class FakeDataSource extends DataSource { } } +class BooleanDataSource extends DataSource { + _dataChange = new BehaviorSubject([false, true, false, true]); + + connect(): Observable { + return this._dataChange; + } + + disconnect() { } +} + @Component({ template: ` @@ -593,12 +696,161 @@ class FakeDataSource extends DataSource { ` }) class SimpleCdkTableApp { - dataSource: FakeDataSource | null = new FakeDataSource(); + dataSource: FakeDataSource | undefined = new FakeDataSource(); columnsToRender = ['column_a', 'column_b', 'column_c']; @ViewChild(CdkTable) table: CdkTable; } +@Component({ + template: ` + + + + {{data}} + + + + + + ` +}) +class BooleanRowCdkTableApp { + dataSource = new BooleanDataSource(); +} + +@Component({ + template: ` + + + Column A + {{row.a}} + + + + Column B + {{row.b}} + + + + Column C + {{row.c}} + + + + Column C + index_1_special_row + + + + Column C + c3_special_row + + + + + + + + ` +}) +class WhenRowCdkTableApp { + dataSource: FakeDataSource = new FakeDataSource(); + columnsToRender = ['column_a', 'column_b', 'column_c']; + isIndex1 = (index: number, _rowData: TestData) => index == 1; + hasC3 = (_index: number, rowData: TestData) => rowData.c == 'c_3'; + + constructor() { this.dataSource.addData(); } + + @ViewChild(CdkTable) table: CdkTable; +} + +@Component({ + template: ` + + + Column A + {{row.a}} + + + + Column B + {{row.b}} + + + + Column C + {{row.c}} + + + + Column C + index_1_special_row + + + + Column C + c3_special_row + + + + + + + ` +}) +class WhenRowWithoutDefaultCdkTableApp { + dataSource: FakeDataSource = new FakeDataSource(); + columnsToRender = ['column_a', 'column_b', 'column_c']; + isIndex1 = (index: number, _rowData: TestData) => index == 1; + hasC3 = (_index: number, rowData: TestData) => rowData.c == 'c_3'; + + @ViewChild(CdkTable) table: CdkTable; +} + +@Component({ + template: ` + + + Column A + {{row.a}} + + + + Column B + {{row.b}} + + + + Column C + {{row.c}} + + + + Column C + index_1_special_row + + + + Column C + c3_special_row + + + + + + + + ` +}) +class WhenRowMultipleDefaultsCdkTableApp { + dataSource: FakeDataSource = new FakeDataSource(); + columnsToRender = ['column_a', 'column_b', 'column_c']; + hasC3 = (_index: number, rowData: TestData) => rowData.c == 'c_3'; + + @ViewChild(CdkTable) table: CdkTable; +} + @Component({ template: ` @@ -753,6 +1005,38 @@ class MissingColumnDefCdkTableApp { dataSource: FakeDataSource = new FakeDataSource(); } +@Component({ + template: ` + + + Column A + {{row.a}} + + + ` +}) +class MissingRowDefsCdkTableApp { + dataSource: FakeDataSource = new FakeDataSource(); +} + +@Component({ + template: ` + + + Column A + {{row.a}} + + + + + + ` +}) +class UndefinedColumnsCdkTableApp { + undefinedColumns; + dataSource: FakeDataSource = new FakeDataSource(); +} + @Component({ template: ` diff --git a/src/cdk/table/table.ts b/src/cdk/table/table.ts index 4bf1760e917e..76c3cb412db4 100644 --- a/src/cdk/table/table.ts +++ b/src/cdk/table/table.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -23,7 +23,6 @@ import { IterableDiffers, NgIterable, QueryList, - Renderer2, TrackByFunction, ViewChild, ViewContainerRef, @@ -31,12 +30,18 @@ import { } from '@angular/core'; import {CollectionViewer, DataSource} from '@angular/cdk/collections'; import {CdkCellOutlet, CdkCellOutletRowContext, CdkHeaderRowDef, CdkRowDef} from './row'; -import {takeUntil} from 'rxjs/operator/takeUntil'; +import {takeUntil} from 'rxjs/operators/takeUntil'; import {BehaviorSubject} from 'rxjs/BehaviorSubject'; import {Subscription} from 'rxjs/Subscription'; import {Subject} from 'rxjs/Subject'; import {CdkCellDef, CdkColumnDef, CdkHeaderCellDef} from './cell'; -import {getTableDuplicateColumnNameError, getTableUnknownColumnError} from './table-errors'; +import { + getTableDuplicateColumnNameError, + getTableMissingMatchingRowDefError, + getTableMissingRowDefsError, + getTableMultipleDefaultRowDefsError, + getTableUnknownColumnError +} from './table-errors'; /** * Provides a handle for the table to grab the view container's ng-container to insert data rows. @@ -57,24 +62,33 @@ export class HeaderRowPlaceholder { } /** - * The table template that can be used by the md-table. Should not be used outside of the + * The table template that can be used by the mat-table. Should not be used outside of the * material library. */ export const CDK_TABLE_TEMPLATE = ` `; +/** + * Class used to conveniently type the embedded view ref for rows with a context. + * @docs-private + */ +abstract class RowViewRef extends EmbeddedViewRef> { } + /** * A data table that connects with a data source to retrieve data of type `T` and renders * a header row and data rows. Updates the rows when new data is provided by the data source. */ @Component({ + moduleId: module.id, selector: 'cdk-table', + exportAs: 'cdkTable', template: CDK_TABLE_TEMPLATE, host: { 'class': 'cdk-table', }, encapsulation: ViewEncapsulation.None, + preserveWhitespaces: false, changeDetection: ChangeDetectionStrategy.OnPush, }) export class CdkTable implements CollectionViewer { @@ -88,11 +102,14 @@ export class CdkTable implements CollectionViewer { private _renderChangeSubscription: Subscription | null; /** Map of all the user's defined columns (header and data cell template) identified by name. */ - private _columnDefinitionsByName = new Map(); + private _columnDefsByName = new Map(); /** Differ used to find the changes in the data provided by the data source. */ private _dataDiffer: IterableDiffer; + /** Stores the row definition that does not have a when predicate. */ + private _defaultRowDef: CdkRowDef | null; + /** * Tracking function that will be used to check the differences in data changes. Used similarly * to `ngFor` `trackBy` function. Optimize row operations by identifying a row based on its data @@ -100,6 +117,7 @@ export class CdkTable implements CollectionViewer { * Accepts a function that takes two parameters, `index` and `item`. */ @Input() + get trackBy(): TrackByFunction { return this._trackByFn; } set trackBy(fn: TrackByFunction) { if (isDevMode() && fn != null && typeof fn !== 'function' && @@ -108,7 +126,6 @@ export class CdkTable implements CollectionViewer { } this._trackByFn = fn; } - get trackBy(): TrackByFunction { return this._trackByFn; } private _trackByFn: TrackByFunction; /** @@ -141,21 +158,21 @@ export class CdkTable implements CollectionViewer { * The column definitions provided by the user that contain what the header and cells should * render for each column. */ - @ContentChildren(CdkColumnDef) _columnDefinitions: QueryList; + @ContentChildren(CdkColumnDef) _columnDefs: QueryList; - /** Template used as the header container. */ - @ContentChild(CdkHeaderRowDef) _headerDefinition: CdkHeaderRowDef; + /** Template definition used as the header container. */ + @ContentChild(CdkHeaderRowDef) _headerDef: CdkHeaderRowDef; - /** Set of templates that used as the data row containers. */ - @ContentChildren(CdkRowDef) _rowDefinitions: QueryList; + /** Set of template definitions that used as the data row containers. */ + @ContentChildren(CdkRowDef) _rowDefs: QueryList>; constructor(private readonly _differs: IterableDiffers, private readonly _changeDetectorRef: ChangeDetectorRef, elementRef: ElementRef, - renderer: Renderer2, @Attribute('role') role: string) { + if (!role) { - renderer.setAttribute(elementRef.nativeElement, 'role', 'grid'); + elementRef.nativeElement.setAttribute('role', 'grid'); } } @@ -165,13 +182,22 @@ export class CdkTable implements CollectionViewer { } ngAfterContentInit() { - this._cacheColumnDefinitionsByName(); - this._columnDefinitions.changes.subscribe(() => this._cacheColumnDefinitionsByName()); + if (!this._headerDef && !this._rowDefs.length) { + throw getTableMissingRowDefsError(); + } + + this._cacheColumnDefsByName(); + this._columnDefs.changes.subscribe(() => this._cacheColumnDefsByName()); this._renderHeaderRow(); } ngAfterContentChecked() { this._renderUpdatedColumns(); + + const defaultRowDefs = this._rowDefs.filter(def => !def.when); + if (defaultRowDefs.length > 1) { throw getTableMultipleDefaultRowDefsError(); } + this._defaultRowDef = defaultRowDefs[0]; + if (this.dataSource && !this._renderChangeSubscription) { this._observeRenderChanges(); } @@ -188,15 +214,14 @@ export class CdkTable implements CollectionViewer { } } - /** Update the map containing the content's column definitions. */ - private _cacheColumnDefinitionsByName() { - this._columnDefinitionsByName.clear(); - this._columnDefinitions.forEach(columnDef => { - if (this._columnDefinitionsByName.has(columnDef.name)) { + private _cacheColumnDefsByName() { + this._columnDefsByName.clear(); + this._columnDefs.forEach(columnDef => { + if (this._columnDefsByName.has(columnDef.name)) { throw getTableDuplicateColumnNameError(columnDef.name); } - this._columnDefinitionsByName.set(columnDef.name, columnDef); + this._columnDefsByName.set(columnDef.name, columnDef); }); } @@ -206,8 +231,8 @@ export class CdkTable implements CollectionViewer { */ private _renderUpdatedColumns() { // Re-render the rows when the row definition columns change. - this._rowDefinitions.forEach(rowDefinition => { - if (!!rowDefinition.getColumnsDiff()) { + this._rowDefs.forEach(def => { + if (!!def.getColumnsDiff()) { // Reset the data to an empty array so that renderRowChanges will re-render all new rows. this._dataDiffer.diff([]); @@ -217,7 +242,7 @@ export class CdkTable implements CollectionViewer { }); // Re-render the header row if there is a difference in its columns. - if (this._headerDefinition.getColumnsDiff()) { + if (this._headerDef.getColumnsDiff()) { this._headerRowPlaceholder.viewContainer.clear(); this._renderHeaderRow(); } @@ -251,7 +276,7 @@ export class CdkTable implements CollectionViewer { /** Set up a subscription for the data provided by the data source. */ private _observeRenderChanges() { - this._renderChangeSubscription = takeUntil.call(this.dataSource.connect(this), this._onDestroy) + this._renderChangeSubscription = this.dataSource.connect(this).pipe(takeUntil(this._onDestroy)) .subscribe(data => { this._data = data; this._renderRowChanges(); @@ -262,41 +287,69 @@ export class CdkTable implements CollectionViewer { * Create the embedded view for the header template and place it in the header row view container. */ private _renderHeaderRow() { - const cells = this._getHeaderCellTemplatesForRow(this._headerDefinition); + const cells = this._getHeaderCellTemplatesForRow(this._headerDef); if (!cells.length) { return; } // TODO(andrewseguin): add some code to enforce that exactly // one CdkCellOutlet was instantiated as a result // of `createEmbeddedView`. this._headerRowPlaceholder.viewContainer - .createEmbeddedView(this._headerDefinition.template, {cells}); + .createEmbeddedView(this._headerDef.template, {cells}); cells.forEach(cell => { - CdkCellOutlet.mostRecentCellOutlet._viewContainer.createEmbeddedView(cell.template, {}); + if (CdkCellOutlet.mostRecentCellOutlet) { + CdkCellOutlet.mostRecentCellOutlet._viewContainer.createEmbeddedView(cell.template, {}); + } }); this._changeDetectorRef.markForCheck(); } - /** Check for changes made in the data and render each change (row added/removed/moved). */ + /** + * Check for changes made in the data and render each change (row added/removed/moved) and update + * row contexts. + */ private _renderRowChanges() { const changes = this._dataDiffer.diff(this._data); if (!changes) { return; } const viewContainer = this._rowPlaceholder.viewContainer; changes.forEachOperation( - (item: IterableChangeRecord, adjustedPreviousIndex: number, currentIndex: number) => { - if (item.previousIndex == null) { - this._insertRow(this._data[currentIndex], currentIndex); + (record: IterableChangeRecord, adjustedPreviousIndex: number, currentIndex: number) => { + if (record.previousIndex == null) { + this._insertRow(record.item, currentIndex); } else if (currentIndex == null) { viewContainer.remove(adjustedPreviousIndex); } else { - const view = viewContainer.get(adjustedPreviousIndex); + const view = >viewContainer.get(adjustedPreviousIndex); viewContainer.move(view!, currentIndex); } }); - this._updateRowContext(); + // Update the meta context of a row's context data (index, count, first, last, ...) + this._updateRowIndexContext(); + + // Update rows that did not get added/removed/moved but may have had their identity changed, + // e.g. if trackBy matched data on some property but the actual data reference changed. + changes.forEachIdentityChange((record: IterableChangeRecord) => { + const rowView = >viewContainer.get(record.currentIndex!); + rowView.context.$implicit = record.item; + }); + } + + /** + * Finds the matching row definition that should be used for this row data. If there is only + * one row definition, it is returned. Otherwise, find the row definition that has a when + * predicate that returns true with the data. If none return true, return the default row + * definition. + */ + _getRowDef(data: T, i: number): CdkRowDef { + if (this._rowDefs.length == 1) { return this._rowDefs.first; } + + let rowDef = this._rowDefs.find(def => def.when && def.when(i, data)) || this._defaultRowDef; + if (!rowDef) { throw getTableMissingMatchingRowDefError(); } + + return rowDef; } /** @@ -304,10 +357,7 @@ export class CdkTable implements CollectionViewer { * within the data row view container. */ private _insertRow(rowData: T, index: number) { - // TODO(andrewseguin): Add when predicates to the row definitions - // to find the right template to used based on - // the data rather than choosing the first row definition. - const row = this._rowDefinitions.first; + const row = this._getRowDef(rowData, index); // Row context that will be provided to both the created embedded row view and its cells. const context: CdkCellOutletRowContext = {$implicit: rowData}; @@ -316,25 +366,24 @@ export class CdkTable implements CollectionViewer { // CdkCellOutlet was instantiated as a result of `createEmbeddedView`. this._rowPlaceholder.viewContainer.createEmbeddedView(row.template, context, index); - // Insert empty cells if there is no data to improve rendering time. - const cells = rowData ? this._getCellTemplatesForRow(row) : []; - - cells.forEach(cell => { - CdkCellOutlet.mostRecentCellOutlet._viewContainer.createEmbeddedView(cell.template, context); + this._getCellTemplatesForRow(row).forEach(cell => { + if (CdkCellOutlet.mostRecentCellOutlet) { + CdkCellOutlet.mostRecentCellOutlet._viewContainer + .createEmbeddedView(cell.template, context); + } }); this._changeDetectorRef.markForCheck(); } /** - * Updates the context for each row to reflect any data changes that may have caused - * rows to be added, removed, or moved. The view container contains the same context - * that was provided to each of its cells. + * Updates the index-related context for each row to reflect any changes in the index of the rows, + * e.g. first/last/even/odd. */ - private _updateRowContext() { + private _updateRowIndexContext() { const viewContainer = this._rowPlaceholder.viewContainer; for (let index = 0, count = viewContainer.length; index < count; index++) { - const viewRef = viewContainer.get(index) as EmbeddedViewRef>; + const viewRef = viewContainer.get(index) as RowViewRef; viewRef.context.index = index; viewRef.context.count = count; viewRef.context.first = index === 0; @@ -351,7 +400,7 @@ export class CdkTable implements CollectionViewer { private _getHeaderCellTemplatesForRow(headerDef: CdkHeaderRowDef): CdkHeaderCellDef[] { if (!headerDef.columns) { return []; } return headerDef.columns.map(columnId => { - const column = this._columnDefinitionsByName.get(columnId); + const column = this._columnDefsByName.get(columnId); if (!column) { throw getTableUnknownColumnError(columnId); @@ -365,10 +414,10 @@ export class CdkTable implements CollectionViewer { * Returns the cell template definitions to insert in the provided row * as defined by its list of columns to display. */ - private _getCellTemplatesForRow(rowDef: CdkRowDef): CdkCellDef[] { + private _getCellTemplatesForRow(rowDef: CdkRowDef): CdkCellDef[] { if (!rowDef.columns) { return []; } return rowDef.columns.map(columnId => { - const column = this._columnDefinitionsByName.get(columnId); + const column = this._columnDefsByName.get(columnId); if (!column) { throw getTableUnknownColumnError(columnId); @@ -378,4 +427,3 @@ export class CdkTable implements CollectionViewer { }); } } - diff --git a/src/cdk/table/tsconfig-build.json b/src/cdk/table/tsconfig-build.json index dfad616c974d..fa5055dc7a07 100644 --- a/src/cdk/table/tsconfig-build.json +++ b/src/cdk/table/tsconfig-build.json @@ -1,7 +1,8 @@ { "extends": "../tsconfig-build", "files": [ - "public_api.ts" + "public-api.ts", + "../typings.d.ts" ], "angularCompilerOptions": { "annotateForClosureCompiler": true, diff --git a/src/cdk/table/typings.d.ts b/src/cdk/table/typings.d.ts new file mode 100644 index 000000000000..ce4ae9b66cf0 --- /dev/null +++ b/src/cdk/table/typings.d.ts @@ -0,0 +1 @@ +declare var module: {id: string}; diff --git a/src/cdk/testing/dispatch-events.ts b/src/cdk/testing/dispatch-events.ts index 6e7739c22454..d1970f344218 100644 --- a/src/cdk/testing/dispatch-events.ts +++ b/src/cdk/testing/dispatch-events.ts @@ -1,12 +1,17 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import {createFakeEvent, createKeyboardEvent, createMouseEvent} from './event-objects'; +import { + createFakeEvent, + createKeyboardEvent, + createMouseEvent, + createTouchEvent +} from './event-objects'; /** Utility to dispatch any event on a Node. */ export function dispatchEvent(node: Node | Window, event: Event): Event { @@ -15,16 +20,23 @@ export function dispatchEvent(node: Node | Window, event: Event): Event { } /** Shorthand to dispatch a fake event on a specified node. */ -export function dispatchFakeEvent(node: Node | Window, type: string): Event { - return dispatchEvent(node, createFakeEvent(type)); +export function dispatchFakeEvent(node: Node | Window, type: string, canBubble?: boolean): Event { + return dispatchEvent(node, createFakeEvent(type, canBubble)); } /** Shorthand to dispatch a keyboard event with a specified key code. */ -export function dispatchKeyboardEvent(node: Node, type: string, keyCode: number): KeyboardEvent { - return dispatchEvent(node, createKeyboardEvent(type, keyCode)) as KeyboardEvent; +export function dispatchKeyboardEvent(node: Node, type: string, keyCode: number, target?: Element): + KeyboardEvent { + return dispatchEvent(node, createKeyboardEvent(type, keyCode, target)) as KeyboardEvent; } /** Shorthand to dispatch a mouse event on the specified coordinates. */ -export function dispatchMouseEvent(node: Node, type: string, x = 0, y = 0): MouseEvent { - return dispatchEvent(node, createMouseEvent(type, x, y)) as MouseEvent; +export function dispatchMouseEvent(node: Node, type: string, x = 0, y = 0, + event = createMouseEvent(type, x, y)): MouseEvent { + return dispatchEvent(node, event) as MouseEvent; +} + +/** Shorthand to dispatch a touch event on the specified coordinates. */ +export function dispatchTouchEvent(node: Node, type: string, x = 0, y = 0) { + return dispatchEvent(node, createTouchEvent(type, x, y)); } diff --git a/src/cdk/testing/event-objects.ts b/src/cdk/testing/event-objects.ts index 1d88d7996df2..9ead01e767cf 100644 --- a/src/cdk/testing/event-objects.ts +++ b/src/cdk/testing/event-objects.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -8,7 +8,7 @@ /** Creates a browser MouseEvent with the specified options. */ export function createMouseEvent(type: string, x = 0, y = 0) { - let event = document.createEvent('MouseEvent'); + const event = document.createEvent('MouseEvent'); event.initMouseEvent(type, false, /* canBubble */ @@ -29,6 +29,24 @@ export function createMouseEvent(type: string, x = 0, y = 0) { return event; } +/** Creates a browser TouchEvent with the specified pointer coordinates. */ +export function createTouchEvent(type: string, pageX = 0, pageY = 0) { + // In favor of creating events that work for most of the browsers, the event is created + // as a basic UI Event. The necessary details for the event will be set manually. + const event = document.createEvent('UIEvent'); + const touchDetails = {pageX, pageY}; + + event.initUIEvent(type, true, true, window, 0); + + // Most of the browsers don't have a "initTouchEvent" method that can be used to define + // the touch details. + Object.defineProperties(event, { + touches: {value: [touchDetails]} + }); + + return event; +} + /** Dispatches a keydown event from an element. */ export function createKeyboardEvent(type: string, keyCode: number, target?: Element, key?: string) { let event = document.createEvent('KeyboardEvent') as any; @@ -56,8 +74,8 @@ export function createKeyboardEvent(type: string, keyCode: number, target?: Elem } /** Creates a fake event object with any desired event type. */ -export function createFakeEvent(type: string) { - let event = document.createEvent('Event'); - event.initEvent(type, true, true); +export function createFakeEvent(type: string, canBubble = true, cancelable = true) { + const event = document.createEvent('Event'); + event.initEvent(type, canBubble, cancelable); return event; } diff --git a/src/cdk/testing/fake-viewport-ruler.ts b/src/cdk/testing/fake-viewport-ruler.ts index b2f894ab3f07..267c45fa0623 100644 --- a/src/cdk/testing/fake-viewport-ruler.ts +++ b/src/cdk/testing/fake-viewport-ruler.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -14,6 +14,10 @@ export class FakeViewportRuler { }; } + getViewportSize() { + return {width: 1014, height: 686}; + } + getViewportScrollPosition() { return {top: 0, left: 0}; } diff --git a/src/cdk/testing/index.ts b/src/cdk/testing/index.ts index 2b504ab757d8..96b6b70a6230 100644 --- a/src/cdk/testing/index.ts +++ b/src/cdk/testing/index.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license @@ -8,4 +8,4 @@ -export * from './public_api'; +export * from './public-api'; diff --git a/src/cdk/testing/public_api.ts b/src/cdk/testing/public-api.ts similarity index 88% rename from src/cdk/testing/public_api.ts rename to src/cdk/testing/public-api.ts index 91d9bedf97c8..c3c76f48c5a6 100644 --- a/src/cdk/testing/public_api.ts +++ b/src/cdk/testing/public-api.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license diff --git a/src/cdk/testing/type-in-element.ts b/src/cdk/testing/type-in-element.ts index b33f6e86af20..4a16bf588965 100644 --- a/src/cdk/testing/type-in-element.ts +++ b/src/cdk/testing/type-in-element.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license diff --git a/src/cdk/testing/wrapped-error-message.ts b/src/cdk/testing/wrapped-error-message.ts index 3cfd0dbe6298..a5fd8595992a 100644 --- a/src/cdk/testing/wrapped-error-message.ts +++ b/src/cdk/testing/wrapped-error-message.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license diff --git a/src/cdk/tsconfig-build.json b/src/cdk/tsconfig-build.json index c74fa84aaa3e..4a62901bfd86 100644 --- a/src/cdk/tsconfig-build.json +++ b/src/cdk/tsconfig-build.json @@ -1,5 +1,4 @@ -// TypeScript config file that is used to compile the cdk package. Target environment needs to be -// ES2015 since the build process will create FESM bundles using rollup. +// TypeScript config file that is used to compile the cdk's ES2015 package. { "compilerOptions": { "baseUrl": ".", @@ -21,11 +20,12 @@ "skipLibCheck": true, "types": [], "paths": { - "@angular/cdk/*": ["../../dist/packages/cdk/*/public_api"] + "@angular/cdk/*": ["../../dist/packages/cdk/*"] } }, "files": [ - "public_api.ts" + "public-api.ts", + "typings.d.ts" ], "angularCompilerOptions": { "annotateForClosureCompiler": true, diff --git a/src/cdk/tsconfig-tests.json b/src/cdk/tsconfig-tests.json index b834ba802df0..86b715415315 100644 --- a/src/cdk/tsconfig-tests.json +++ b/src/cdk/tsconfig-tests.json @@ -14,8 +14,14 @@ } }, "files": [ - "./testing/index.ts" + "./testing/index.ts", + "typings.d.ts" ], + "angularCompilerOptions": { + "strictMetadataEmit": true, + "skipTemplateCodegen": true, + "emitDecoratorMetadata": true + }, "include": [ // Include the index.ts for each secondary entry-point "./*/index.ts", diff --git a/src/cdk/tsconfig.json b/src/cdk/tsconfig.json new file mode 100644 index 000000000000..db5560801237 --- /dev/null +++ b/src/cdk/tsconfig.json @@ -0,0 +1,12 @@ +// Configuration for IDEs only. +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "..", + "baseUrl": ".", + "paths": { + "@angular/cdk/*": ["./*"] + } + }, + "include": ["./**/*.ts"] +} diff --git a/src/cdk/typings.d.ts b/src/cdk/typings.d.ts new file mode 100644 index 000000000000..ce4ae9b66cf0 --- /dev/null +++ b/src/cdk/typings.d.ts @@ -0,0 +1 @@ +declare var module: {id: string}; diff --git a/src/cdk/version.ts b/src/cdk/version.ts index 4d5a1335755f..fe791edbde7a 100644 --- a/src/cdk/version.ts +++ b/src/cdk/version.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright Google Inc. All Rights Reserved. + * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license diff --git a/src/demo-app/a11y/a11y-module.ts b/src/demo-app/a11y/a11y-module.ts index fec683d71a01..9911341393ea 100644 --- a/src/demo-app/a11y/a11y-module.ts +++ b/src/demo-app/a11y/a11y-module.ts @@ -1,3 +1,11 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + import {NgModule} from '@angular/core'; import {CommonModule} from '@angular/common'; import {FormsModule, ReactiveFormsModule} from '@angular/forms'; @@ -19,20 +27,33 @@ import { DialogNeptuneIFrameDialog, DialogWelcomeExampleDialog } from './dialog/dialog-a11y'; - +import {ExpansionPanelAccessibilityDemo} from './expansion/expansion-a11y'; import {GridListAccessibilityDemo} from './grid-list/grid-list-a11y'; +import {ListAccessibilityDemo} from './list/list-a11y'; import {RadioAccessibilityDemo} from './radio/radio-a11y'; import {ToolbarAccessibilityDemo} from './toolbar/toolbar-a11y'; import {DatepickerAccessibilityDemo} from './datepicker/datepicker-a11y'; import {IconAccessibilityDemo} from './icon/icon-a11y'; import {InputAccessibilityDemo} from './input/input-a11y'; import {MenuAccessibilityDemo} from './menu/menu-a11y'; +import {ProgressBarAccessibilityDemo} from './progress-bar/progress-bar-a11y'; import {ProgressSpinnerAccessibilityDemo} from './progress-spinner/progress-spinner-a11y'; import {SliderAccessibilityDemo} from './slider/slider-a11y'; import {SlideToggleAccessibilityDemo} from './slide-toggle/slide-toggle-a11y'; import {SnackBarAccessibilityDemo} from './snack-bar/snack-bar-a11y'; import {SelectAccessibilityDemo} from './select/select-a11y'; - +import {TableAccessibilityDemo} from './table/table-a11y'; +import { + TabsAccessibilityDemo, + SunnyTabContent, + RainyTabContent, + FoggyTabContent, +} from './tabs/tabs-a11y'; +import {TooltipAccessibilityDemo} from './tooltip/tooltip-a11y'; +import {SidenavAccessibilityDemo} from './sidenav/sidenav-a11y'; +import {SidenavBasicAccessibilityDemo} from './sidenav/basic-sidenav-a11y'; +import {SidenavDualAccessibilityDemo} from './sidenav/dual-sidenav-a11y'; +import {SidenavMobileAccessibilityDemo} from './sidenav/mobile-sidenav-a11y'; @NgModule({ imports: [ @@ -68,17 +89,30 @@ export class AccessibilityRoutingModule {} DialogNeptuneExampleDialog, DialogNeptuneIFrameDialog, DialogWelcomeExampleDialog, + ExpansionPanelAccessibilityDemo, + FoggyTabContent, GridListAccessibilityDemo, IconAccessibilityDemo, InputAccessibilityDemo, + ListAccessibilityDemo, MenuAccessibilityDemo, + ProgressBarAccessibilityDemo, ProgressSpinnerAccessibilityDemo, RadioAccessibilityDemo, - ToolbarAccessibilityDemo, + RainyTabContent, + SelectAccessibilityDemo, + SidenavAccessibilityDemo, + SidenavBasicAccessibilityDemo, + SidenavDualAccessibilityDemo, + SidenavMobileAccessibilityDemo, SliderAccessibilityDemo, SlideToggleAccessibilityDemo, SnackBarAccessibilityDemo, - SelectAccessibilityDemo, + TableAccessibilityDemo, + SunnyTabContent, + TabsAccessibilityDemo, + ToolbarAccessibilityDemo, + TooltipAccessibilityDemo, ], entryComponents: [ DialogAccessibilityDemo, @@ -87,6 +121,9 @@ export class AccessibilityRoutingModule {} DialogNeptuneExampleDialog, DialogNeptuneIFrameDialog, DialogWelcomeExampleDialog, + FoggyTabContent, + RainyTabContent, + SunnyTabContent, ] }) export class AccessibilityDemoModule {} diff --git a/src/demo-app/a11y/a11y.html b/src/demo-app/a11y/a11y.html index 065ddcc8381a..511dc5263762 100644 --- a/src/demo-app/a11y/a11y.html +++ b/src/demo-app/a11y/a11y.html @@ -1,9 +1,17 @@ -

Accessibility Demo

+ +

+ Accessibility Demo +

- + +
- +
+

{{currentComponent}} Demo

+ +
diff --git a/src/demo-app/a11y/a11y.ts b/src/demo-app/a11y/a11y.ts index 22c8916bebd3..9db5dda65aa7 100644 --- a/src/demo-app/a11y/a11y.ts +++ b/src/demo-app/a11y/a11y.ts @@ -1,4 +1,14 @@ -import {Component, ViewEncapsulation} from '@angular/core'; +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Component, ElementRef, OnDestroy, ViewChild, ViewEncapsulation} from '@angular/core'; +import {NavigationEnd, Router} from '@angular/router'; +import {Subscription} from 'rxjs/Subscription'; @Component({ moduleId: module.id, @@ -13,28 +23,71 @@ export class AccessibilityHome {} templateUrl: 'a11y.html', styleUrls: ['a11y.css'], encapsulation: ViewEncapsulation.None, + preserveWhitespaces: false, }) -export class AccessibilityDemo { +export class AccessibilityDemo implements OnDestroy { + currentComponent: string = ''; + + fullscreen = false; + + private _routerSubscription = Subscription.EMPTY; + + @ViewChild('maincontent') mainContent: ElementRef; + @ViewChild('header') sectionHeader: ElementRef; + navItems = [ {name: 'Home', route: '.'}, {name: 'Autocomplete', route: 'autocomplete'}, - {name: 'Button', route: 'button'}, {name: 'Button toggle', route: 'button-toggle'}, + {name: 'Button', route: 'button'}, {name: 'Card', route: 'card'}, {name: 'Checkbox', route: 'checkbox'}, {name: 'Chips', route: 'chips'}, {name: 'Datepicker', route: 'datepicker'}, {name: 'Dialog', route: 'dialog'}, + {name: 'Expansion panel', route: 'expansion'}, {name: 'Grid list', route: 'grid-list'}, {name: 'Icon', route: 'icon'}, {name: 'Input', route: 'input'}, + {name: 'List', route: 'list'}, {name: 'Menu', route: 'menu'}, + {name: 'Progress bar', route: 'progress-bar'}, {name: 'Progress spinner', route: 'progress-spinner'}, {name: 'Radio buttons', route: 'radio'}, - {name: 'Slider', route: 'slider'}, + {name: 'Select', route: 'select'}, + {name: 'Sidenav', route: 'sidenav'}, {name: 'Slide toggle', route: 'slide-toggle'}, + {name: 'Slider', route: 'slider'}, {name: 'Snack bar', route: 'snack-bar'}, - {name: 'Select', route: 'select'}, + {name: 'Table', route: 'table'}, + {name: 'Tabs', route: 'tabs'}, {name: 'Toolbar', route: 'toolbar'}, + {name: 'Tooltip', route: 'tooltip'}, ]; + + constructor(router: Router) { + this._routerSubscription = router.events.subscribe((e) => { + if (e instanceof NavigationEnd) { + let fragments = e.url.split('/'); + let nav = this.navItems.find(navItem => { + return fragments[fragments.length - 1] === navItem.route; + }); + this.currentComponent = nav ? nav.name : ''; + + let routerState = router.routerState.root; + while (routerState.children.length) { + routerState = routerState.children[0]; + } + this.fullscreen = !!routerState.snapshot.data.fullscreen; + } + }); + } + + skipNavigation() { + (this.currentComponent ? this.sectionHeader : this.mainContent).nativeElement.focus(); + } + + ngOnDestroy() { + this._routerSubscription.unsubscribe(); + } } diff --git a/src/demo-app/a11y/autocomplete/autocomplete-a11y.html b/src/demo-app/a11y/autocomplete/autocomplete-a11y.html index f2a6ee6c926c..e84762443315 100644 --- a/src/demo-app/a11y/autocomplete/autocomplete-a11y.html +++ b/src/demo-app/a11y/autocomplete/autocomplete-a11y.html @@ -1,15 +1,15 @@

Filtering and selection

Select your favorite state

- - + - - + + {{ state.name }} - - - + + +

Selected value: {{ value }}

diff --git a/src/demo-app/a11y/autocomplete/autocomplete-a11y.ts b/src/demo-app/a11y/autocomplete/autocomplete-a11y.ts index a11b7a7cdc81..c4dc3ba355a3 100644 --- a/src/demo-app/a11y/autocomplete/autocomplete-a11y.ts +++ b/src/demo-app/a11y/autocomplete/autocomplete-a11y.ts @@ -1,3 +1,11 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + import {Component} from '@angular/core'; diff --git a/src/demo-app/a11y/button-toggle/button-toggle-a11y.html b/src/demo-app/a11y/button-toggle/button-toggle-a11y.html index 8f6677b7284f..4286a815e610 100644 --- a/src/demo-app/a11y/button-toggle/button-toggle-a11y.html +++ b/src/demo-app/a11y/button-toggle/button-toggle-a11y.html @@ -1,62 +1,62 @@

Single button toggle

- Yes + Yes

Button toggles with icons

- - - format_align_left - - - format_align_center - - - format_align_right - - - format_align_justify - - + + + format_align_left + + + format_align_center + + + format_align_right + + + format_align_justify + +

Multi-selection button toggle group

- - Flour - Eggs - Sugar - Milk - + + Flour + Eggs + Sugar + Milk +

Exclusive selection button toggle group

- - + + {{pie}} - - + +

Your favorite type of pie is: {{favoritePie}}

Disabled button toggle group

- - Flour - Eggs - Sugar - Milk - + + Flour + Eggs + Sugar + Milk +

Vertical button toggle group

- - Flour - Eggs - Sugar - Milk - + + Flour + Eggs + Sugar + Milk +
\ No newline at end of file diff --git a/src/demo-app/a11y/button-toggle/button-toggle-a11y.ts b/src/demo-app/a11y/button-toggle/button-toggle-a11y.ts index 9818274aff5f..7699f352340e 100644 --- a/src/demo-app/a11y/button-toggle/button-toggle-a11y.ts +++ b/src/demo-app/a11y/button-toggle/button-toggle-a11y.ts @@ -1,3 +1,11 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + import {Component} from '@angular/core'; diff --git a/src/demo-app/a11y/button/button-a11y.html b/src/demo-app/a11y/button/button-a11y.html index 875f983c2175..fdfd08af2a0a 100644 --- a/src/demo-app/a11y/button/button-a11y.html +++ b/src/demo-app/a11y/button/button-a11y.html @@ -3,50 +3,50 @@

Button elements

Click on the buttons to increase the button counter.

Current number of clicks: {{counter}}

- - - + + - -

Anchor elements

- Google search - YouTube - Google search + YouTube + - search + search - - search + search - - search + search

Buttons in different colors

- - - + + +

Disabled button

The following "cancel" button is disabled

- +
diff --git a/src/demo-app/a11y/button/button-a11y.ts b/src/demo-app/a11y/button/button-a11y.ts index 616e8252d548..6255fbeecf5e 100644 --- a/src/demo-app/a11y/button/button-a11y.ts +++ b/src/demo-app/a11y/button/button-a11y.ts @@ -1,5 +1,13 @@ -import {Component} from '@angular/core'; +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import {Component} from '@angular/core'; +import {MatSnackBar} from '@angular/material'; @Component({ moduleId: module.id, @@ -10,7 +18,16 @@ import {Component} from '@angular/core'; export class ButtonAccessibilityDemo { counter: number = 0; + constructor(public snackBar: MatSnackBar) {} + + openSnackBar(message: string) { + this.snackBar.open(message, '', { + duration: 2000, + }); + } + increase() { this.counter++; + this.openSnackBar(`Click counter is set to ${this.counter}`); } } diff --git a/src/demo-app/a11y/card/card-a11y.html b/src/demo-app/a11y/card/card-a11y.html index 6a1961f088ce..c896216f530c 100644 --- a/src/demo-app/a11y/card/card-a11y.html +++ b/src/demo-app/a11y/card/card-a11y.html @@ -1,8 +1,8 @@

Dogs group

Showing a card with a group of content

- - + +

Herding Group

Hound Group

Non-Sporting Group

@@ -12,25 +12,25 @@

Dogs group

Working Group

Foundation Stock Service

Miscellaneous Class

-
-
+ +

Husky

Showing a card with title only

- + Siberian Husky - +

Malamute

Showing a Card with title and subtitle.

- - Alaskan Malamute - Dog breed - + + Alaskan Malamute + Dog breed +
@@ -39,55 +39,55 @@

German Shepherd

Showing a card with title, subtitle, and a footer.

- - Dog breed - German Shepherd - + + Dog breed + German Shepherd + The German Shepherd is a breed of medium to large-sized working dog that originated in Germany. The breed's officially recognized name is German Shepherd Dog in the English language. The breed is also known as the Alsatian in Britain and Ireland. - - + + People also search for Rottweiler, Siberian Husky, Labrador Retriever, Doberman Pinscher - - + +

Dachshund

Showing a card with title, subtitle, and avatar as header and a card image.

- - - - Dachshund - Dog breed - - + + + Dachshund + Dog breed + + - + The dachshund is a short-legged, long-bodied, hound-type dog breed. - - + +

Shiba Inu

Showing a card with header, content, image, and two action buttons: "share" and "like".

- - - - Shiba Inu - Dog Breed - - - + + + + Shiba Inu + Dog Breed + + + The Shiba Inu is the smallest of the six original and distinct spitz breeds of dog from Japan. A small, agile dog that copes very well with mountainous terrain, the Shiba Inu was originally bred for hunting. - - - - - - + + + + + +
diff --git a/src/demo-app/a11y/card/card-a11y.ts b/src/demo-app/a11y/card/card-a11y.ts index 613d72d3af4f..101422b96467 100644 --- a/src/demo-app/a11y/card/card-a11y.ts +++ b/src/demo-app/a11y/card/card-a11y.ts @@ -1,5 +1,13 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + import {Component} from '@angular/core'; -import {MdSnackBar} from '@angular/material'; +import {MatSnackBar} from '@angular/material'; @Component({ moduleId: module.id, @@ -10,7 +18,7 @@ import {MdSnackBar} from '@angular/material'; export class CardAccessibilityDemo { showProgress: boolean = false; - constructor(private snackBar: MdSnackBar) {} + constructor(private snackBar: MatSnackBar) {} openSnackbar(message: string) { this.snackBar.open(message, '', {duration: 2000}); diff --git a/src/demo-app/a11y/checkbox/checkbox-a11y.html b/src/demo-app/a11y/checkbox/checkbox-a11y.html index 56eca75a04f6..ecf6a3e8aa2e 100644 --- a/src/demo-app/a11y/checkbox/checkbox-a11y.html +++ b/src/demo-app/a11y/checkbox/checkbox-a11y.html @@ -1,34 +1,34 @@

Checkbox without label

- +

Standalone checkbox

- Yes, I agree to the terms and conditions + Yes, I agree to the terms and conditions
- No, I do not agree to the terms and conditions + No, I do not agree to the terms and conditions
- A partially done task + A partially done task

Nested checklist

- {{task.name}} - +
- + {{subtask.name}} - +
@@ -36,7 +36,7 @@

Nested checklist

Colored checkboxes

- Primary - Accent - Warn + Primary + Accent + Warn
diff --git a/src/demo-app/a11y/checkbox/checkbox-a11y.ts b/src/demo-app/a11y/checkbox/checkbox-a11y.ts index b401f065a98e..717dd406a823 100644 --- a/src/demo-app/a11y/checkbox/checkbox-a11y.ts +++ b/src/demo-app/a11y/checkbox/checkbox-a11y.ts @@ -1,3 +1,11 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + import {Component} from '@angular/core'; export interface Task { diff --git a/src/demo-app/a11y/chips/chips-a11y.html b/src/demo-app/a11y/chips/chips-a11y.html index 466003f84aec..481039952be2 100644 --- a/src/demo-app/a11y/chips/chips-a11y.html +++ b/src/demo-app/a11y/chips/chips-a11y.html @@ -1,57 +1,57 @@

Basic chips

- - Carnation - Irises - Buttercup - + + Carnation + Irises + Buttercup +

Unstyled chips

- - Husky - Golden Retriever - Border Collie - + + Husky + Golden Retriever + Border Collie +

Removable chips in a form field

- - - + + + {{person.name}} - cancel - - - cancel + + + - + [matChipInputFor]="chipList" + [matChipInputAddOnBlur]="addOnBlur" + (matChipInputTokenEnd)="add($event)" /> +

Colored chips

This example is good for contrast-radio checking.

- - Primary - Accent - Warn - + + Primary + Accent + Warn +

Stacked chips

- - Lemon - Lime - Grapefruit - + + Lemon + Lime + Grapefruit +
diff --git a/src/demo-app/a11y/chips/chips-a11y.ts b/src/demo-app/a11y/chips/chips-a11y.ts index ca356a88de27..e1b4eebea6dd 100644 --- a/src/demo-app/a11y/chips/chips-a11y.ts +++ b/src/demo-app/a11y/chips/chips-a11y.ts @@ -1,5 +1,13 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + import {Component} from '@angular/core'; -import {MdChipInputEvent, MdSnackBar} from '@angular/material'; +import {MatChipInputEvent, MatSnackBar} from '@angular/material'; export interface Person { @@ -36,13 +44,13 @@ export class ChipsAccessibilityDemo { { name: 'Warn', color: 'warn' } ]; - constructor(public snackBar: MdSnackBar) {} + constructor(public snackBar: MatSnackBar) {} displayMessage(message: string): void { this.message = message; } - add(event: MdChipInputEvent): void { + add(event: MatChipInputEvent): void { let input = event.input; let value = event.value; diff --git a/src/demo-app/a11y/datepicker/datepicker-a11y.html b/src/demo-app/a11y/datepicker/datepicker-a11y.html index aef7740b4bb8..ad89bde33077 100644 --- a/src/demo-app/a11y/datepicker/datepicker-a11y.html +++ b/src/demo-app/a11y/datepicker/datepicker-a11y.html @@ -1,112 +1,112 @@

Choose a date (e.g. choose your date of birth)

- - + - - - + + + Please choose a date. - - + + Please choose an earlier date. - - + +

Choose a date with touch UI (e.g. choose a payment date on mobile)

When would you like to schedule your payment?

- - + - - - + + + Please choose a date. - - + + Please choose a later date. - - + +

Choose date with startAt, min and max (e.g. schedule a departing and returning flight)

- - + - - - + + + Please choose a date. - - + + Please choose a later date. - - + + Please choose an earlier date. - - - - + + + - - - + + + Please choose a later date. - - + + Please choose a date after your departure. - - + + Please choose an earlier date. - - + +

Choose date with date filter (e.g. schedule a doctor appointment)

- - + - - - + + + Please choose a date. - - + + No appointments available on this date. - - + +
diff --git a/src/demo-app/a11y/datepicker/datepicker-a11y.ts b/src/demo-app/a11y/datepicker/datepicker-a11y.ts index 718aecf5455f..330c7ba3037d 100644 --- a/src/demo-app/a11y/datepicker/datepicker-a11y.ts +++ b/src/demo-app/a11y/datepicker/datepicker-a11y.ts @@ -1,3 +1,11 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + import {Component} from '@angular/core'; @Component({ diff --git a/src/demo-app/a11y/dialog/dialog-a11y.html b/src/demo-app/a11y/dialog/dialog-a11y.html index e1593db566ad..150980cc9def 100644 --- a/src/demo-app/a11y/dialog/dialog-a11y.html +++ b/src/demo-app/a11y/dialog/dialog-a11y.html @@ -1,13 +1,13 @@

Welcome message

Activate the button to see a welcome dialog with a simple message and a close button.

- +

Choose a fruit

Active the button to choose apple or peach in a dialog.

- +

You chose: {{fruitSelectedOption}}

@@ -19,7 +19,7 @@

Neptune

Active the button to see a dialog showing information of Neptune. A Wikipedia page can be opened either in a new tab or in a stacked dialog.

- +
@@ -27,5 +27,5 @@

Address form

Active the button to fill out shipping address information in a dialog.

- +
diff --git a/src/demo-app/a11y/dialog/dialog-a11y.ts b/src/demo-app/a11y/dialog/dialog-a11y.ts index 73fb3fe565fc..36065397e3fa 100644 --- a/src/demo-app/a11y/dialog/dialog-a11y.ts +++ b/src/demo-app/a11y/dialog/dialog-a11y.ts @@ -1,5 +1,13 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + import {Component} from '@angular/core'; -import {MdDialog} from '@angular/material'; +import {MatDialog} from '@angular/material'; @Component({ @@ -11,7 +19,7 @@ import {MdDialog} from '@angular/material'; export class DialogAccessibilityDemo { fruitSelectedOption: string = ''; - constructor(public dialog: MdDialog) {} + constructor(public dialog: MatDialog) {} openFruitDialog() { let dialogRef = this.dialog.open(DialogFruitExampleDialog); @@ -53,7 +61,7 @@ export class DialogWelcomeExampleDialog {} templateUrl: './dialog-neptune-a11y.html' }) export class DialogNeptuneExampleDialog { - constructor(public dialog: MdDialog) { } + constructor(public dialog: MatDialog) { } showInStackedDialog() { this.dialog.open(DialogNeptuneIFrameDialog); diff --git a/src/demo-app/a11y/dialog/dialog-address-form-a11y.html b/src/demo-app/a11y/dialog/dialog-address-form-a11y.html index fd3a29c9fa53..8a95e7a89be5 100644 --- a/src/demo-app/a11y/dialog/dialog-address-form-a11y.html +++ b/src/demo-app/a11y/dialog/dialog-address-form-a11y.html @@ -1,45 +1,45 @@ -

Company

+

Company

- +
- - - + + + - - + +
- - - - + + + +

- - - - - - + + + + + +

- - - + + +
- - - - - - {{postalCode.value.length}} / 5 - + + + + + + {{postalCode.value.length}} / 5 +
-
+ - - - - + + + + diff --git a/src/demo-app/a11y/dialog/dialog-fruit-a11y.html b/src/demo-app/a11y/dialog/dialog-fruit-a11y.html index f3086a812c28..cb303d41d829 100644 --- a/src/demo-app/a11y/dialog/dialog-fruit-a11y.html +++ b/src/demo-app/a11y/dialog/dialog-fruit-a11y.html @@ -1,6 +1,6 @@ -

Fruit

-
Which would you like to choose?
-
- - +

Fruit

+
Which would you like to choose?
+
+ +
diff --git a/src/demo-app/a11y/dialog/dialog-neptune-a11y.html b/src/demo-app/a11y/dialog/dialog-neptune-a11y.html index e247e83c7714..fd9e67a2e941 100644 --- a/src/demo-app/a11y/dialog/dialog-neptune-a11y.html +++ b/src/demo-app/a11y/dialog/dialog-neptune-a11y.html @@ -1,6 +1,6 @@ -

Neptune

+

Neptune

- + Neptune @@ -13,15 +13,15 @@

Neptune

astronomical units (4.50×109 km). It is named after the Roman god of the sea and has the astronomical symbol ♆, a stylised version of the god Neptune's trident.

-
+ - - + + - + Read more on Wikipedia - - + diff --git a/src/demo-app/a11y/dialog/dialog-neptune-iframe-a11y.html b/src/demo-app/a11y/dialog/dialog-neptune-iframe-a11y.html index a51b3ad5a958..da7cf120ad29 100644 --- a/src/demo-app/a11y/dialog/dialog-neptune-iframe-a11y.html +++ b/src/demo-app/a11y/dialog/dialog-neptune-iframe-a11y.html @@ -1,9 +1,9 @@ -

Neptune

+

Neptune

- + - + - - - + + + diff --git a/src/demo-app/a11y/dialog/dialog-welcome-a11y.html b/src/demo-app/a11y/dialog/dialog-welcome-a11y.html index 25c8355f7f5f..f1d0cf3074f6 100644 --- a/src/demo-app/a11y/dialog/dialog-welcome-a11y.html +++ b/src/demo-app/a11y/dialog/dialog-welcome-a11y.html @@ -10,4 +10,4 @@

Welcome to Angular Material dialog demo page!

be accessible, enabling people with disabilities to participate equally on the Web.

- + diff --git a/src/demo-app/a11y/expansion/expansion-a11y.html b/src/demo-app/a11y/expansion/expansion-a11y.html new file mode 100644 index 000000000000..3d50dec3273e --- /dev/null +++ b/src/demo-app/a11y/expansion/expansion-a11y.html @@ -0,0 +1,53 @@ +
+

Siberian Husky

+

Single expansion panel

+ + + + Dog breed + + Siberian Husky + + The Siberian Husky is a medium size working dog breed that originated + in north-eastern Siberia, Russia. The breed belongs to the Spitz genetic family. + + + Wikipedia + + +
+ +
+

Dog breeds

+

Multiple expansion panel

+ + + Golden Retriever +

+ The Golden Retriever is a large-sized breed of dog bred as gun dogs to retrieve shot + waterfowl such as ducks and upland game birds during hunting and shooting parties, and + were named 'retriever' because of their ability to retrieve shot game undamaged. + Golden Retrievers have an instinctive love of water, and are easy to train to basic + or advanced obedience standards. +

+
+ + Beagle +

+ The Beagle is a breed of small hound, similar in appearance to the much larger foxhound. + The beagle is a scent hound, developed primarily for hunting hare. +

+
+ + Dachshund +

+ The dachshund is a short-legged, long-bodied, hound-type dog breed. The standard size + dachshund was developed to scent, chase, and flush out badgers and other burrow-dwelling + animals, while the miniature ... +

+ + + +
+
+
diff --git a/src/demo-app/a11y/expansion/expansion-a11y.scss b/src/demo-app/a11y/expansion/expansion-a11y.scss new file mode 100644 index 000000000000..d150f2dddcd5 --- /dev/null +++ b/src/demo-app/a11y/expansion/expansion-a11y.scss @@ -0,0 +1,6 @@ +.demo-expansion { + button, a { + margin: 8px; + text-transform: uppercase; + } +} diff --git a/src/demo-app/a11y/expansion/expansion-a11y.ts b/src/demo-app/a11y/expansion/expansion-a11y.ts new file mode 100644 index 000000000000..511e61c654ce --- /dev/null +++ b/src/demo-app/a11y/expansion/expansion-a11y.ts @@ -0,0 +1,18 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Component} from '@angular/core'; + +@Component({ + moduleId: module.id, + selector: 'expansion-a11y', + templateUrl: 'expansion-a11y.html', + styleUrls: ['expansion-a11y.css'] +}) +export class ExpansionPanelAccessibilityDemo { +} diff --git a/src/demo-app/a11y/grid-list/grid-list-a11y.html b/src/demo-app/a11y/grid-list/grid-list-a11y.html index 0316771a2c52..3627a0948176 100644 --- a/src/demo-app/a11y/grid-list/grid-list-a11y.html +++ b/src/demo-app/a11y/grid-list/grid-list-a11y.html @@ -1,42 +1,42 @@

Types of coffee (fix-height grid-list)

- - + {{tile.text}} - - + +

Types of coffee (ratio-height grid list)

- - + + {{tile.text}} - - + +

Types of coffee (fit-height grid list)

- - + {{tile.text}} - - + +

Angular team dogs (Grid list with header and footer)

- - - {{dog.name}} + + + {{dog.name}} Photo of {{dog.name}} - - Human: {{dog.human}} - - - + + Human: {{dog.human}} + + +
diff --git a/src/demo-app/a11y/grid-list/grid-list-a11y.ts b/src/demo-app/a11y/grid-list/grid-list-a11y.ts index 033c3b207107..de3da59c0008 100644 --- a/src/demo-app/a11y/grid-list/grid-list-a11y.ts +++ b/src/demo-app/a11y/grid-list/grid-list-a11y.ts @@ -1,3 +1,11 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + import {Component} from '@angular/core'; export interface Dog { diff --git a/src/demo-app/a11y/icon/icon-a11y.html b/src/demo-app/a11y/icon/icon-a11y.html index 3b8bc223aa41..9c01019db4f8 100644 --- a/src/demo-app/a11y/icon/icon-a11y.html +++ b/src/demo-app/a11y/icon/icon-a11y.html @@ -1,30 +1,30 @@

Fingerprint status

Showing a status message with a purely decorative icon

- fingerprint + fingerprint Your fingerprint was recorded!

Delete icon button

Showing a button with interactive icon which can perform an action

-

Home link

Showing a link with interactive icon

- - home + + home

Done status

Showing a status indicator with an indicator icon

- done + done Done Tasks
diff --git a/src/demo-app/a11y/icon/icon-a11y.ts b/src/demo-app/a11y/icon/icon-a11y.ts index dfac7d4c04ea..aabf76cd3602 100644 --- a/src/demo-app/a11y/icon/icon-a11y.ts +++ b/src/demo-app/a11y/icon/icon-a11y.ts @@ -1,14 +1,23 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + import {Component, ViewEncapsulation} from '@angular/core'; -import {MdSnackBar} from '@angular/material'; +import {MatSnackBar} from '@angular/material'; @Component({ moduleId: module.id, selector: 'icon-a11y', templateUrl: 'icon-a11y.html', encapsulation: ViewEncapsulation.None, + preserveWhitespaces: false, }) export class IconAccessibilityDemo { - constructor(private snackBar: MdSnackBar) {} + constructor(private snackBar: MatSnackBar) {} deleteIcon() { this.snackBar.open('Item deleted', '', {duration: 2000}); diff --git a/src/demo-app/a11y/input/input-a11y.html b/src/demo-app/a11y/input/input-a11y.html index dabc8dc3b78a..cb9d19dfb345 100644 --- a/src/demo-app/a11y/input/input-a11y.html +++ b/src/demo-app/a11y/input/input-a11y.html @@ -1,57 +1,57 @@

Basic input box (e.g. name field)

- - - - - - + + + + + +

Input with hint (e.g. password field)

- - + - - Hint: favorite color - You must enter your password. - + Hint: favorite color + You must enter your password. +

Input with error message (e.g. email field)

- - + - You must enter your email. - Not a valid email address. - + You must enter your email. + Not a valid email address. +

Input with prefix & suffix (e.g. currency converter)

- - - $ - + + + $ + = - - - ‎¥‎ - + + + ‎¥‎ + (as of 7/31/2017)

Textarea input (e.g. comment box)

- - - Leave us a comment! - {{commentCount}}/{{commentMax}} - + Leave us a comment! + {{commentCount}}/{{commentMax}} +
diff --git a/src/demo-app/a11y/input/input-a11y.ts b/src/demo-app/a11y/input/input-a11y.ts index c1782d33bd9e..0f33b3f93816 100644 --- a/src/demo-app/a11y/input/input-a11y.ts +++ b/src/demo-app/a11y/input/input-a11y.ts @@ -1,3 +1,11 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + import {Component} from '@angular/core'; const USD_TO_JPY = 110.29; diff --git a/src/demo-app/a11y/list/list-a11y.html b/src/demo-app/a11y/list/list-a11y.html new file mode 100644 index 000000000000..70eea8b8410b --- /dev/null +++ b/src/demo-app/a11y/list/list-a11y.html @@ -0,0 +1,66 @@ +
+
+

Seasoning

+

Showing a non-interactive list of seasonings.

+ + {{item}} + +
+ + +
+

Mailbox navigation

+

Showing a navigation list with links to google search

+ + + {{link.name}} + + +
+ +
+

Messages

+

+ Showing a list of messages, where each message item has sender's name, sender's avatar, + message subject, and content of the message. +

+ + + +

{{message.from}}

+

{{message.subject}}

+

{{message.message}}

+
+
+ +
+ +
+

Seasoning

+

Showing a non-interactive list of seasonings with dense style.

+ + {{item}} + +
+ +
+

Folders and notes for mailbox

+

Showing a list with two sections, "folders" and "notes".

+ +

Folders

+ + folder +

{{folder.name}}

+

{{folder.updated}}

+
+ +

Notes

+ + note +

{{note.name}}

+

{{note.updated}}

+
+
+
+
diff --git a/src/demo-app/a11y/list/list-a11y.scss b/src/demo-app/a11y/list/list-a11y.scss new file mode 100644 index 000000000000..1d270debe13d --- /dev/null +++ b/src/demo-app/a11y/list/list-a11y.scss @@ -0,0 +1,11 @@ +.demo-list { + .mat-list, .mat-nav-list { + border: 1px solid rgba(0, 0, 0, 0.12); + max-width: 350px; + margin: 20px 20px 0 0; + } + + .demo-secondary-text { + color: rgba(0, 0, 0, 0.54); + } +} diff --git a/src/demo-app/a11y/list/list-a11y.ts b/src/demo-app/a11y/list/list-a11y.ts new file mode 100644 index 000000000000..3a033d24cb58 --- /dev/null +++ b/src/demo-app/a11y/list/list-a11y.ts @@ -0,0 +1,63 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Component} from '@angular/core'; + +@Component({ + moduleId: module.id, + selector: 'list-a11y', + templateUrl: 'list-a11y.html', + styleUrls: ['list-a11y.css'] +}) +export class ListAccessibilityDemo { + items: string[] = [ + 'Pepper', + 'Salt', + 'Paprika' + ]; + + messages = [ + { + from: 'Nancy', + subject: 'Brunch?', + message: 'Did you want to go on Sunday? I was thinking that might work.', + image: 'https://angular.io/generated/images/bios/julie-ralph.jpg' + }, + { + from: 'Mary', + subject: 'Summer BBQ', + message: 'Wish I could come, but I have some prior obligations.', + image: 'https://angular.io/generated/images/bios/juleskremer.jpg' + }, + { + from: 'Bobby', + subject: 'Oui oui', + message: 'Do you have Paris reservations for the 15th? I just booked!', + image: 'https://angular.io/generated/images/bios/jelbourn.jpg' + } + ]; + + links = [ + {name: 'Inbox'}, + {name: 'Outbox'}, + {name: 'Spam'}, + {name: 'Trash'} + + ]; + + folders = [ + {name: 'Imported', updated: 'Miles'}, + {name: 'Important', updated: 'Tina'}, + {name: 'Unread', updated: 'Jeremy'}, + ]; + + notes = [ + {name: 'Update screenshots', updated: 'Kara'}, + {name: 'Install new application', updated: 'Andrew'}, + ]; +} diff --git a/src/demo-app/a11y/menu/menu-a11y.html b/src/demo-app/a11y/menu/menu-a11y.html index 250143c1a87d..0b810c70e2f7 100644 --- a/src/demo-app/a11y/menu/menu-a11y.html +++ b/src/demo-app/a11y/menu/menu-a11y.html @@ -2,52 +2,52 @@

Icon Trigger

- - - - + + + + -

Menu with Icons

- - - - - - +

Menu with links

- - - + + Angular - + Angular Material - +
diff --git a/src/demo-app/a11y/menu/menu-a11y.ts b/src/demo-app/a11y/menu/menu-a11y.ts index 2d7c5d900ec3..60b6fd319cda 100644 --- a/src/demo-app/a11y/menu/menu-a11y.ts +++ b/src/demo-app/a11y/menu/menu-a11y.ts @@ -1,3 +1,11 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + import {Component} from '@angular/core'; @Component({ diff --git a/src/demo-app/a11y/progress-bar/progress-bar-a11y.html b/src/demo-app/a11y/progress-bar/progress-bar-a11y.html new file mode 100644 index 000000000000..416f7e64e237 --- /dev/null +++ b/src/demo-app/a11y/progress-bar/progress-bar-a11y.html @@ -0,0 +1,24 @@ +
+

Survey progress (Determinate progress bar)

+ + +
+ +
+

Video progress (Progress bar with buffer)

+ + +
+ +
+

Loading content progress (Indeterminate progress bar)

+ + +
+ +
+

Search progress (Query progress bar)

+ +
diff --git a/src/demo-app/a11y/progress-bar/progress-bar-a11y.ts b/src/demo-app/a11y/progress-bar/progress-bar-a11y.ts new file mode 100644 index 000000000000..5cb3297b5170 --- /dev/null +++ b/src/demo-app/a11y/progress-bar/progress-bar-a11y.ts @@ -0,0 +1,21 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Component} from '@angular/core'; + + +@Component({ + moduleId: module.id, + selector: 'progress-bar-a11y', + templateUrl: 'progress-bar-a11y.html', +}) +export class ProgressBarAccessibilityDemo { + surveyProgress: number = 30; + videoPlayValue: number = 20; + videoBufferValue: number = 60; +} diff --git a/src/demo-app/a11y/progress-spinner/progress-spinner-a11y.html b/src/demo-app/a11y/progress-spinner/progress-spinner-a11y.html index ea7809a443eb..acf69bb60136 100644 --- a/src/demo-app/a11y/progress-spinner/progress-spinner-a11y.html +++ b/src/demo-app/a11y/progress-spinner/progress-spinner-a11y.html @@ -1,16 +1,16 @@

Loading indicator (Indeterminate progress spinner)

- - + +

Portion of pizza eaten (Determinate progress spinner)

- - - + - +
diff --git a/src/demo-app/a11y/progress-spinner/progress-spinner-a11y.ts b/src/demo-app/a11y/progress-spinner/progress-spinner-a11y.ts index 9cb0a66f8018..5502b9854fa1 100644 --- a/src/demo-app/a11y/progress-spinner/progress-spinner-a11y.ts +++ b/src/demo-app/a11y/progress-spinner/progress-spinner-a11y.ts @@ -1,3 +1,11 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + import {Component} from '@angular/core'; @Component({ diff --git a/src/demo-app/a11y/radio/radio-a11y.html b/src/demo-app/a11y/radio/radio-a11y.html index 7ae213f85fed..8a349a95f967 100644 --- a/src/demo-app/a11y/radio/radio-a11y.html +++ b/src/demo-app/a11y/radio/radio-a11y.html @@ -1,19 +1,19 @@

Radio buttons in group

- - + + {{season}} - - + +

Radio buttons with align-end label position

- - Toast - Biscuit - Bagel - + + Toast + Biscuit + Bagel +
@@ -22,15 +22,15 @@

Disabled radio buttons

This section contains three radio buttons for "Yes", "No", and "Maybe". The "Maybe" radio button is disabled.

- Yes - No - Maybe + Yes + No + Maybe

Radio buttons with different colors

- Default (accent) - Primary - Accent - Warn + Default (accent) + Primary + Accent + Warn
diff --git a/src/demo-app/a11y/radio/radio-a11y.ts b/src/demo-app/a11y/radio/radio-a11y.ts index 093dfabf3be3..3f2e6a172fdd 100644 --- a/src/demo-app/a11y/radio/radio-a11y.ts +++ b/src/demo-app/a11y/radio/radio-a11y.ts @@ -1,3 +1,11 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + import {Component} from '@angular/core'; diff --git a/src/demo-app/a11y/routes.ts b/src/demo-app/a11y/routes.ts index 1d40d0cca555..8ce0372611af 100644 --- a/src/demo-app/a11y/routes.ts +++ b/src/demo-app/a11y/routes.ts @@ -1,3 +1,11 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + import {Routes} from '@angular/router'; import {AutocompleteAccessibilityDemo} from './autocomplete/autocomplete-a11y'; import {ButtonAccessibilityDemo} from './button/button-a11y'; @@ -6,6 +14,7 @@ import {CardAccessibilityDemo} from './card/card-a11y'; import {CheckboxAccessibilityDemo} from './checkbox/checkbox-a11y'; import {ChipsAccessibilityDemo} from './chips/chips-a11y'; import {DialogAccessibilityDemo} from './dialog/dialog-a11y'; +import {ExpansionPanelAccessibilityDemo} from './expansion/expansion-a11y'; import {GridListAccessibilityDemo} from './grid-list/grid-list-a11y'; import {RadioAccessibilityDemo} from './radio/radio-a11y'; import {AccessibilityHome} from './a11y'; @@ -13,12 +22,22 @@ import {ToolbarAccessibilityDemo} from './toolbar/toolbar-a11y'; import {DatepickerAccessibilityDemo} from './datepicker/datepicker-a11y'; import {IconAccessibilityDemo} from './icon/icon-a11y'; import {InputAccessibilityDemo} from './input/input-a11y'; +import {ListAccessibilityDemo} from './list/list-a11y'; import {MenuAccessibilityDemo} from './menu/menu-a11y'; +import {ProgressBarAccessibilityDemo} from './progress-bar/progress-bar-a11y'; import {ProgressSpinnerAccessibilityDemo} from './progress-spinner/progress-spinner-a11y'; import {SliderAccessibilityDemo} from './slider/slider-a11y'; import {SlideToggleAccessibilityDemo} from './slide-toggle/slide-toggle-a11y'; import {SnackBarAccessibilityDemo} from './snack-bar/snack-bar-a11y'; import {SelectAccessibilityDemo} from './select/select-a11y'; +import {TableAccessibilityDemo} from './table/table-a11y'; +import {TabsAccessibilityDemo} from './tabs/tabs-a11y'; +import {TABS_DEMO_ROUTES} from './tabs/routes'; +import {TooltipAccessibilityDemo} from './tooltip/tooltip-a11y'; +import {SidenavAccessibilityDemo} from './sidenav/sidenav-a11y'; +import {SidenavBasicAccessibilityDemo} from './sidenav/basic-sidenav-a11y'; +import {SidenavDualAccessibilityDemo} from './sidenav/dual-sidenav-a11y'; +import {SidenavMobileAccessibilityDemo} from './sidenav/mobile-sidenav-a11y'; export const ACCESSIBILITY_DEMO_ROUTES: Routes = [ {path: '', component: AccessibilityHome}, @@ -30,15 +49,25 @@ export const ACCESSIBILITY_DEMO_ROUTES: Routes = [ {path: 'chips', component: ChipsAccessibilityDemo}, {path: 'datepicker', component: DatepickerAccessibilityDemo}, {path: 'dialog', component: DialogAccessibilityDemo}, + {path: 'expansion', component: ExpansionPanelAccessibilityDemo}, {path: 'grid-list', component: GridListAccessibilityDemo}, {path: 'icon', component: IconAccessibilityDemo}, {path: 'input', component: InputAccessibilityDemo}, + {path: 'list', component: ListAccessibilityDemo}, {path: 'menu', component: MenuAccessibilityDemo}, + {path: 'progress-bar', component: ProgressBarAccessibilityDemo}, {path: 'progress-spinner', component: ProgressSpinnerAccessibilityDemo}, {path: 'radio', component: RadioAccessibilityDemo}, - {path: 'slider', component: SliderAccessibilityDemo}, + {path: 'select', component: SelectAccessibilityDemo}, + {path: 'sidenav', component: SidenavAccessibilityDemo}, + {path: 'sidenav/basic', component: SidenavBasicAccessibilityDemo, data: {fullscreen: true}}, + {path: 'sidenav/dual', component: SidenavDualAccessibilityDemo, data: {fullscreen: true}}, + {path: 'sidenav/mobile', component: SidenavMobileAccessibilityDemo, data: {fullscreen: true}}, {path: 'slide-toggle', component: SlideToggleAccessibilityDemo}, + {path: 'slider', component: SliderAccessibilityDemo}, {path: 'snack-bar', component: SnackBarAccessibilityDemo}, - {path: 'select', component: SelectAccessibilityDemo}, + {path: 'tabs', component: TabsAccessibilityDemo, children: TABS_DEMO_ROUTES}, {path: 'toolbar', component: ToolbarAccessibilityDemo}, + {path: 'table', component: TableAccessibilityDemo}, + {path: 'tooltip', component: TooltipAccessibilityDemo}, ]; diff --git a/src/demo-app/a11y/select/select-a11y.html b/src/demo-app/a11y/select/select-a11y.html index 26599933be10..829809d74987 100644 --- a/src/demo-app/a11y/select/select-a11y.html +++ b/src/demo-app/a11y/select/select-a11y.html @@ -2,71 +2,89 @@

Single selection

Select your favorite color

- - - {{ color.label }} - - + + + + {{ color.label }} + + +

Multiple selection

Pick toppings for your pizza

- - - {{ topping.label }} - - + + + + {{ topping.label }} + + +

Grouped options

Pick your Pokemon

- - - - {{ creature.label }} - - - + + + + + {{ creature.label }} + + + +

Colors

- - 2000 - 2100 - + + + 2000 + 2100 + + - - Alaska - Alabama - + + + Alaska + Alabama + + - - English - Spanish - + + + English + Spanish + +
- - Mihiramon - Sandiramon - + + + Mihiramon + Sandiramon + + - - Water - Coke - + + + Water + Coke + + - - Light - Dark - + + + Light + Dark + +
diff --git a/src/demo-app/a11y/select/select-a11y.ts b/src/demo-app/a11y/select/select-a11y.ts index 21e4fd72744b..ced37285c640 100644 --- a/src/demo-app/a11y/select/select-a11y.ts +++ b/src/demo-app/a11y/select/select-a11y.ts @@ -1,3 +1,11 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + import {Component} from '@angular/core'; diff --git a/src/demo-app/a11y/sidenav/basic-sidenav-a11y.html b/src/demo-app/a11y/sidenav/basic-sidenav-a11y.html new file mode 100644 index 000000000000..38dc634eb0a5 --- /dev/null +++ b/src/demo-app/a11y/sidenav/basic-sidenav-a11y.html @@ -0,0 +1,24 @@ + + +

Basic Sidenav App

+
+ + + + + Home + Basic sidenav example + Responsive sidenav example + Dual sidenavs example + + + + + + + diff --git a/src/demo-app/a11y/sidenav/basic-sidenav-a11y.ts b/src/demo-app/a11y/sidenav/basic-sidenav-a11y.ts new file mode 100644 index 000000000000..fa6d1741a98d --- /dev/null +++ b/src/demo-app/a11y/sidenav/basic-sidenav-a11y.ts @@ -0,0 +1,21 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Component, ViewEncapsulation} from '@angular/core'; + + +@Component({ + moduleId: module.id, + selector: 'basic-sidenav-a11y', + templateUrl: 'basic-sidenav-a11y.html', + styleUrls: ['shared.css'], + host: {'class': 'a11y-demo-sidenav-app'}, + encapsulation: ViewEncapsulation.None, + preserveWhitespaces: false, +}) +export class SidenavBasicAccessibilityDemo {} diff --git a/src/demo-app/a11y/sidenav/dual-sidenav-a11y.html b/src/demo-app/a11y/sidenav/dual-sidenav-a11y.html new file mode 100644 index 000000000000..38b6c43bb23e --- /dev/null +++ b/src/demo-app/a11y/sidenav/dual-sidenav-a11y.html @@ -0,0 +1,43 @@ + + +

Dual Sidenav App

+ + +
+ + + + + Home + Basic sidenav example + Responsive sidenav example + Dual sidenavs example + + + + + + + + +

Playlists

+ + + +
+
diff --git a/src/demo-app/a11y/sidenav/dual-sidenav-a11y.scss b/src/demo-app/a11y/sidenav/dual-sidenav-a11y.scss new file mode 100644 index 000000000000..7dca328564d8 --- /dev/null +++ b/src/demo-app/a11y/sidenav/dual-sidenav-a11y.scss @@ -0,0 +1,13 @@ +.a11y-demo-sidenav-spacer { + flex: 1; +} + +mat-sidenav.a11y-demo-sidenav-playlist { + display: flex; + flex-direction: column; + width: 200px; +} + +.a11y-demo-sidenav-playlist-header { + text-align: center; +} diff --git a/src/demo-app/a11y/sidenav/dual-sidenav-a11y.ts b/src/demo-app/a11y/sidenav/dual-sidenav-a11y.ts new file mode 100644 index 000000000000..4075ed3577ab --- /dev/null +++ b/src/demo-app/a11y/sidenav/dual-sidenav-a11y.ts @@ -0,0 +1,28 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Component, ViewEncapsulation} from '@angular/core'; +import {MatSnackBar} from '@angular/material/snack-bar'; + + +@Component({ + moduleId: module.id, + selector: 'dual-sidenav-a11y', + templateUrl: 'dual-sidenav-a11y.html', + styleUrls: ['shared.css', 'dual-sidenav-a11y.css'], + host: {'class': 'a11y-demo-sidenav-app'}, + encapsulation: ViewEncapsulation.None, + preserveWhitespaces: false, +}) +export class SidenavDualAccessibilityDemo { + constructor(private _snackbar: MatSnackBar) {} + + play(list: string) { + this._snackbar.open(`Playing "${list}"`, '', {duration: 1000}); + } +} diff --git a/src/demo-app/a11y/sidenav/mobile-sidenav-a11y.html b/src/demo-app/a11y/sidenav/mobile-sidenav-a11y.html new file mode 100644 index 000000000000..addb089205bd --- /dev/null +++ b/src/demo-app/a11y/sidenav/mobile-sidenav-a11y.html @@ -0,0 +1,34 @@ + + +

Responsive Sidenav App

+
+ + + + + Home + Basic sidenav example + Responsive sidenav example + Dual sidenavs example + +

Filler content

+
+ + + +

Filler content

+
+
diff --git a/src/demo-app/a11y/sidenav/mobile-sidenav-a11y.scss b/src/demo-app/a11y/sidenav/mobile-sidenav-a11y.scss new file mode 100644 index 000000000000..5e3b79c72cb1 --- /dev/null +++ b/src/demo-app/a11y/sidenav/mobile-sidenav-a11y.scss @@ -0,0 +1,4 @@ +.a11y-demo-sidenav-header-fixed { + position: fixed; + z-index: 2; +} diff --git a/src/demo-app/a11y/sidenav/mobile-sidenav-a11y.ts b/src/demo-app/a11y/sidenav/mobile-sidenav-a11y.ts new file mode 100644 index 000000000000..0c9a72f6a678 --- /dev/null +++ b/src/demo-app/a11y/sidenav/mobile-sidenav-a11y.ts @@ -0,0 +1,38 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {MediaMatcher} from '@angular/cdk/layout'; +import {ChangeDetectorRef, Component, OnDestroy, ViewEncapsulation} from '@angular/core'; + + +@Component({ + moduleId: module.id, + selector: 'mobile-sidenav-a11y', + templateUrl: 'mobile-sidenav-a11y.html', + styleUrls: ['shared.css', 'mobile-sidenav-a11y.css'], + host: {'class': 'a11y-demo-sidenav-app'}, + encapsulation: ViewEncapsulation.None, + preserveWhitespaces: false, +}) +export class SidenavMobileAccessibilityDemo implements OnDestroy { + mobileQuery: MediaQueryList; + + filler = Array(20).fill(0); + + _mobileQueryListener: () => void; + + constructor(changeDetectorRef: ChangeDetectorRef, media: MediaMatcher) { + this._mobileQueryListener = () => changeDetectorRef.detectChanges(); + this.mobileQuery = media.matchMedia('(max-width: 600px)'); + this.mobileQuery.addListener(this._mobileQueryListener); + } + + ngOnDestroy(): void { + this.mobileQuery.removeListener(this._mobileQueryListener); + } +} diff --git a/src/demo-app/a11y/sidenav/shared.scss b/src/demo-app/a11y/sidenav/shared.scss new file mode 100644 index 000000000000..7311cf913d93 --- /dev/null +++ b/src/demo-app/a11y/sidenav/shared.scss @@ -0,0 +1,29 @@ +.a11y-demo-sidenav-app { + display: flex; + flex-direction: column; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; +} + +.a11y-demo-sidenav-container { + flex: 1; +} + +.a11y-demo-sidenav-container-fixed { + flex: none; +} + +h1.a11y-demo-sidenav-app-name { + margin-left: 8px; +} + +button.a11y-demo-sidenav-close { + margin: 20px; +} + +.a11y-demo-sidenav-filler { + margin: 100px 20px; +} diff --git a/src/demo-app/a11y/sidenav/sidenav-a11y.html b/src/demo-app/a11y/sidenav/sidenav-a11y.html new file mode 100644 index 000000000000..320b9babbabe --- /dev/null +++ b/src/demo-app/a11y/sidenav/sidenav-a11y.html @@ -0,0 +1,20 @@ +
+

Basic sidenav (e.g. desktop app with hamburger menu)

+ + View example + +
+ +
+

Mobile sidenav (e.g. responsive design hamburger menu)

+ + View example + +
+ +
+

Dual sidenavs (e.g. music app with hamburger menu and playlist sidenav)

+ + View example + +
diff --git a/src/demo-app/a11y/sidenav/sidenav-a11y.ts b/src/demo-app/a11y/sidenav/sidenav-a11y.ts new file mode 100644 index 000000000000..529ae262d1ab --- /dev/null +++ b/src/demo-app/a11y/sidenav/sidenav-a11y.ts @@ -0,0 +1,18 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Component} from '@angular/core'; + + +@Component({ + moduleId: module.id, + selector: 'sidenav-a11y', + templateUrl: 'sidenav-a11y.html', + preserveWhitespaces: false, +}) +export class SidenavAccessibilityDemo {} diff --git a/src/demo-app/a11y/slide-toggle/slide-toggle-a11y.html b/src/demo-app/a11y/slide-toggle/slide-toggle-a11y.html index 3fe2d1ef16be..80ae889df807 100644 --- a/src/demo-app/a11y/slide-toggle/slide-toggle-a11y.html +++ b/src/demo-app/a11y/slide-toggle/slide-toggle-a11y.html @@ -1,28 +1,28 @@

Receive email update

Showing a toggle to manage whether receive email update.

- + Receive email update - +

Music

Showing a disabled toggle to control music on/off status.

- + Music {{musicToggle ? 'on' : 'off'}} (disabled) - +

Terms and conditions

Showing a required toggle to accept terms and conditions in a form.

- + I agree to terms and conditions - +

- +

diff --git a/src/demo-app/a11y/slide-toggle/slide-toggle-a11y.ts b/src/demo-app/a11y/slide-toggle/slide-toggle-a11y.ts index 6975bcfcd8ba..f97be6e8159e 100644 --- a/src/demo-app/a11y/slide-toggle/slide-toggle-a11y.ts +++ b/src/demo-app/a11y/slide-toggle/slide-toggle-a11y.ts @@ -1,5 +1,13 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + import {Component} from '@angular/core'; -import {MdSnackBar} from '@angular/material'; +import {MatSnackBar} from '@angular/material'; @Component({ @@ -12,7 +20,7 @@ export class SlideToggleAccessibilityDemo { termsToggle: boolean = false; musicToggle: boolean = false; - constructor(private snackBar: MdSnackBar) {} + constructor(private snackBar: MatSnackBar) {} onFormSubmit() { this.snackBar.open('Terms and condistions accepted!', '', {duration: 2000}); diff --git a/src/demo-app/a11y/slider/slider-a11y.html b/src/demo-app/a11y/slider/slider-a11y.html index b4d31868b510..baa781c32f0c 100644 --- a/src/demo-app/a11y/slider/slider-a11y.html +++ b/src/demo-app/a11y/slider/slider-a11y.html @@ -5,15 +5,15 @@

Continuous slider (e.g. color component sliders)

- +
- +
- +
@@ -23,7 +23,7 @@

Continuous slider (e.g. color component sliders)

Discrete slider (e.g. rate a product)

Please rate our product on a scale of 1 (not satisfied) to 5 (extremely satisfied).

- +
@@ -31,9 +31,9 @@

Vertical slider (e.g. volume control)

Use the slider to adjust the volume.

- +
diff --git a/src/demo-app/a11y/slider/slider-a11y.ts b/src/demo-app/a11y/slider/slider-a11y.ts index 2ab15ff5e0cd..2d72c0bc02da 100644 --- a/src/demo-app/a11y/slider/slider-a11y.ts +++ b/src/demo-app/a11y/slider/slider-a11y.ts @@ -1,3 +1,11 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + import {Component} from '@angular/core'; @Component({ diff --git a/src/demo-app/a11y/snack-bar/snack-bar-a11y.html b/src/demo-app/a11y/snack-bar/snack-bar-a11y.html index 0065282adccb..45e955476faf 100644 --- a/src/demo-app/a11y/snack-bar/snack-bar-a11y.html +++ b/src/demo-app/a11y/snack-bar/snack-bar-a11y.html @@ -1,7 +1,7 @@

Notification

Showing a notification by snack bar without showing actions

-
@@ -9,5 +9,5 @@

Notification

Disco party

Showing a notification by snack bar with a dismiss button

- +
diff --git a/src/demo-app/a11y/snack-bar/snack-bar-a11y.ts b/src/demo-app/a11y/snack-bar/snack-bar-a11y.ts index 8a4953fc8061..d8378fd3df2f 100644 --- a/src/demo-app/a11y/snack-bar/snack-bar-a11y.ts +++ b/src/demo-app/a11y/snack-bar/snack-bar-a11y.ts @@ -1,5 +1,13 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + import {Component} from '@angular/core'; -import {MdSnackBar} from '@angular/material'; +import {MatSnackBar} from '@angular/material'; @Component({ moduleId: module.id, @@ -7,7 +15,7 @@ import {MdSnackBar} from '@angular/material'; templateUrl: 'snack-bar-a11y.html', }) export class SnackBarAccessibilityDemo { - constructor(private snackBar: MdSnackBar) {} + constructor(private snackBar: MatSnackBar) {} openDiscoPartySnackBar() { this.snackBar.open('Disco party!', 'Dismiss', {duration: 5000}); diff --git a/src/demo-app/a11y/table/table-a11y.html b/src/demo-app/a11y/table/table-a11y.html new file mode 100644 index 000000000000..ed2166548046 --- /dev/null +++ b/src/demo-app/a11y/table/table-a11y.html @@ -0,0 +1,78 @@ +
+
+

Basic Table

+

Shows name, color and age data.

+ + + Name + {{row.name}} + + + Color + {{row.color}} + + + Age + {{row.age}} + + + + +
+ +
+

Sortable Table

+

Shows name, color and age data. Sorted ascending by age.

+ + + Name + {{row.name}} + + + Color + {{row.color}} + + + Age + {{row.age}} + + + + +
+ +
+

Paginated Table

+

Shows name, color and age data. Shows only first 5 until paginated.

+ + + Name + {{row.name}} + + + Color + {{row.color}} + + + Age + {{row.age}} + + + + + + +
+
diff --git a/src/demo-app/a11y/table/table-a11y.scss b/src/demo-app/a11y/table/table-a11y.scss new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/src/demo-app/a11y/table/table-a11y.ts b/src/demo-app/a11y/table/table-a11y.ts new file mode 100644 index 000000000000..ab7c48807422 --- /dev/null +++ b/src/demo-app/a11y/table/table-a11y.ts @@ -0,0 +1,125 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Component, ViewChild} from '@angular/core'; +import {DataSource} from '@angular/cdk/table'; +import {BehaviorSubject} from 'rxjs/BehaviorSubject'; +import {Observable} from 'rxjs/Observable'; +import {MatSort, MatPaginator} from '@angular/material'; +import {merge} from 'rxjs/observable/merge'; +import {map} from 'rxjs/operators/map'; + +export interface UserData { + name: string; + color: string; + age: number; +} + +const exampleData = [ + {name: 'Austin', color: 'blue', age: 30}, + {name: 'Jeremy', color: 'green', age: 33}, + {name: 'Kara', color: 'purple', age: 29}, + {name: 'Tina', color: 'yellow', age: 35}, + {name: 'Brad', color: 'pink', age: 40}, + {name: 'Jules', color: 'red', age: 21}, +]; + +@Component({ + moduleId: module.id, + selector: 'table-a11y', + templateUrl: 'table-a11y.html', + styleUrls: ['table-a11y.css'], +}) +export class TableAccessibilityDemo { + @ViewChild(MatSort) sort: MatSort; + @ViewChild(MatPaginator) pager: MatPaginator; + + displayedColumns = ['name', 'color', 'age']; + basicDataSource: BasicDataSource; + sortDataSource: SortDataSource; + paginatedDataSource: PaginatedDataSource; + + ngOnInit(): void { + this.basicDataSource = new BasicDataSource(); + this.sortDataSource = new SortDataSource(this.sort); + this.paginatedDataSource = new PaginatedDataSource(this.pager); + } +} + +export class BasicDataSource extends DataSource { + dataChange: BehaviorSubject = new BehaviorSubject([]); + + constructor() { + super(); + this.dataChange.next(exampleData); + } + + connect(): Observable { + return this.dataChange; + } + + disconnect() {} +} + +export class SortDataSource extends DataSource { + dataChange: BehaviorSubject = new BehaviorSubject([]); + + constructor(private _sort: MatSort) { + super(); + this.dataChange.next(exampleData); + } + + connect(): Observable { + const displayDataChanges = [ + this.dataChange, + this._sort.sortChange, + ]; + + return merge(...displayDataChanges).pipe(map(() => this.getSortedData())); + } + + disconnect() {} + + getSortedData(): UserData[] { + const data = [...exampleData]; + if (!this._sort.active || this._sort.direction == '') { + return data; + } + + return data.sort((a: UserData, b: UserData) => { + return (a.age < b.age ? -1 : 1) * (this._sort.direction == 'asc' ? 1 : -1); + }); + } +} + +export class PaginatedDataSource extends DataSource { + dataChange: BehaviorSubject = new BehaviorSubject([]); + + constructor(private _paginator: MatPaginator) { + super(); + this.dataChange.next(exampleData); + } + + connect(): Observable { + const displayDataChanges = [ + this.dataChange, + this._paginator.page, + ]; + + return merge(...displayDataChanges) + .pipe( + map(() => { + const data = [...exampleData]; + const startIndex = this._paginator.pageIndex * this._paginator.pageSize; + return data.splice(startIndex, this._paginator.pageSize); + }) + ); + } + + disconnect() {} +} diff --git a/src/demo-app/a11y/tabs/routes.ts b/src/demo-app/a11y/tabs/routes.ts new file mode 100644 index 000000000000..f99882d25f8f --- /dev/null +++ b/src/demo-app/a11y/tabs/routes.ts @@ -0,0 +1,18 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Routes} from '@angular/router'; + +import {SunnyTabContent, RainyTabContent, FoggyTabContent} from './tabs-a11y'; + +export const TABS_DEMO_ROUTES: Routes = [ + {path: '', redirectTo: 'sunny-tab', pathMatch: 'full'}, + {path: 'sunny-tab', component: SunnyTabContent}, + {path: 'rainy-tab', component: RainyTabContent}, + {path: 'foggy-tab', component: FoggyTabContent}, +]; diff --git a/src/demo-app/a11y/tabs/tabs-a11y.html b/src/demo-app/a11y/tabs/tabs-a11y.html new file mode 100644 index 000000000000..d391431dc90a --- /dev/null +++ b/src/demo-app/a11y/tabs/tabs-a11y.html @@ -0,0 +1,63 @@ +
+

Weather

+

Switch tabs to navigate

+ + + +
+ +
+

Dog breeds

+

Dynamic height tabs

+ + + + {{tab.label}} +
+ {{tab.content}} +
+
+
+ The Labrador Retriever, also Labrador, is a type of retriever-gun dog. The Labrador is + one of the most popular breeds of dog in the United Kingdom and the United States. +
+
+ A favourite disability assistance breed in many countries, Labradors are frequently + trained to aid the blind, those who have autism, to act as a therapy dog and perform + screening and detection work for law enforcement and other official agencies. They are + prized as sporting and hunting dogs. +
+
+ A few kennels breeding their ancestors, the St. John's water dog, were in England. + At the same time, a combination of the sheep protection policy in Newfoundland and rabies + quarantine in the United Kingdom, led to the gradual demise of the St. John's water dog + in Canada. +
+
+ In the 1830s, the 10th Earl of Home and his nephews the 5th Duke of Buccleuch and Lord + John Scott, had imported progenitors of the breed from Newfoundland to Europe for + use as gundogs. Another early advocate of these Newfoundland dogs, or Labrador Retrievers + as they later became known, was the 2nd Earl of Malmesbury who bred them for their + expertise in waterfowling. + + During the 1880s, the 3rd Earl of Malmesbury, the 6th Duke of Buccleuch and the 12th Earl + of Home collaborated to develop and establish the modern Labrador breed. The dogs + Buccleuch Avon and Buccleuch Ned, given by Malmesbury to Buccleuch, were mated with + bitches carrying blood from those originally imported by the 5th Duke and the 10th Earl + of Home. The offspring are considered to be the ancestors of modern Labradors. +
+
+
+
+
+
+
+ diff --git a/src/demo-app/a11y/tabs/tabs-a11y.scss b/src/demo-app/a11y/tabs/tabs-a11y.scss new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/src/demo-app/a11y/tabs/tabs-a11y.ts b/src/demo-app/a11y/tabs/tabs-a11y.ts new file mode 100644 index 000000000000..7d3da29c74a8 --- /dev/null +++ b/src/demo-app/a11y/tabs/tabs-a11y.ts @@ -0,0 +1,73 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Component} from '@angular/core'; + +@Component({ + moduleId: module.id, + selector: 'tabs-a11y', + templateUrl: 'tabs-a11y.html', + styleUrls: ['tabs-a11y.css'], +}) +export class TabsAccessibilityDemo { + // Nav bar demo + tabLinks = [ + {label: 'Sun', link: 'sunny-tab'}, + {label: 'Rain', link: 'rainy-tab'}, + {label: 'Fog', link: 'foggy-tab'}, + ]; + + // Standard tabs demo + tabs = [ + { + label: 'German Shepherd', + content: `The German Shepherd is a breed of medium to large-sized working dog that originated + in Germany. The breed's officially recognized name is German Shepherd Dog in the + English language. The breed is also known as the Alsatian in Britain and Ireland.` + }, { + label: 'Labrador Retriever', + extraContent: true, + content: `The Labrador Retriever, also Labrador, is a type of retriever-gun dog. The Labrador + is one of the most popular breeds of dog in the United Kingdom and the United States.` + }, { + label: 'Rottweiler', + disabled: true, + content: `The Rottweiler is a breed of domestic dog, regarded as medium-to-large or large. + The dogs were known in German as Rottweiler Metzgerhund, meaning Rottweil butchers' dogs, + because their main use was to ...` + }, { + label: 'Beagle', + content: `The Beagle is a breed of small hound, similar in appearance to the much larger + foxhound. The beagle is a scent hound, developed primarily for hunting hare.` + }, + ]; +} + + +@Component({ + moduleId: module.id, + selector: 'sunny-routed-content', + template: 'Having a lot of light from the sun.', +}) +export class SunnyTabContent {} + + +@Component({ + moduleId: module.id, + selector: 'rainy-routed-content', + template: 'A rainy period of time is one when it rains a lot', +}) +export class RainyTabContent {} + + +@Component({ + moduleId: module.id, + selector: 'foggy-routed-content', + template: 'If the weather is foggy, there is fog', +}) +export class FoggyTabContent {} diff --git a/src/demo-app/a11y/toolbar/toolbar-a11y.html b/src/demo-app/a11y/toolbar/toolbar-a11y.html index d50e35d6ccc3..064da06c75d9 100644 --- a/src/demo-app/a11y/toolbar/toolbar-a11y.html +++ b/src/demo-app/a11y/toolbar/toolbar-a11y.html @@ -1,40 +1,40 @@

Basic Toolbar with Text (e.g. Only display app’s name)

- +

My App

-
+

Hello World!

Multiple Lines Toolbar

- +

Settings

-
- + +

Profile

-
+

My profile

Toolbar with favorite icon

- +

My App

- -
+

Hello World!

Toolbar colors

- +

My App

-
+

Hello World!

diff --git a/src/demo-app/a11y/toolbar/toolbar-a11y.ts b/src/demo-app/a11y/toolbar/toolbar-a11y.ts index d4d213974f1f..659ad1d33332 100644 --- a/src/demo-app/a11y/toolbar/toolbar-a11y.ts +++ b/src/demo-app/a11y/toolbar/toolbar-a11y.ts @@ -1,3 +1,11 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + import {Component} from '@angular/core'; diff --git a/src/demo-app/a11y/tooltip/tooltip-a11y.html b/src/demo-app/a11y/tooltip/tooltip-a11y.html new file mode 100644 index 000000000000..699f019dfcee --- /dev/null +++ b/src/demo-app/a11y/tooltip/tooltip-a11y.html @@ -0,0 +1,45 @@ +
+

Mouse over or tab to trigger a tooltip

+

Mouse over or focus the button to show and hide the tooltip

+ +
+ + +
+

Click to trigger a tooltip

+ + +
+ + +
+

Different tooltip positions

+ + + + + + + + +
+ +
+

Delayed tooltip

+ +
diff --git a/src/demo-app/a11y/tooltip/tooltip-a11y.ts b/src/demo-app/a11y/tooltip/tooltip-a11y.ts new file mode 100644 index 000000000000..074d6439f530 --- /dev/null +++ b/src/demo-app/a11y/tooltip/tooltip-a11y.ts @@ -0,0 +1,16 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Component} from '@angular/core'; + +@Component({ + moduleId: module.id, + selector: 'tooltip-a11y', + templateUrl: 'tooltip-a11y.html', +}) +export class TooltipAccessibilityDemo {} diff --git a/src/demo-app/autocomplete/autocomplete-demo.html b/src/demo-app/autocomplete/autocomplete-demo.html index 5ca27f2a5d0b..04321b056aa5 100644 --- a/src/demo-app/autocomplete/autocomplete-demo.html +++ b/src/demo-app/autocomplete/autocomplete-demo.html @@ -1,87 +1,74 @@ Space above cards:
- + Reactive length: {{ reactiveStates.length }}
Reactive value: {{ stateCtrl.value | json }}
Reactive dirty: {{ stateCtrl.dirty }}
- - - - + + + + {{ state.name }} ({{state.code}}) - - - + + + - - - - + + - + -
+ - +
Template-driven value (currentState): {{ currentState }}
Template-driven dirty: {{ modelDir ? modelDir.dirty : false }}
- - + - - + + {{ state.name }} - - - + + + - - - - + + - + -
+ - +
Option groups (currentGroupedState): {{ currentGroupedState }}
- + - -
+ +
- - - {{ state.name }} - ({{state.code}}) - - - - - - {{ state.name }} - - - - - + - {{ state.name }} - - + {{ state.name }} + + diff --git a/src/demo-app/autocomplete/autocomplete-demo.ts b/src/demo-app/autocomplete/autocomplete-demo.ts index a138a007df82..3a70ab24a490 100644 --- a/src/demo-app/autocomplete/autocomplete-demo.ts +++ b/src/demo-app/autocomplete/autocomplete-demo.ts @@ -1,7 +1,15 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + import {Component, ViewChild, ViewEncapsulation} from '@angular/core'; import {FormControl, NgModel} from '@angular/forms'; -import 'rxjs/add/operator/startWith'; -import 'rxjs/add/operator/map'; +import {startWith} from 'rxjs/operators/startWith'; +import {map} from 'rxjs/operators/map'; export interface State { code: string; @@ -18,7 +26,8 @@ export interface StateGroup { selector: 'autocomplete-demo', templateUrl: 'autocomplete-demo.html', styleUrls: ['autocomplete-demo.css'], - encapsulation: ViewEncapsulation.None + encapsulation: ViewEncapsulation.None, + preserveWhitespaces: false, }) export class AutocompleteDemo { stateCtrl: FormControl; @@ -92,9 +101,11 @@ export class AutocompleteDemo { this.tdStates = this.states; this.stateCtrl = new FormControl({code: 'CA', name: 'California'}); this.reactiveStates = this.stateCtrl.valueChanges - .startWith(this.stateCtrl.value) - .map(val => this.displayFn(val)) - .map(name => this.filterStates(name)); + .pipe( + startWith(this.stateCtrl.value), + map(val => this.displayFn(val)), + map(name => this.filterStates(name)) + ); this.filteredGroupedStates = this.groupedStates = this.states.reduce((groups, state) => { let group = groups.find(g => g.letter === state.name[0]); diff --git a/src/demo-app/baseline/baseline-demo.html b/src/demo-app/baseline/baseline-demo.html index fa30ca0d8aaf..8a695ce1c7f4 100644 --- a/src/demo-app/baseline/baseline-demo.html +++ b/src/demo-app/baseline/baseline-demo.html @@ -1,55 +1,61 @@ - - Basic Forms - + + Basic Forms + Text Before | - Checkbox Label + Checkbox Label | Text 1 | - Radio 1 + Radio 1 | Text 2 | - Radio 2 + Radio 2 | Text 3 | - Radio 3 + Radio 3 | Text 4 | - - - + + + | Text 5 | - - Option - + + + Option + This option is really really really long + + | Text 6 | - - - + + + | Text After - - + + - - Headers - + + Headers +

Text Before | - Checkbox Label + Checkbox Label | Text 1 | - Radio 1 + Radio 1 | Text 2 | - Radio 2 + Radio 2 | Text 3 | - Radio 3 + Radio 3 | Text 4 | - - - + + + | Text 5 | - - Option - + + + Option + This option is really really really long + + | Text 6 | - - - + + + | Text After

-
-
+ + diff --git a/src/demo-app/baseline/baseline-demo.ts b/src/demo-app/baseline/baseline-demo.ts index e5466fbba37c..d76fb57786ad 100644 --- a/src/demo-app/baseline/baseline-demo.ts +++ b/src/demo-app/baseline/baseline-demo.ts @@ -1,3 +1,11 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + import {Component} from '@angular/core'; diff --git a/src/demo-app/button-toggle/button-toggle-demo.html b/src/demo-app/button-toggle/button-toggle-demo.html index 0d158c21ecc3..fdb4c24fafa1 100644 --- a/src/demo-app/button-toggle/button-toggle-demo.html +++ b/src/demo-app/button-toggle/button-toggle-demo.html @@ -1,57 +1,57 @@

- Show Button Toggles Vertical + Show Button Toggles Vertical

- Disable Button Toggle Items + Disable Button Toggle Items

Exclusive Selection

- - format_align_left - format_align_center - format_align_right - format_align_justify - + + format_align_left + format_align_center + format_align_right + format_align_justify +

Disabled Group

- - - format_bold - - - format_italic - - - format_underline - - + + + format_bold + + + format_italic + + + format_underline + +

Multiple Selection

- - Flour - Eggs - Sugar - Milk - + + Flour + Eggs + Sugar + Milk +

Single Toggle

-Yes +Yes

Dynamic Exclusive Selection

- - + + {{pie}} - - + +

Your favorite type of pie is: {{favoritePie}}

diff --git a/src/demo-app/button-toggle/button-toggle-demo.ts b/src/demo-app/button-toggle/button-toggle-demo.ts index f7824df5d932..9ef9d884d51b 100644 --- a/src/demo-app/button-toggle/button-toggle-demo.ts +++ b/src/demo-app/button-toggle/button-toggle-demo.ts @@ -1,3 +1,11 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + import {Component} from '@angular/core'; @Component({ diff --git a/src/demo-app/button/button-demo.html b/src/demo-app/button/button-demo.html index 98a31e38b9a1..211d2c0c33b9 100644 --- a/src/demo-app/button/button-demo.html +++ b/src/demo-app/button/button-demo.html @@ -1,113 +1,114 @@
- - - + + - - Link - check - + Link + check + - - Link + + Link
- SEARCH - SEARCH - - check + SEARCH + SEARCH + + check - - check + + check
- - - + + +
- - - + + +
- - -
- - -

isDisabled: {{isDisabled}}, clickCounter: {{clickCounter}}

- - - - + + + +
- - - off - - + + off + + -
- SEARCH - + SEARCH +
- SEARCH - + SEARCH +
- - - - + + + +
- - - - + + + +
- - + +
diff --git a/src/demo-app/button/button-demo.ts b/src/demo-app/button/button-demo.ts index fa67d77380ca..c10962c132c9 100644 --- a/src/demo-app/button/button-demo.ts +++ b/src/demo-app/button/button-demo.ts @@ -1,3 +1,11 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + import {Component} from '@angular/core'; diff --git a/src/demo-app/card/card-demo.html b/src/demo-app/card/card-demo.html index 98cc322d20e3..221c295e0d77 100644 --- a/src/demo-app/card/card-demo.html +++ b/src/demo-app/card/card-demo.html @@ -1,61 +1,61 @@
- + Hello - + - - - Card with title - Subtitle - - - + + + Card with title + Subtitle + + + - - Subtitle - Card with title and footer - + + Subtitle + Card with title and footer +

This is supporting text.

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

-
- - - - - - - -
+ + + + + + + + + - - - Content Title - + + + Content Title +

Here is some content

-
- - - - -
+ + + + + + - - - - Header title - Header subtitle - - - + + + + Header title + Header subtitle + + +

Here is some content

-
-
+ + - - Easily customizable - - - - - + + Easily customizable + + + + +
diff --git a/src/demo-app/card/card-demo.ts b/src/demo-app/card/card-demo.ts index 590761f8a310..7a2bcbbf1282 100644 --- a/src/demo-app/card/card-demo.ts +++ b/src/demo-app/card/card-demo.ts @@ -1,3 +1,11 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + import {Component} from '@angular/core'; diff --git a/src/demo-app/checkbox/checkbox-demo.html b/src/demo-app/checkbox/checkbox-demo.html index 28760dac161c..0faf25ebd0fb 100644 --- a/src/demo-app/checkbox/checkbox-demo.html +++ b/src/demo-app/checkbox/checkbox-demo.html @@ -1,6 +1,6 @@ -

md-checkbox: Basic Example

+

mat-checkbox: Basic Example

-md-checkbox: Basic Example [align]="alignment"> Do you want to foobar the bazquux? - - {{printResult()}} + - {{printResult()}}
md-checkbox: Basic Example

Pseudo checkboxes

- - + + - - + + - - + +

Nested Checklist

- + diff --git a/src/demo-app/checkbox/checkbox-demo.ts b/src/demo-app/checkbox/checkbox-demo.ts index 9e507ea2506e..54a8e1104eb5 100644 --- a/src/demo-app/checkbox/checkbox-demo.ts +++ b/src/demo-app/checkbox/checkbox-demo.ts @@ -1,3 +1,11 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + import {Component} from '@angular/core'; @@ -9,7 +17,7 @@ export interface Task { @Component({ moduleId: module.id, - selector: 'md-checkbox-demo-nested-checklist', + selector: 'mat-checkbox-demo-nested-checklist', styles: [` li { margin-bottom: 4px; @@ -17,7 +25,7 @@ export interface Task { `], templateUrl: 'nested-checklist.html', }) -export class MdCheckboxDemoNestedChecklist { +export class MatCheckboxDemoNestedChecklist { tasks: Task[] = [ { name: 'Reminders', @@ -63,7 +71,7 @@ export class MdCheckboxDemoNestedChecklist { @Component({ moduleId: module.id, - selector: 'md-checkbox-demo', + selector: 'mat-checkbox-demo', templateUrl: 'checkbox-demo.html', styleUrls: ['checkbox-demo.css'], }) diff --git a/src/demo-app/checkbox/nested-checklist.html b/src/demo-app/checkbox/nested-checklist.html index 71c77d83f2e5..6a4ab3e617ab 100644 --- a/src/demo-app/checkbox/nested-checklist.html +++ b/src/demo-app/checkbox/nested-checklist.html @@ -1,17 +1,17 @@

Tasks

  • -

    {{task.name}}

    -
    +
    • - + {{subtask.name}} - +
  • diff --git a/src/demo-app/chips/chips-demo.html b/src/demo-app/chips/chips-demo.html index 860caedb0c2b..3ad365c44110 100644 --- a/src/demo-app/chips/chips-demo.html +++ b/src/demo-app/chips/chips-demo.html @@ -1,87 +1,105 @@
    - - Static Chips + + Static Chips - +

    Simple

    - - Chip 1 - Chip 2 - Chip 3 - + + Chip 1 + Chip 2 + Chip 3 +

    Unstyled

    - - Basic Chip 1 - Basic Chip 2 - Basic Chip 3 - + + Basic Chip 1 + Basic Chip 2 + Basic Chip 3 +

    Advanced

    - - Selected/Colored + + Selected/Colored - With Events - cancel - - + cancel + +
    {{message}}
    -
    -
    + + - - Dynamic Chips + + Dynamic Chips - +

    Form Field

    - You can easily put the the <md-chip-list> inside of an - <md-form-field>. + You can easily put the the <mat-chip-list> inside of an + <mat-form-field>.

    - - - - + Set tabIndex to {{tabIndex === 0 ? -1 : 0}} + + + + {{person.name}} - cancel - - - - + cancel + + + +

    - You can also put <md-form-field> outside of an md-chip-list. - With mdChipInput the input work with chip-list + You can also put <mat-form-field> outside of an mat-chip-list. + With matChipInput the input work with chip-list

    - - - {{person.name}} - cancel - - - - - + + + + {{person.name}} + cancel + + + + + +

    + Chips list without input +

    + + + + + + {{person.name}} + cancel + + + +

    The example above has overridden the [separatorKeys] input to allow for @@ -90,9 +108,9 @@

    Form Field

    Options

    - Selectable - Removable - Add on Blur + Selectable + Removable + Add on Blur

    Stacked Chips

    @@ -102,12 +120,34 @@

    Stacked Chips

    (focus) event to run custom code.

    - - + {{aColor.name}} - - -
    -
    + + + +

    NgModel with chip list

    + + + {{aColor.name}} + cancel + + + + The selected colors are {{color}}. + +

    NgModel with single selection chip list

    + + + {{aColor.name}} + cancel + + + + The selected color is {{selectedColor}}. + +
    diff --git a/src/demo-app/chips/chips-demo.scss b/src/demo-app/chips/chips-demo.scss index 5fdb999211a1..648001486a03 100644 --- a/src/demo-app/chips/chips-demo.scss +++ b/src/demo-app/chips/chips-demo.scss @@ -21,7 +21,11 @@ margin: auto 10px; } - md-chip-list input { + mat-chip-list input { width: 150px; } } + +.has-chip-list { + width: 100%; +} diff --git a/src/demo-app/chips/chips-demo.ts b/src/demo-app/chips/chips-demo.ts index d58217352256..5d94abbc1a86 100644 --- a/src/demo-app/chips/chips-demo.ts +++ b/src/demo-app/chips/chips-demo.ts @@ -1,7 +1,14 @@ -import {Component} from '@angular/core'; -import {MdChipInputEvent, ENTER} from '@angular/material'; +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ -const COMMA = 188; +import {ENTER, COMMA} from '@angular/cdk/keycodes'; +import {Component} from '@angular/core'; +import {MatChipInputEvent} from '@angular/material'; export interface Person { name: string; @@ -19,6 +26,7 @@ export interface DemoColor { styleUrls: ['chips-demo.css'] }) export class ChipsDemo { + tabIndex: number = 0; visible: boolean = true; color: string = ''; selectable: boolean = true; @@ -29,6 +37,8 @@ export class ChipsDemo { // Enter, comma, semi-colon separatorKeysCodes = [ENTER, COMMA, 186]; + selectedPeople = null; + people: Person[] = [ { name: 'Kara' }, { name: 'Jeremy' }, @@ -49,7 +59,7 @@ export class ChipsDemo { this.message = message; } - add(event: MdChipInputEvent): void { + add(event: MatChipInputEvent): void { let input = event.input; let value = event.value; @@ -72,7 +82,23 @@ export class ChipsDemo { } } + removeColor(color: DemoColor) { + let index = this.availableColors.indexOf(color); + + if (index >= 0) { + this.availableColors.splice(index, 1); + } + + index = this.selectedColors.indexOf(color.name); + + if (index >= 0) { + this.selectedColors.splice(index, 1); + } + } + toggleVisible(): void { this.visible = false; } + selectedColors: any[] = ['Primary', 'Warn']; + selectedColor = 'Accent'; } diff --git a/src/demo-app/dataset/colors.ts b/src/demo-app/dataset/colors.ts index 05a0516fa8b1..1b9dee7d060d 100644 --- a/src/demo-app/dataset/colors.ts +++ b/src/demo-app/dataset/colors.ts @@ -1,3 +1,11 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + export const COLORS = [ 'AliceBlue', 'AntiqueWhite', diff --git a/src/demo-app/dataset/names.ts b/src/demo-app/dataset/names.ts index 22e7e8c83432..02bd859d53bc 100644 --- a/src/demo-app/dataset/names.ts +++ b/src/demo-app/dataset/names.ts @@ -1,3 +1,11 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + export const NAMES: string[] = [ 'Abbott', 'Acevedo', diff --git a/src/demo-app/datepicker/datepicker-demo.html b/src/demo-app/datepicker/datepicker-demo.html index 3cdf0a807e16..b9c6c7dba22c 100644 --- a/src/demo-app/datepicker/datepicker-demo.html +++ b/src/demo-app/datepicker/datepicker-demo.html @@ -1,101 +1,135 @@

    Options

    - Use touch UI - Filter odd months and dates - Start in year view - Disable datepicker + Use touch UI + Filter odd months and dates + Start in year view + Disable datepicker + Disable input

    - - - - - - - - - - + + + + + + + + + +

    - - - - - + + + + +

    Result

    - - - + + - - - - "{{resultPickerModel.getError('mdDatepickerParse').text}}" is not a valid date! - - Too early! - Too late! - Date unavailable! - + + + "{{resultPickerModel.getError('matDatepickerParse').text}}" is not a valid date! + + Too early! + Too late! + Date unavailable! +

    Last input: {{lastDateInput}}

    Last change: {{lastDateChange}}


    - - + - +

    Input disabled datepicker

    - - - + + - - + + +

    + +

    Input disabled via FormControl

    +

    + + + + + + +

    Input disabled, datepicker popup enabled

    - - - + + - - + + +

    + +

    Datepicker with value property binding

    +

    + + + + +

    diff --git a/src/demo-app/datepicker/datepicker-demo.scss b/src/demo-app/datepicker/datepicker-demo.scss index c98cba687f8c..9672ffb589e1 100644 --- a/src/demo-app/datepicker/datepicker-demo.scss +++ b/src/demo-app/datepicker/datepicker-demo.scss @@ -1,3 +1,3 @@ -md-calendar { +mat-calendar { width: 300px; } diff --git a/src/demo-app/datepicker/datepicker-demo.ts b/src/demo-app/datepicker/datepicker-demo.ts index 3fc61b0b3144..c97074be3f07 100644 --- a/src/demo-app/datepicker/datepicker-demo.ts +++ b/src/demo-app/datepicker/datepicker-demo.ts @@ -1,5 +1,14 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + import {ChangeDetectionStrategy, Component} from '@angular/core'; -import {MdDatepickerInputEvent} from '@angular/material'; +import {FormControl} from '@angular/forms'; +import {MatDatepickerInputEvent} from '@angular/material/datepicker'; @Component({ @@ -24,6 +33,8 @@ export class DatepickerDemo { dateFilter = (date: Date) => date.getMonth() % 2 == 1 && date.getDate() % 2 == 0; - onDateInput = (e: MdDatepickerInputEvent) => this.lastDateInput = e.value; - onDateChange = (e: MdDatepickerInputEvent) => this.lastDateChange = e.value; + onDateInput = (e: MatDatepickerInputEvent) => this.lastDateInput = e.value; + onDateChange = (e: MatDatepickerInputEvent) => this.lastDateChange = e.value; + + dateCtrl = new FormControl(); } diff --git a/src/demo-app/demo-app-module.ts b/src/demo-app/demo-app-module.ts index b0d3e33cf61b..a60bcb9f2e4d 100644 --- a/src/demo-app/demo-app-module.ts +++ b/src/demo-app/demo-app-module.ts @@ -1,6 +1,14 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + import {ApplicationRef, NgModule} from '@angular/core'; import {BrowserModule} from '@angular/platform-browser'; -import {HttpModule} from '@angular/http'; +import {HttpClientModule} from '@angular/common/http'; import {RouterModule} from '@angular/router'; import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; import {ALL_ROUTES} from './demo-app/routes'; @@ -13,7 +21,7 @@ import {AccessibilityDemoModule} from './a11y/a11y-module'; imports: [ BrowserModule, BrowserAnimationsModule, - HttpModule, + HttpClientModule, DemoModule, AccessibilityDemoModule, RouterModule.forRoot(ALL_ROUTES), @@ -32,4 +40,3 @@ export class DemoAppModule { this._appRef.bootstrap(EntryApp); } } - diff --git a/src/demo-app/demo-app-types.d.ts b/src/demo-app/demo-app-types.d.ts index 1c32e621291f..c92ee049c207 100644 --- a/src/demo-app/demo-app-types.d.ts +++ b/src/demo-app/demo-app-types.d.ts @@ -1 +1,9 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + declare var module: { id: string }; diff --git a/src/demo-app/demo-app/demo-app.html b/src/demo-app/demo-app/demo-app.html index 7138842d1a48..ba6af65434be 100644 --- a/src/demo-app/demo-app/demo-app.html +++ b/src/demo-app/demo-app/demo-app.html @@ -1,8 +1,8 @@ - - - + + + - Baseline - - - + + +
    - -

    Angular Material Demos

    - - - +
    -
    +
    -
    + diff --git a/src/demo-app/demo-app/demo-app.ts b/src/demo-app/demo-app/demo-app.ts index 2938c6fe5400..579efc8966ee 100644 --- a/src/demo-app/demo-app/demo-app.ts +++ b/src/demo-app/demo-app/demo-app.ts @@ -1,10 +1,13 @@ -import { - Component, - ViewEncapsulation, - ElementRef, - Renderer2, -} from '@angular/core'; -import {OverlayContainer} from '@angular/material'; +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {OverlayContainer} from '@angular/cdk/overlay'; +import {Component, ElementRef, ViewEncapsulation} from '@angular/core'; /** * The entry app for demo site. Routes under `accessibility` will use AccessibilityDemo component, @@ -17,6 +20,7 @@ import {OverlayContainer} from '@angular/material'; selector: 'entry-app', template: '', encapsulation: ViewEncapsulation.None, + preserveWhitespaces: false, }) export class EntryApp {} @@ -41,49 +45,52 @@ export class Home {} templateUrl: 'demo-app.html', styleUrls: ['demo-app.css'], encapsulation: ViewEncapsulation.None, + preserveWhitespaces: false, }) export class DemoApp { dark = false; navItems = [ {name: 'Autocomplete', route: '/autocomplete'}, - {name: 'Button', route: '/button'}, {name: 'Button Toggle', route: '/button-toggle'}, + {name: 'Button', route: '/button'}, {name: 'Card', route: '/card'}, - {name: 'Chips', route: '/chips'}, {name: 'Checkbox', route: '/checkbox'}, + {name: 'Chips', route: '/chips'}, {name: 'Datepicker', route: '/datepicker'}, {name: 'Dialog', route: '/dialog'}, + {name: 'Drawer', route: '/drawer'}, {name: 'Expansion Panel', route: '/expansion'}, + {name: 'Focus Origin', route: '/focus-origin'}, {name: 'Gestures', route: '/gestures'}, {name: 'Grid List', route: '/grid-list'}, {name: 'Icon', route: '/icon'}, {name: 'Input', route: '/input'}, {name: 'List', route: '/list'}, - {name: 'Menu', route: '/menu'}, {name: 'Live Announcer', route: '/live-announcer'}, + {name: 'Menu', route: '/menu'}, {name: 'Overlay', route: '/overlay'}, + {name: 'Platform', route: '/platform'}, {name: 'Portal', route: '/portal'}, {name: 'Progress Bar', route: '/progress-bar'}, {name: 'Progress Spinner', route: '/progress-spinner'}, {name: 'Radio', route: '/radio'}, {name: 'Ripple', route: '/ripple'}, + {name: 'Screen Type', route: '/screen-type'}, {name: 'Select', route: '/select'}, {name: 'Sidenav', route: '/sidenav'}, - {name: 'Slider', route: '/slider'}, {name: 'Slide Toggle', route: '/slide-toggle'}, + {name: 'Slider', route: '/slider'}, {name: 'Snack Bar', route: '/snack-bar'}, + {name: 'Stepper', route: '/stepper'}, {name: 'Table', route: '/table'}, {name: 'Tabs', route: '/tabs'}, {name: 'Toolbar', route: '/toolbar'}, {name: 'Tooltip', route: '/tooltip'}, - {name: 'Platform', route: '/platform'}, - {name: 'Style', route: '/style'}, {name: 'Typography', route: '/typography'} ]; constructor( private _element: ElementRef, - private _renderer: Renderer2, private _overlayContainer: OverlayContainer) {} toggleFullscreen() { @@ -105,11 +112,11 @@ export class DemoApp { this.dark = !this.dark; if (this.dark) { - this._renderer.addClass(this._element.nativeElement, darkThemeClass); - this._overlayContainer.themeClass = darkThemeClass; + this._element.nativeElement.classList.add(darkThemeClass); + this._overlayContainer.getContainerElement().classList.add(darkThemeClass); } else { - this._renderer.removeClass(this._element.nativeElement, darkThemeClass); - this._overlayContainer.themeClass = ''; + this._element.nativeElement.classList.remove(darkThemeClass); + this._overlayContainer.getContainerElement().classList.remove(darkThemeClass); } } } diff --git a/src/demo-app/demo-app/demo-module.ts b/src/demo-app/demo-app/demo-module.ts index 7b5165b3fa35..1e0002b5dd83 100644 --- a/src/demo-app/demo-app/demo-module.ts +++ b/src/demo-app/demo-app/demo-module.ts @@ -1,51 +1,65 @@ -import {NgModule} from '@angular/core'; +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {FullscreenOverlayContainer, OverlayContainer} from '@angular/cdk/overlay'; import {CommonModule} from '@angular/common'; +import {NgModule} from '@angular/core'; import {FormsModule, ReactiveFormsModule} from '@angular/forms'; import {RouterModule} from '@angular/router'; -import {DemoApp, Home} from './demo-app'; -import {DEMO_APP_ROUTES} from './routes'; -import {ProgressBarDemo} from '../progress-bar/progress-bar-demo'; -import {ContentElementDialog, DialogDemo, IFrameDialog, JazzDialog} from '../dialog/dialog-demo'; -import {RippleDemo} from '../ripple/ripple-demo'; -import {IconDemo} from '../icon/icon-demo'; -import {GesturesDemo} from '../gestures/gestures-demo'; +import {AutocompleteDemo} from '../autocomplete/autocomplete-demo'; +import {BaselineDemo} from '../baseline/baseline-demo'; +import {ButtonToggleDemo} from '../button-toggle/button-toggle-demo'; +import {ButtonDemo} from '../button/button-demo'; import {CardDemo} from '../card/card-demo'; +import {CheckboxDemo, MatCheckboxDemoNestedChecklist} from '../checkbox/checkbox-demo'; import {ChipsDemo} from '../chips/chips-demo'; -import {RadioDemo} from '../radio/radio-demo'; -import {ButtonToggleDemo} from '../button-toggle/button-toggle-demo'; -import {ProgressSpinnerDemo} from '../progress-spinner/progress-spinner-demo'; -import {TooltipDemo} from '../tooltip/tooltip-demo'; -import {ListDemo} from '../list/list-demo'; -import {BaselineDemo} from '../baseline/baseline-demo'; +import {DatepickerDemo} from '../datepicker/datepicker-demo'; +import {DemoMaterialModule} from '../demo-material-module'; +import {ContentElementDialog, DialogDemo, IFrameDialog, JazzDialog} from '../dialog/dialog-demo'; +import {DrawerDemo} from '../drawer/drawer-demo'; +import {ExpansionDemo} from '../expansion/expansion-demo'; +import {FocusOriginDemo} from '../focus-origin/focus-origin-demo'; +import {GesturesDemo} from '../gestures/gestures-demo'; import {GridListDemo} from '../grid-list/grid-list-demo'; +import {IconDemo} from '../icon/icon-demo'; +import {InputDemo} from '../input/input-demo'; +import {ListDemo} from '../list/list-demo'; import {LiveAnnouncerDemo} from '../live-announcer/live-announcer-demo'; -import {OverlayDemo, RotiniPanel, SpagettiPanel} from '../overlay/overlay-demo'; -import {SlideToggleDemo} from '../slide-toggle/slide-toggle-demo'; -import {ToolbarDemo} from '../toolbar/toolbar-demo'; -import {ButtonDemo} from '../button/button-demo'; -import {CheckboxDemo, MdCheckboxDemoNestedChecklist} from '../checkbox/checkbox-demo'; +import {MenuDemo} from '../menu/menu-demo'; +import { + OverlayDemo, + RotiniPanel, + SpagettiPanel, + KeyboardTrackingPanel +} from '../overlay/overlay-demo'; +import {PlatformDemo} from '../platform/platform-demo'; +import {PortalDemo, ScienceJoke} from '../portal/portal-demo'; +import {ProgressBarDemo} from '../progress-bar/progress-bar-demo'; +import {ProgressSpinnerDemo} from '../progress-spinner/progress-spinner-demo'; +import {RadioDemo} from '../radio/radio-demo'; +import {RippleDemo} from '../ripple/ripple-demo'; import {SelectDemo} from '../select/select-demo'; -import {SliderDemo} from '../slider/slider-demo'; import {SidenavDemo} from '../sidenav/sidenav-demo'; +import {SlideToggleDemo} from '../slide-toggle/slide-toggle-demo'; +import {SliderDemo} from '../slider/slider-demo'; import {SnackBarDemo} from '../snack-bar/snack-bar-demo'; -import {PortalDemo, ScienceJoke} from '../portal/portal-demo'; -import {MenuDemo} from '../menu/menu-demo'; -import {FoggyTabContent, RainyTabContent, SunnyTabContent, TabsDemo} from '../tabs/tabs-demo'; -import {PlatformDemo} from '../platform/platform-demo'; -import {AutocompleteDemo} from '../autocomplete/autocomplete-demo'; -import {InputDemo} from '../input/input-demo'; -import {StyleDemo} from '../style/style-demo'; -import {TableDemo} from '../table/table-demo'; +import {StepperDemo} from '../stepper/stepper-demo'; import {PeopleDatabase} from '../table/people-database'; -import {DatepickerDemo} from '../datepicker/datepicker-demo'; -import {TypographyDemo} from '../typography/typography-demo'; -import {ExpansionDemo} from '../expansion/expansion-demo'; -import {DemoMaterialModule} from '../demo-material-module'; -import { - FullscreenOverlayContainer, - OverlayContainer, -} from '@angular/material'; +import {TableDemo} from '../table/table-demo'; +import {ScreenTypeDemo} from '../screen-type/screen-type-demo'; +import {LayoutModule} from '@angular/cdk/layout'; import {TableHeaderDemo} from '../table/table-header-demo'; +import {FoggyTabContent, RainyTabContent, SunnyTabContent, TabsDemo} from '../tabs/tabs-demo'; +import {ToolbarDemo} from '../toolbar/toolbar-demo'; +import {TooltipDemo} from '../tooltip/tooltip-demo'; +import {TypographyDemo} from '../typography/typography-demo'; +import {DemoApp, Home} from './demo-app'; +import {DEMO_APP_ROUTES} from './routes'; @NgModule({ imports: [ @@ -54,6 +68,7 @@ import {TableHeaderDemo} from '../table/table-header-demo'; ReactiveFormsModule, RouterModule.forChild(DEMO_APP_ROUTES), DemoMaterialModule, + LayoutModule, ], declarations: [ AutocompleteDemo, @@ -61,59 +76,64 @@ import {TableHeaderDemo} from '../table/table-header-demo'; ButtonDemo, ButtonToggleDemo, CardDemo, - ChipsDemo, CheckboxDemo, + ChipsDemo, + ContentElementDialog, DatepickerDemo, DemoApp, DialogDemo, + DrawerDemo, + ExpansionDemo, + FocusOriginDemo, + FoggyTabContent, GesturesDemo, GridListDemo, Home, IconDemo, + IFrameDialog, InputDemo, JazzDialog, - ContentElementDialog, - IFrameDialog, + KeyboardTrackingPanel, ListDemo, LiveAnnouncerDemo, - MdCheckboxDemoNestedChecklist, + MatCheckboxDemoNestedChecklist, MenuDemo, - SnackBarDemo, OverlayDemo, + PlatformDemo, PortalDemo, ProgressBarDemo, ProgressSpinnerDemo, RadioDemo, + RainyTabContent, RippleDemo, RotiniPanel, ScienceJoke, + ScreenTypeDemo, SelectDemo, SidenavDemo, SliderDemo, SlideToggleDemo, + SnackBarDemo, SpagettiPanel, - StyleDemo, + StepperDemo, + SunnyTabContent, + TableDemo, TableHeaderDemo, + TabsDemo, ToolbarDemo, TooltipDemo, - TableDemo, - TabsDemo, - SunnyTabContent, - RainyTabContent, - FoggyTabContent, - PlatformDemo, TypographyDemo, - ExpansionDemo, ], providers: [ {provide: OverlayContainer, useClass: FullscreenOverlayContainer}, PeopleDatabase ], entryComponents: [ - DemoApp, - JazzDialog, ContentElementDialog, + DemoApp, IFrameDialog, + JazzDialog, + KeyboardTrackingPanel, RotiniPanel, ScienceJoke, SpagettiPanel, diff --git a/src/demo-app/demo-app/routes.ts b/src/demo-app/demo-app/routes.ts index ac5d752b0f5f..275f0161918b 100644 --- a/src/demo-app/demo-app/routes.ts +++ b/src/demo-app/demo-app/routes.ts @@ -1,83 +1,98 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + import {Routes} from '@angular/router'; -import {Home} from './demo-app'; -import {ButtonDemo} from '../button/button-demo'; +import {AccessibilityDemo} from '../a11y/a11y'; +import {ACCESSIBILITY_DEMO_ROUTES} from '../a11y/routes'; +import {AutocompleteDemo} from '../autocomplete/autocomplete-demo'; import {BaselineDemo} from '../baseline/baseline-demo'; import {ButtonToggleDemo} from '../button-toggle/button-toggle-demo'; -import {TabsDemo} from '../tabs/tabs-demo'; -import {GridListDemo} from '../grid-list/grid-list-demo'; +import {ButtonDemo} from '../button/button-demo'; +import {CardDemo} from '../card/card-demo'; +import {CheckboxDemo} from '../checkbox/checkbox-demo'; +import {ChipsDemo} from '../chips/chips-demo'; +import {DatepickerDemo} from '../datepicker/datepicker-demo'; +import {DialogDemo} from '../dialog/dialog-demo'; +import {DrawerDemo} from '../drawer/drawer-demo'; +import {ExpansionDemo} from '../expansion/expansion-demo'; +import {FocusOriginDemo} from '../focus-origin/focus-origin-demo'; import {GesturesDemo} from '../gestures/gestures-demo'; -import {LiveAnnouncerDemo} from '../live-announcer/live-announcer-demo'; -import {ListDemo} from '../list/list-demo'; +import {GridListDemo} from '../grid-list/grid-list-demo'; import {IconDemo} from '../icon/icon-demo'; -import {ToolbarDemo} from '../toolbar/toolbar-demo'; -import {CheckboxDemo} from '../checkbox/checkbox-demo'; +import {InputDemo} from '../input/input-demo'; +import {ListDemo} from '../list/list-demo'; +import {LiveAnnouncerDemo} from '../live-announcer/live-announcer-demo'; +import {MenuDemo} from '../menu/menu-demo'; import {OverlayDemo} from '../overlay/overlay-demo'; +import {PlatformDemo} from '../platform/platform-demo'; import {PortalDemo} from '../portal/portal-demo'; import {ProgressBarDemo} from '../progress-bar/progress-bar-demo'; import {ProgressSpinnerDemo} from '../progress-spinner/progress-spinner-demo'; +import {RadioDemo} from '../radio/radio-demo'; +import {RippleDemo} from '../ripple/ripple-demo'; +import {ScreenTypeDemo} from '../screen-type/screen-type-demo'; import {SelectDemo} from '../select/select-demo'; import {SidenavDemo} from '../sidenav/sidenav-demo'; import {SlideToggleDemo} from '../slide-toggle/slide-toggle-demo'; import {SliderDemo} from '../slider/slider-demo'; -import {RadioDemo} from '../radio/radio-demo'; -import {CardDemo} from '../card/card-demo'; -import {ChipsDemo} from '../chips/chips-demo'; -import {MenuDemo} from '../menu/menu-demo'; -import {RippleDemo} from '../ripple/ripple-demo'; -import {DialogDemo} from '../dialog/dialog-demo'; -import {TooltipDemo} from '../tooltip/tooltip-demo'; import {SnackBarDemo} from '../snack-bar/snack-bar-demo'; -import {TABS_DEMO_ROUTES} from '../tabs/routes'; -import {PlatformDemo} from '../platform/platform-demo'; -import {AutocompleteDemo} from '../autocomplete/autocomplete-demo'; -import {InputDemo} from '../input/input-demo'; -import {StyleDemo} from '../style/style-demo'; -import {DatepickerDemo} from '../datepicker/datepicker-demo'; +import {StepperDemo} from '../stepper/stepper-demo'; import {TableDemo} from '../table/table-demo'; +import {TABS_DEMO_ROUTES} from '../tabs/routes'; +import {TabsDemo} from '../tabs/tabs-demo'; +import {ToolbarDemo} from '../toolbar/toolbar-demo'; +import {TooltipDemo} from '../tooltip/tooltip-demo'; import {TypographyDemo} from '../typography/typography-demo'; -import {ExpansionDemo} from '../expansion/expansion-demo'; -import {DemoApp} from './demo-app'; -import {AccessibilityDemo} from '../a11y/a11y'; -import {ACCESSIBILITY_DEMO_ROUTES} from '../a11y/routes'; +import {DemoApp, Home} from './demo-app'; export const DEMO_APP_ROUTES: Routes = [ {path: '', component: DemoApp, children: [ {path: '', component: Home}, {path: 'autocomplete', component: AutocompleteDemo}, + {path: 'baseline', component: BaselineDemo}, {path: 'button', component: ButtonDemo}, + {path: 'button-toggle', component: ButtonToggleDemo}, {path: 'card', component: CardDemo}, + {path: 'checkbox', component: CheckboxDemo}, {path: 'chips', component: ChipsDemo}, - {path: 'table', component: TableDemo}, {path: 'datepicker', component: DatepickerDemo}, + {path: 'dialog', component: DialogDemo}, + {path: 'drawer', component: DrawerDemo}, + {path: 'expansion', component: ExpansionDemo}, + {path: 'focus-origin', component: FocusOriginDemo}, + {path: 'gestures', component: GesturesDemo}, + {path: 'grid-list', component: GridListDemo}, + {path: 'icon', component: IconDemo}, + {path: 'input', component: InputDemo}, + {path: 'list', component: ListDemo}, + {path: 'live-announcer', component: LiveAnnouncerDemo}, + {path: 'menu', component: MenuDemo}, + {path: 'overlay', component: OverlayDemo}, + {path: 'platform', component: PlatformDemo}, + {path: 'portal', component: PortalDemo}, + {path: 'progress-bar', component: ProgressBarDemo}, + {path: 'progress-spinner', component: ProgressSpinnerDemo}, {path: 'radio', component: RadioDemo}, + {path: 'ripple', component: RippleDemo}, {path: 'select', component: SelectDemo}, {path: 'sidenav', component: SidenavDemo}, {path: 'slide-toggle', component: SlideToggleDemo}, {path: 'slider', component: SliderDemo}, - {path: 'progress-spinner', component: ProgressSpinnerDemo}, - {path: 'progress-bar', component: ProgressBarDemo}, - {path: 'portal', component: PortalDemo}, - {path: 'overlay', component: OverlayDemo}, - {path: 'checkbox', component: CheckboxDemo}, - {path: 'input', component: InputDemo}, - {path: 'toolbar', component: ToolbarDemo}, - {path: 'icon', component: IconDemo}, - {path: 'list', component: ListDemo}, - {path: 'menu', component: MenuDemo}, - {path: 'live-announcer', component: LiveAnnouncerDemo}, - {path: 'gestures', component: GesturesDemo}, - {path: 'grid-list', component: GridListDemo}, + {path: 'snack-bar', component: SnackBarDemo}, + {path: 'stepper', component: StepperDemo}, + {path: 'table', component: TableDemo}, {path: 'tabs', component: TabsDemo, children: TABS_DEMO_ROUTES}, - {path: 'button-toggle', component: ButtonToggleDemo}, - {path: 'baseline', component: BaselineDemo}, - {path: 'ripple', component: RippleDemo}, - {path: 'dialog', component: DialogDemo}, + {path: 'toolbar', component: ToolbarDemo}, {path: 'tooltip', component: TooltipDemo}, - {path: 'snack-bar', component: SnackBarDemo}, - {path: 'platform', component: PlatformDemo}, - {path: 'style', component: StyleDemo}, {path: 'typography', component: TypographyDemo}, {path: 'expansion', component: ExpansionDemo}, + {path: 'stepper', component: StepperDemo}, + {path: 'screen-type', component: ScreenTypeDemo}, ]} ]; diff --git a/src/demo-app/demo-material-module.ts b/src/demo-app/demo-material-module.ts index 74b1b5ce9e17..e649fe88457e 100644 --- a/src/demo-app/demo-material-module.ts +++ b/src/demo-app/demo-material-module.ts @@ -1,39 +1,47 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + import {NgModule} from '@angular/core'; import { - MdAutocompleteModule, - MdButtonModule, - MdButtonToggleModule, - MdCardModule, - MdCheckboxModule, - MdChipsModule, - MdDatepickerModule, - MdDialogModule, - MdExpansionModule, - MdFormFieldModule, - MdGridListModule, - MdIconModule, - MdInputModule, - MdListModule, - MdMenuModule, - MdNativeDateModule, - MdPaginatorModule, - MdProgressBarModule, - MdProgressSpinnerModule, - MdRadioModule, - MdRippleModule, - MdSelectModule, - MdSidenavModule, - MdSliderModule, - MdSlideToggleModule, - MdSnackBarModule, - MdSortModule, - MdTableModule, - MdTabsModule, - MdToolbarModule, - MdTooltipModule, - StyleModule + MatAutocompleteModule, + MatButtonModule, + MatButtonToggleModule, + MatCardModule, + MatCheckboxModule, + MatChipsModule, + MatDatepickerModule, + MatDialogModule, + MatExpansionModule, + MatFormFieldModule, + MatGridListModule, + MatIconModule, + MatInputModule, + MatListModule, + MatMenuModule, + MatPaginatorModule, + MatProgressBarModule, + MatProgressSpinnerModule, + MatRadioModule, + MatSelectModule, + MatSidenavModule, + MatSliderModule, + MatSlideToggleModule, + MatSnackBarModule, + MatSortModule, + MatTableModule, + MatTabsModule, + MatToolbarModule, + MatTooltipModule, + MatStepperModule, } from '@angular/material'; +import {MatNativeDateModule, MatRippleModule} from '@angular/material'; import {CdkTableModule} from '@angular/cdk/table'; +import {CdkAccordionModule} from '@angular/cdk/accordion'; import {A11yModule} from '@angular/cdk/a11y'; import {BidiModule} from '@angular/cdk/bidi'; import {OverlayModule} from '@angular/cdk/overlay'; @@ -46,41 +54,42 @@ import {PortalModule} from '@angular/cdk/portal'; */ @NgModule({ exports: [ - MdAutocompleteModule, - MdButtonModule, - MdButtonToggleModule, - MdCardModule, - MdCheckboxModule, - MdChipsModule, - MdTableModule, - MdDatepickerModule, - MdDialogModule, - MdExpansionModule, - MdFormFieldModule, - MdGridListModule, - MdIconModule, - MdInputModule, - MdListModule, - MdMenuModule, - MdPaginatorModule, - MdProgressBarModule, - MdProgressSpinnerModule, - MdRadioModule, - MdRippleModule, - MdSelectModule, - MdSidenavModule, - MdSlideToggleModule, - MdSliderModule, - MdSnackBarModule, - MdSortModule, - MdTabsModule, - MdToolbarModule, - MdTooltipModule, - MdNativeDateModule, + MatAutocompleteModule, + MatButtonModule, + MatButtonToggleModule, + MatCardModule, + MatCheckboxModule, + MatChipsModule, + MatTableModule, + MatDatepickerModule, + MatDialogModule, + MatExpansionModule, + MatFormFieldModule, + MatGridListModule, + MatIconModule, + MatInputModule, + MatListModule, + MatMenuModule, + MatPaginatorModule, + MatProgressBarModule, + MatProgressSpinnerModule, + MatRadioModule, + MatRippleModule, + MatSelectModule, + MatSidenavModule, + MatSlideToggleModule, + MatSliderModule, + MatSnackBarModule, + MatSortModule, + MatStepperModule, + MatTabsModule, + MatToolbarModule, + MatTooltipModule, + MatNativeDateModule, CdkTableModule, - StyleModule, A11yModule, BidiModule, + CdkAccordionModule, ObserversModule, OverlayModule, PlatformModule, diff --git a/src/demo-app/dialog/dialog-demo.html b/src/demo-app/dialog/dialog-demo.html index 37efd6e028f7..dcb68eb03872 100644 --- a/src/demo-app/dialog/dialog-demo.html +++ b/src/demo-app/dialog/dialog-demo.html @@ -1,79 +1,99 @@

    Dialog demo

    - - - - - + +

    Dialog dimensions

    - - - - - - + + + + + + +

    + +

    + + + + + + +

    + +

    + + + + + +

    Dialog position

    - - - - - - + + + + + +

    - - - - - - + + + + + +

    Dialog backdrop

    - - - + + +

    - Has backdrop + Has backdrop

    Other options

    - - Start - End - Center - + + + Start + End + Center + +

    - - - + + +

    - Disable close + Disable close

    -
    -
    + +

    Last afterClosed result: {{lastAfterClosedResult}}

    Last beforeClose result: {{lastBeforeCloseResult}}

    @@ -83,9 +103,9 @@

    Other options

    It's Jazz!

    - - - + + +

    {{ data.message }}

    diff --git a/src/demo-app/dialog/dialog-demo.ts b/src/demo-app/dialog/dialog-demo.ts index 339fcbffa018..8e4ba9e3bd7a 100644 --- a/src/demo-app/dialog/dialog-demo.ts +++ b/src/demo-app/dialog/dialog-demo.ts @@ -1,7 +1,16 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + import {Component, Inject, ViewChild, TemplateRef} from '@angular/core'; -import {DOCUMENT} from '@angular/platform-browser'; -import {MdDialog, MdDialogRef, MD_DIALOG_DATA} from '@angular/material'; +import {DOCUMENT} from '@angular/common'; +import {MatDialog, MatDialogConfig, MatDialogRef, MAT_DIALOG_DATA} from '@angular/material'; +const defaultDialogConfig = new MatDialogConfig(); @Component({ moduleId: module.id, @@ -10,7 +19,7 @@ import {MdDialog, MdDialogRef, MD_DIALOG_DATA} from '@angular/material'; styleUrls: ['dialog-demo.css'], }) export class DialogDemo { - dialogRef: MdDialogRef | null; + dialogRef: MatDialogRef | null; lastAfterClosedResult: string; lastBeforeCloseResult: string; actionsAlignment: string; @@ -21,6 +30,10 @@ export class DialogDemo { backdropClass: '', width: '', height: '', + minWidth: '', + minHeight: '', + maxWidth: defaultDialogConfig.maxWidth, + maxHeight: '', position: { top: '', bottom: '', @@ -35,7 +48,7 @@ export class DialogDemo { @ViewChild(TemplateRef) template: TemplateRef; - constructor(public dialog: MdDialog, @Inject(DOCUMENT) doc: any) { + constructor(public dialog: MatDialog, @Inject(DOCUMENT) doc: any) { // Possible useful example for the open and closeAll events. // Adding a class to the body if a dialog opens and // removing it after all open dialogs are closed @@ -78,9 +91,9 @@ export class DialogDemo { template: `

    It's Jazz!

    - - - + + +

    {{ data.message }}

    @@ -90,8 +103,8 @@ export class JazzDialog { private _dimesionToggle = false; constructor( - public dialogRef: MdDialogRef, - @Inject(MD_DIALOG_DATA) public data: any) { } + public dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: any) { } togglePosition(): void { this._dimesionToggle = !this._dimesionToggle; @@ -117,9 +130,9 @@ export class JazzDialog { }` ], template: ` -

    Neptune

    +

    Neptune

    - +

    @@ -131,32 +144,32 @@ export class JazzDialog { astronomical units (4.50×109 km). It is named after the Roman god of the sea and has the astronomical symbol ♆, a stylised version of the god Neptune's trident.

    -
    + - + + mat-dialog-close>Close Read more on Wikipedia - + ` }) export class ContentElementDialog { actionsAlignment: string; - constructor(public dialog: MdDialog) { } + constructor(public dialog: MatDialog) { } showInStackedDialog() { this.dialog.open(IFrameDialog); @@ -171,18 +184,18 @@ export class ContentElementDialog { }` ], template: ` -

    Neptune

    +

    Neptune

    - + - + - + - + mat-dialog-close>Close + ` }) export class IFrameDialog { diff --git a/src/demo-app/drawer/drawer-demo.html b/src/demo-app/drawer/drawer-demo.html new file mode 100644 index 000000000000..e83193ac1b6c --- /dev/null +++ b/src/demo-app/drawer/drawer-demo.html @@ -0,0 +1,87 @@ +

    Basic Use Case

    + + + + Start Side Drawer +
    + +
    + +
    + +
    Mode: {{start.mode}}
    +
    + +
    + + + End Side Drawer +
    + +
    + +
    +

    My Content

    + +
    +
    Drawer
    + + +
    + + + + +
    +
    + +

    Drawer Already Opened

    + + + + Drawer + + +
    + +
    +
    + +

    Dynamic Position Drawer

    + + + Start + End + +
    + + +
    +
    + +

    Drawer with focus attributes

    + + + + + Link + Focus region start + Link + Initially focused + Focus region end + Link + + + +
    +

    My Content

    + +
    +
    Drawer
    + +
    +
    +
    diff --git a/src/demo-app/drawer/drawer-demo.scss b/src/demo-app/drawer/drawer-demo.scss new file mode 100644 index 000000000000..1bf747c0615b --- /dev/null +++ b/src/demo-app/drawer/drawer-demo.scss @@ -0,0 +1,7 @@ +.demo-drawer-container { + border: 3px solid black; +} + +.demo-drawer-content { + padding: 15px; +} diff --git a/src/demo-app/drawer/drawer-demo.ts b/src/demo-app/drawer/drawer-demo.ts new file mode 100644 index 000000000000..4e944959c249 --- /dev/null +++ b/src/demo-app/drawer/drawer-demo.ts @@ -0,0 +1,22 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Component, ViewEncapsulation} from '@angular/core'; + + +@Component({ + moduleId: module.id, + selector: 'drawer-demo', + templateUrl: 'drawer-demo.html', + styleUrls: ['drawer-demo.css'], + encapsulation: ViewEncapsulation.None, + preserveWhitespaces: false, +}) +export class DrawerDemo { + invert = false; +} diff --git a/src/demo-app/expansion/expansion-demo.html b/src/demo-app/expansion/expansion-demo.html index 3b5976712c58..71f8aac725ae 100644 --- a/src/demo-app/expansion/expansion-demo.html +++ b/src/demo-app/expansion/expansion-demo.html @@ -1,53 +1,102 @@

    Single Expansion Panel

    - - + + + This is a panel description. Panel Title - + + This is the content text that makes sense here. - - - - - + Trigger a ripple + + + + + + +
    -

    Accordion

    + + + + + + + +
    + +

    matAccordion

    Accordion Options

    - Allow Multi Expansion - Hide Indicators - Disable Panel 2 - Show Panel 3 + Allow Multi Expansion + Hide Indicators + Disable Panel 2 + Show Panel 3

    Accordion Style

    - - Default - Flat - + + Default + Flat +

    Accordion Panel(s)

    - Panel 1 - Panel 2 + Panel 1 + Panel 2

    - - - Section 1 + + + Section 1

    This is the content text that makes sense here.

    -
    - - Section 2 + + + Section 2

    This is the content text that makes sense here.

    -
    - - Section 3 - Reveal Buttons Below - - - - - -
    + + + Section 3 + Reveal Buttons Below + + + + + + + diff --git a/src/demo-app/expansion/expansion-demo.scss b/src/demo-app/expansion/expansion-demo.scss index df5591b49721..b8ef2d203d35 100644 --- a/src/demo-app/expansion/expansion-demo.scss +++ b/src/demo-app/expansion/expansion-demo.scss @@ -1,4 +1,4 @@ -.md-expansion-demo-width { +.mat-expansion-demo-width { width: 600px; display: block; } diff --git a/src/demo-app/expansion/expansion-demo.ts b/src/demo-app/expansion/expansion-demo.ts index ac1076b020c0..c82169438bbc 100644 --- a/src/demo-app/expansion/expansion-demo.ts +++ b/src/demo-app/expansion/expansion-demo.ts @@ -1,3 +1,11 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + import {Component, ViewEncapsulation} from '@angular/core'; @Component({ @@ -6,6 +14,7 @@ import {Component, ViewEncapsulation} from '@angular/core'; styleUrls: ['expansion-demo.css'], templateUrl: 'expansion-demo.html', encapsulation: ViewEncapsulation.None, + preserveWhitespaces: false, }) export class ExpansionDemo { displayMode: string = 'default'; @@ -13,4 +22,6 @@ export class ExpansionDemo { hideToggle = false; disabled = false; showPanel3 = true; + expandedHeight: string; + collapsedHeight: string; } diff --git a/src/demo-app/style/style-demo.html b/src/demo-app/focus-origin/focus-origin-demo.html similarity index 100% rename from src/demo-app/style/style-demo.html rename to src/demo-app/focus-origin/focus-origin-demo.html diff --git a/src/demo-app/style/style-demo.scss b/src/demo-app/focus-origin/focus-origin-demo.scss similarity index 84% rename from src/demo-app/style/style-demo.scss rename to src/demo-app/focus-origin/focus-origin-demo.scss index ddfd64de6bb3..f972dda6e39a 100644 --- a/src/demo-app/style/style-demo.scss +++ b/src/demo-app/focus-origin/focus-origin-demo.scss @@ -1,3 +1,7 @@ +.demo-focusable { + border: 2px solid transparent; +} + .demo-focusable.cdk-focused { border: 2px solid red; } diff --git a/src/demo-app/focus-origin/focus-origin-demo.ts b/src/demo-app/focus-origin/focus-origin-demo.ts new file mode 100644 index 000000000000..fa82edc0b1e0 --- /dev/null +++ b/src/demo-app/focus-origin/focus-origin-demo.ts @@ -0,0 +1,21 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Component} from '@angular/core'; +import {FocusMonitor} from '@angular/cdk/a11y'; + + +@Component({ + moduleId: module.id, + selector: 'focus-origin-demo', + templateUrl: 'focus-origin-demo.html', + styleUrls: ['focus-origin-demo.css'], +}) +export class FocusOriginDemo { + constructor(public fom: FocusMonitor) {} +} diff --git a/src/demo-app/gestures/gestures-demo.ts b/src/demo-app/gestures/gestures-demo.ts index 693a1b612cbc..330c15a0510e 100644 --- a/src/demo-app/gestures/gestures-demo.ts +++ b/src/demo-app/gestures/gestures-demo.ts @@ -1,3 +1,11 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + import {Component} from '@angular/core'; @Component({ diff --git a/src/demo-app/grid-list/grid-list-demo.html b/src/demo-app/grid-list/grid-list-demo.html index 6a915d77f7d1..a9e869cfcc69 100644 --- a/src/demo-app/grid-list/grid-list-demo.html +++ b/src/demo-app/grid-list/grid-list-demo.html @@ -1,91 +1,91 @@
    - - Basic grid list - - - One - Two - Three - Four - - - + + Basic grid list + + + One + Two + Three + Four + + + - - Fixed-height grid list - - - + Fixed-height grid list + + + {{tile.text}} - - - - + + + +

    Change list cols:

    Change row height:

    - -
    -
    + + + - - Ratio-height grid list - - - + + Ratio-height grid list + + + {{tile.text}} - - - - + + + +

    Change ratio:

    -
    -
    + + - - Fit-height grid list - - + Fit-height grid list + + - + {{tile.text}} - - - - + + + +

    Change list height:

    Change gutter:

    -
    -
    + + - - Grid list with header - - - - - info_outline + + Grid list with header + + + + + info_outline {{dog.name}} - - - - - + + + + + - - Grid list with footer - - - + + Grid list with footer + + + - -

    {{dog.name}}

    - Human: {{dog.human}} - star_border -
    -
    -
    -
    -
    + +

    {{dog.name}}

    + Human: {{dog.human}} + star_border +
    + + + +
    diff --git a/src/demo-app/grid-list/grid-list-demo.ts b/src/demo-app/grid-list/grid-list-demo.ts index e52a0aa5e87e..803c48a82a4f 100644 --- a/src/demo-app/grid-list/grid-list-demo.ts +++ b/src/demo-app/grid-list/grid-list-demo.ts @@ -1,3 +1,11 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + import {Component} from '@angular/core'; diff --git a/src/demo-app/icon/icon-demo.html b/src/demo-app/icon/icon-demo.html index 338c4717ff9f..6067745806b6 100644 --- a/src/demo-app/icon/icon-demo.html +++ b/src/demo-app/icon/icon-demo.html @@ -4,32 +4,32 @@

    - By name registered with MdIconProvider: - - + By name registered with MatIconProvider: + +

    From icon set: - - - + + +

    Ligature from Material Icons font: - home + home

    Using a theme: - home - home - home + home + home + home

    Custom icon font and CSS: - +

    diff --git a/src/demo-app/icon/icon-demo.ts b/src/demo-app/icon/icon-demo.ts index 67faa801d2a9..3dc295156a81 100644 --- a/src/demo-app/icon/icon-demo.ts +++ b/src/demo-app/icon/icon-demo.ts @@ -1,17 +1,26 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + import {Component, ViewEncapsulation} from '@angular/core'; import {DomSanitizer} from '@angular/platform-browser'; -import {MdIconRegistry} from '@angular/material'; +import {MatIconRegistry} from '@angular/material'; @Component({ moduleId: module.id, - selector: 'md-icon-demo', + selector: 'mat-icon-demo', templateUrl: 'icon-demo.html', styleUrls: ['icon-demo.css'], encapsulation: ViewEncapsulation.None, + preserveWhitespaces: false, }) export class IconDemo { - constructor(mdIconRegistry: MdIconRegistry, sanitizer: DomSanitizer) { - mdIconRegistry + constructor(iconRegistry: MatIconRegistry, sanitizer: DomSanitizer) { + iconRegistry .addSvgIcon('thumb-up', sanitizer.bypassSecurityTrustResourceUrl('/icon/assets/thumbup-icon.svg')) .addSvgIconSetInNamespace('core', diff --git a/src/demo-app/index.html b/src/demo-app/index.html index 706848123e5d..de018522783e 100644 --- a/src/demo-app/index.html +++ b/src/demo-app/index.html @@ -11,7 +11,7 @@ - + @@ -22,7 +22,7 @@ - +