Merge 81c0b333e3c9709b59d2b9d30024ae064a48f6c7 on remote branch

Change-Id: I4e8e9bd05831f486e26391af43734212386be57e
diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml
new file mode 100644
index 0000000..f75004e
--- /dev/null
+++ b/.github/workflows/build_and_test.yml
@@ -0,0 +1,38 @@
+name: Build and Test
+
+on:
+  push:
+    paths-ignore:
+      - '**.md'
+  pull_request:
+    paths-ignore:
+      - '**.md'
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        # test against latest update of each major Java version, as well as specific updates of LTS versions:
+        java: [ 11, 13 ]
+    name: Build ktfmt on Java ${{ matrix.java }}
+    steps:
+    - uses: actions/checkout@v1
+      with:
+        submodules: recursive
+    - name: Set up JDK ${{ matrix.java }}
+      uses: actions/setup-java@v1
+      with:
+        java-version: ${{ matrix.java }}
+    - name: Build ktfmt
+      run: mvn -B install --file pom.xml
+    - name: Build ktfmt_idea_plugin
+      run: |
+        pushd ktfmt_idea_plugin
+        ./gradlew build
+        popd
+    - name: Build the Online Formatter
+      run: |
+        pushd online_formatter
+        ./gradlew build
+        popd
diff --git a/.github/workflows/publish_artifacts_on_release.yaml b/.github/workflows/publish_artifacts_on_release.yaml
new file mode 100644
index 0000000..85ecb96
--- /dev/null
+++ b/.github/workflows/publish_artifacts_on_release.yaml
@@ -0,0 +1,70 @@
+# When a new release is created, publish artifacts to Maven Central / JetBrains Marketplace / etc..
+#
+# About secrets used here:
+# 1. OSSRH_USERNAME, OSSRH_TOKEN: token obtained from https://oss.sonatype.org/
+# 2. OSSRH_GPG_SECRET_KEY - private key for signing Maven artifacts
+# 3. OSSRH_GPG_SECRET_KEY_PASSWORD - password for unlocking OSSRH_GPG_SECRET_KEY
+# 4. JETBRAINS_MARKETPLACE_TOKEN - token obtained from https://plugins.jetbrains.com/author/me/tokens
+
+name: Publish package to Maven Central and JetBrains Marketplace
+on:
+  release:
+    types: [created]
+jobs:
+  publish:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+      - name: Set up Maven Central Repository
+        uses: actions/setup-java@v1
+        with:
+          java-version: 11
+          server-id: ossrh
+          server-username: MAVEN_USERNAME
+          server-password: MAVEN_PASSWORD
+      - id: install-secret-key
+        name: Install gpg secret key
+        run: |
+          cat <(echo -e "${{ secrets.OSSRH_GPG_SECRET_KEY }}") | gpg --batch --import
+          gpg --list-secret-keys --keyid-format LONG
+      - id: publish-to-central
+        name: Publish to Maven Central Repository
+        env:
+          MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }}
+          MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }}
+        run: |
+          mvn \
+            --no-transfer-progress \
+            --batch-mode \
+            -Dgpg.passphrase=${{ secrets.OSSRH_GPG_SECRET_KEY_PASSWORD }} \
+            -Prelease clean deploy
+      - name: Publish IntelliJ plugin to JetBrains Marketplace
+        run: |
+          pushd ktfmt_idea_plugin
+          ./gradlew publishPlugin --stacktrace
+          popd
+        env:
+          JETBRAINS_MARKETPLACE_TOKEN: ${{ secrets.JETBRAINS_MARKETPLACE_TOKEN }}
+      - uses: actions/setup-node@v2
+      - name: Deploy website
+        run: |
+          KTFMT_TMP_DIR=$(mktemp -d)
+
+          pushd website
+          npm install .
+          KTFMT_WEBSITE_OUTPUT_DIR="$KTFMT_TMP_DIR" gulp build-website
+          popd
+
+          git fetch
+          git checkout gh-pages
+          git config user.email ktfmt@facebook.com
+          git config user.name ktfmt
+
+          rm -rf *
+          cp -R "$KTFMT_TMP_DIR"/* .
+
+          git add .
+          git commit -m "Publish website"
+          git push --force
+
+          rm -rf "$KTFMT_TMP_DIR"
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b11d424
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,25 @@
+.gradle/
+ktfmt_idea_plugin/build/
+.idea/
+*.ims
+*.iml
+
+.classpath
+.project
+.factorypath
+.settings/
+.apt_generated/
+
+target/
+
+bin/
+out/
+
+.DS_Store
+classes/
+
+online_formatter/build/
+online_formatter/out.yml
+
+website/node_modules
+release-ktfmt-website/
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..ded40a0
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "vendor/google-java-format"]
+	path = vendor/google-java-format
+	url = https://github.com/google/google-java-format.git
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..5294b93
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,55 @@
+//
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// README:
+// - Before expanding the visiblity of any of the targets in this file,
+//   please add a test that covers the new dependencies in the TEST_MAPPING file
+//   of this directory. We cannot guarantee that ExoPlayer dependents will not
+//   be accidentally broken if it is not covered by presubmit.
+// - The names in this files follow the pattern exoplayer-dep_name[-module_name]
+//   where dep_name identifies the client, and module_name disambiguates the
+//   module for cases where necessary (example: The same client depends
+//   separately on two exoplayer modules).
+
+package {
+    default_applicable_licenses: ["external_ktfmt_license"],
+}
+
+
+license {
+    name: "external_ktfmt_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
+    ],
+    license_text: [
+        "LICENSE",
+    ],
+}
+
+java_binary_host {
+    name: "ktfmt",
+    manifest: "MANIFEST.mf",
+    srcs: ["core/src/main/**/*.kt"],
+    static_libs: [
+        "kotlin-stdlib-jdk7",
+        "kotlin-compiler-embeddable",
+        "google_java_format",
+        "guava",
+        "trove-prebuilt",
+        "jna-prebuilt",
+    ],
+}
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..4bd525a
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,76 @@
+# Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as
+contributors and maintainers pledge to make participation in our project and
+our community a harassment-free experience for everyone, regardless of age, body
+size, disability, ethnicity, sex characteristics, gender identity and expression,
+level of experience, education, socio-economic status, nationality, personal
+appearance, race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment
+include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or
+advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic
+address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable
+behavior and are expected to take appropriate and fair corrective action in
+response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or
+reject comments, commits, code, wiki edits, issues, and other contributions
+that are not aligned to this Code of Conduct, or to ban temporarily or
+permanently any contributor for other behaviors that they deem inappropriate,
+threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies within all project spaces, and it also applies when
+an individual is representing the project or its community in public spaces.
+Examples of representing a project or community include using an official
+project e-mail address, posting via an official social media account, or acting
+as an appointed representative at an online or offline event. Representation of
+a project may be further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported by contacting the project team at <opensource-conduct@fb.com>. All
+complaints will be reviewed and investigated and will result in a response that
+is deemed necessary and appropriate to the circumstances. The project team is
+obligated to maintain confidentiality with regard to the reporter of an incident.
+Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good
+faith may face temporary or permanent repercussions as determined by other
+members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
+available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
+
+[homepage]: https://www.contributor-covenant.org
+
+For answers to common questions about this code of conduct, see
+https://www.contributor-covenant.org/faq
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..75522ca
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,33 @@
+# Contributing to ktfmt
+We want to make contributing to this project as easy and transparent as
+possible.
+
+**If you plan a significant change, please first start a discussion as a GitHub Issue.**
+
+## Pull Requests
+We actively welcome your pull requests.
+
+1. Fork the repo and create your branch from `main`.
+2. If you've added code that should be tested, add tests.
+3. If you've changed APIs, update the documentation.
+4. Ensure the test suite passes.
+5. Make sure your code lints.
+6. If you haven't already, complete the Contributor License Agreement ("CLA").
+
+## Contributor License Agreement ("CLA")
+In order to accept your pull request, we need you to submit a CLA. You only need
+to do this once to work on any of Facebook's open source projects.
+
+Complete your CLA here: <https://code.facebook.com/cla>
+
+## Issues
+We use GitHub issues to track public bugs. Please ensure your description is
+clear and has sufficient instructions to be able to reproduce the issue.
+
+Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe
+disclosure of security bugs. In those cases, please go through the process
+outlined on that page and do not file a public issue.
+
+## License
+By contributing to ktfmt, you agree that your contributions will be licensed
+under the LICENSE file in the root directory of this source tree.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..872bf6a
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,206 @@
+Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+   
+=============================================================================
+
+This product bundles parts of google-java-format 1.7, which is available under an
+Apache license.
diff --git a/MANIFEST.mf b/MANIFEST.mf
new file mode 100644
index 0000000..a6f54fb
--- /dev/null
+++ b/MANIFEST.mf
@@ -0,0 +1,2 @@
+Manifest-Version: 1.0
+Main-Class: com.facebook.ktfmt.cli.Main
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..23da49e
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,13 @@
+name: "ktfmt"
+description:
+    "A formatter for Kotlin code."
+
+third_party {
+  url {
+    type: GIT
+    value: "https://github.com/facebookincubator/ktfmt.git"
+  }
+  version: "v0.39"
+  last_upgrade_date { year: 2022 month: 7 day: 25 }
+  license_type: NOTICE
+}
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_APACHE2
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..92e2205
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,2 @@
+jdemeulenaere@google.com
+nicomazz@google.com
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..71b7c8b
--- /dev/null
+++ b/README.md
@@ -0,0 +1,124 @@
+[![](https://github.com/facebookincubator/ktfmt/workflows/build/badge.svg)](https://github.com/facebookincubator/ktfmt/actions?query=workflow%3Abuild)
+
+# ktfmt
+
+`ktfmt` is a program that pretty-prints (formats) Kotlin code, based on [google-java-format](https://github.com/google/google-java-format).
+
+**Note** that `ktfmt` still has some rough edges which we're constantly working on fixing.
+
+The minimum supported runtime version is JDK 11, released September 2018.
+
+## Demo
+
+|Before Formatting| Formatted by `ktfmt`|
+| ---- | ---- |
+| ![Original](docs/images/before.png) | ![ktfmt](docs/images/ktfmt.png) |
+
+For comparison, the same code formatted by [`ktlint`](https://github.com/pinterest/ktlint) and IntelliJ:
+
+| Formatted by `ktlint`|Formatted by IntelliJ|
+| ------ | --------|
+| ![ktlint](docs/images/ktlint.png) | ![IntelliJ](docs/images/intellij.png) |
+
+## Using the formatter
+
+### IntelliJ, Android Studio, and other JetBrains IDEs
+
+A
+[ktfmt IntelliJ plugin](https://plugins.jetbrains.com/plugin/14912-ktfmt)
+is available from the plugin repository. To install it, go to your IDE's
+settings and select the `Plugins` category. Click the `Marketplace` tab, search
+for the `ktfmt` plugin, and click the `Install` button.
+
+The plugin will be disabled by default. To enable it in the current project, go
+to `File→Settings...→ktfmt Settings` (or `IntelliJ
+IDEA→Preferences...→Editor→ktfmt Settings` on macOS) and
+check the `Enable ktfmt` checkbox. (A notification will be
+presented when you first open a project offering to do this for you.)
+
+To enable it by default in new projects, use `File→New Project Settings→Preferences for new Projects→Editor→ktfmt Settings`.
+
+When enabled, it will replace the normal `Reformat Code` action, which can be
+triggered from the `Code` menu or with the Ctrl-Alt-L (by default) keyboard
+shortcut.
+
+To configure IntelliJ to approximate ktfmt's formatting rules during code editing,
+you can edit your project's
+[`.editorconfig` file](https://www.jetbrains.com/help/idea/configuring-code-style.html#editorconfig)
+to include the Kotlin section from one of the files inside
+[`docs/editorconfig`](docs/editorconfig).
+Not all of ktfmt's rules can be represented as IntelliJ editor settings, so you will still
+need to run ktfmt. Alternately, that file can be used as a reference to manually change
+the project's code style settings.
+
+### from the command-line
+
+[Download the formatter](https://github.com/facebookincubator/ktfmt/releases)
+and run it with:
+
+```
+java -jar /path/to/ktfmt-<VERSION>-jar-with-dependencies.jar [--dropbox-style] [files...]
+```
+
+`--dropbox-style` makes `ktfmt` use a block indent of 4 spaces instead of 2. See below for details.
+
+***Note:*** *There is no configurability as to the formatter's algorithm for
+formatting (apart from `--dropbox-style`). This is a deliberate design decision to unify our code
+formatting on a single format.*
+
+### using Gradle
+
+A [Gradle plugin (ktfmt-gradle)](https://github.com/cortinico/ktfmt-gradle) is available on the Gradle Plugin Portal. To set it up, just follow the instructions in the [How-to-use section](https://github.com/cortinico/ktfmt-gradle#how-to-use-).
+
+Alternatively, you can use [Spotless](https://github.com/diffplug/spotless) with the [ktfmt Gradle plugin](https://github.com/diffplug/spotless/tree/main/plugin-gradle#ktfmt).
+
+### using Maven
+
+Consider using [Spotless](https://github.com/diffplug/spotless) with the [ktfmt Maven plugin](https://github.com/diffplug/spotless/tree/main/plugin-maven#ktfmt).
+
+## FAQ
+
+### `ktfmt` vs `ktlint` vs IntelliJ
+
+`ktfmt` uses google-java-format's underlying engine, and as such, many items on [google-java-format's FAQ](https://github.com/google/google-java-format/wiki/FAQ) apply to `ktfmt` as well.
+
+In particular,
+1. `ktfmt` ignores most existing formatting. It respects existing newlines in some places, but in general, its output is deterministic and is independent of the input code.
+2. `ktfmt` exposes no configuration options that govern formatting behavior. See https://github.com/google/google-java-format/wiki/FAQ#i-just-need-to-configure-it-a-bit-differently-how for the rationale.
+
+These two properties make `ktfmt` a good fit in large Kotlin code bases, where consistency is very important.
+
+We created `ktfmt` because `ktlint` and IntelliJ sometime fail to produce nice-looking code that fits in 100 columns, as can be seen in the [Demo](README.md#Demo) section.
+
+### `ktfmt` uses a 2-space indent; why not 4? any way to change that?
+
+Two reasons -
+1. Many of our projects use a mixture of Kotlin and Java, and we found the back-and-forth in styles to be distracting.
+2. From a pragmatic standpoint, the formatting engine behind google-java-format uses more whitespace and newlines than other formatters. Using an indentation of 4 spaces quickly reaches the maximal column width.
+
+However, we do offer an escape-hatch for projects that absolutely cannot make the move to `ktfmt` because of 2-space: the `--dropbox-style` flag changes block indents to 4-space.
+
+## Developer's Guide
+
+### Setup
+
+* Open `pom.xml` in IntelliJ. Choose "Open as a Project"
+* The IntelliJ project will unfortunately be broken on import. To fix,
+    * Turn off ErrorProne by removing the compiler parameters in IntelliJ at the bottom of "Settings -> Build, Execution, Deployment -> Compiler -> Java Compiler" (see https://github.com/google/google-java-format/issues/417)
+
+### Development
+
+* Currently, we mainly develop by adding tests to `FormatterTest.kt`.
+
+### Building on the Command Line
+
+* Run `mvn install`
+* Run `java -jar core/target/ktfmt-<VERSION>-jar-with-dependencies.jar`
+
+### Releasing
+
+See [RELEASING.md](RELEASING.md).
+
+## License
+
+Apache License 2.0
diff --git a/RELEASING.md b/RELEASING.md
new file mode 100644
index 0000000..64d5745
--- /dev/null
+++ b/RELEASING.md
@@ -0,0 +1,10 @@
+# Releasing a New `ktfmt` Version
+
+1. Run `./bump_versions.sh $OLD_VERSION $NEW_VERSION` and commit the changes.
+2. Create a new Release in GitHub. A GitHub Action is automatically triggered and builds and publishes the artifacts to
+    1. Maven
+    2. IntelliJ Plugin marketplace
+3. TODO: also automate website generation (https://facebookincubator.github.io/ktfmt/) and the AWS Lambda that powers it. For now, you must clone the repo locally, and manually run some steps.
+    1. pushd online_formatter; ./build_and_deploy.sh; popd
+        1. Credentials should be configured using https://docs.aws.amazon.com/cli/latest/topic/config-vars.html#credentials
+    2. Follow instructions in website/README.md
diff --git a/core/pom.xml b/core/pom.xml
new file mode 100644
index 0000000..c7e8df4
--- /dev/null
+++ b/core/pom.xml
@@ -0,0 +1,275 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>ktfmt</artifactId>
+
+    <name>Ktfmt</name>
+    <description>A program that reformats Kotlin source code to comply with the common community standard for Kotlin code conventions.</description>
+
+    <parent>
+        <groupId>com.facebook</groupId>
+        <artifactId>ktfmt-parent</artifactId>
+        <version>0.39</version>
+    </parent>
+
+    <properties>
+        <dokka.version>0.10.1</dokka.version>
+        <kotlin.version>1.6.10</kotlin.version>
+        <kotlin.compiler.incremental>true</kotlin.compiler.incremental>
+        <kotlin.compiler.jvmTarget>1.8</kotlin.compiler.jvmTarget>
+        <main.class>com.facebook.ktfmt.cli.Main</main.class>
+    </properties>
+
+    <pluginRepositories>
+        <pluginRepository>
+            <id>jcenter</id>
+            <name>JCenter</name>
+            <url>https://jcenter.bintray.com/</url>
+        </pluginRepository>
+    </pluginRepositories>
+
+    <build>
+        <sourceDirectory>${project.basedir}/src/main/java</sourceDirectory>
+        <testSourceDirectory>${project.basedir}/src/test/java</testSourceDirectory>
+        <plugins>
+            <plugin>
+                <groupId>org.jetbrains.kotlin</groupId>
+                <artifactId>kotlin-maven-plugin</artifactId>
+                <version>${kotlin.version}</version>
+
+                <executions>
+                    <execution>
+                        <id>compile</id>
+                        <goals>
+                            <goal>compile</goal>
+                        </goals>
+                        <configuration>
+                            <sourceDirs>
+                                <sourceDir>${project.basedir}/src/main/java</sourceDir>
+                            </sourceDirs>
+                        </configuration>
+                    </execution>
+
+                    <execution>
+                        <id>test-compile</id>
+                        <goals>
+                            <goal>test-compile</goal>
+                        </goals>
+                        <configuration>
+                            <sourceDirs>
+                                <sourceDir>${project.basedir}/src/test/java</sourceDir>
+                            </sourceDirs>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.5.1</version>
+                <configuration>
+                    <source>1.8</source>
+                    <target>1.8</target>
+                </configuration>
+                <executions>
+                    <!-- Replacing default-compile as it is treated specially by maven -->
+                    <execution>
+                        <id>default-compile</id>
+                        <phase>none</phase>
+                    </execution>
+                    <!-- Replacing default-testCompile as it is treated specially by maven -->
+                    <execution>
+                        <id>default-testCompile</id>
+                        <phase>none</phase>
+                    </execution>
+                    <execution>
+                        <id>java-compile</id>
+                        <phase>compile</phase>
+                        <goals>
+                            <goal>compile</goal>
+                        </goals>
+                    </execution>
+                    <execution>
+                        <id>java-test-compile</id>
+                        <phase>test-compile</phase>
+                        <goals>
+                            <goal>testCompile</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <version>2.6</version>
+                <configuration>
+                    <archive>
+                        <manifest>
+                            <addClasspath>true</addClasspath>
+                            <mainClass>${main.class}</mainClass>
+                        </manifest>
+                    </archive>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <version>2.6</version>
+                <executions>
+                    <execution>
+                        <id>make-assembly</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>single</goal>
+                        </goals>
+                        <configuration>
+                            <archive>
+                                <manifest>
+                                    <mainClass>${main.class}</mainClass>
+                                </manifest>
+                            </archive>
+                            <descriptorRefs>
+                                <descriptorRef>jar-with-dependencies</descriptorRef>
+                            </descriptorRefs>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>29.0-jre</version>
+        </dependency>
+        <dependency>
+            <groupId>org.jetbrains.kotlin</groupId>
+            <artifactId>kotlin-stdlib-jdk7</artifactId>
+            <version>${kotlin.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.jetbrains.kotlin</groupId>
+            <artifactId>kotlin-test</artifactId>
+            <version>${kotlin.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.jetbrains.kotlin</groupId>
+            <artifactId>kotlin-compiler-embeddable</artifactId>
+            <version>${kotlin.version}</version>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/net.java.dev.jna/jna -->
+        <dependency>
+            <groupId>net.java.dev.jna</groupId>
+            <artifactId>jna</artifactId>
+            <version>4.2.2</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.googlejavaformat</groupId>
+            <artifactId>google-java-format</artifactId>
+            <version>1.8</version>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.13.1</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.google.truth</groupId>
+            <artifactId>truth</artifactId>
+            <version>1.0</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <profiles>
+        <profile>
+            <id>release</id>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.sonatype.plugins</groupId>
+                        <artifactId>nexus-staging-maven-plugin</artifactId>
+                        <version>1.6.7</version>
+                        <extensions>true</extensions>
+                        <configuration>
+                            <serverId>ossrh</serverId>
+                            <nexusUrl>https://oss.sonatype.org/</nexusUrl>
+                            <autoReleaseAfterClose>true</autoReleaseAfterClose>
+                        </configuration>
+                    </plugin>
+
+                    <!-- Source JAR -->
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-source-plugin</artifactId>
+                        <version>2.2.1</version>
+                        <executions>
+                            <execution>
+                                <id>attach-sources</id>
+                                <goals>
+                                    <goal>jar-no-fork</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+
+                    <!-- Javadoc JAR -->
+                    <!-- Dokka, Kotlin's javadoc, doesn't work on JDK 10+, so we're creating an empty javadoc jar instead. -->
+                    <!-- (https://github.com/Kotlin/dokka/issues/294) -->
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-jar-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <phase>package</phase>
+                                <goals>
+                                    <goal>jar</goal>
+                                </goals>
+                                <configuration>
+                                    <classifier>javadoc</classifier>
+                                    <classesDirectory>${project.basedir}/non/existing/dir</classesDirectory>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+
+                    <!-- Sign artifacts -->
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-gpg-plugin</artifactId>
+                        <version>1.6</version>
+                        <executions>
+                            <execution>
+                                <id>sign-artifacts</id>
+                                <phase>verify</phase>
+                                <goals>
+                                    <goal>sign</goal>
+                                </goals>
+                                <configuration>
+                                    <!-- Honor -Dgpg.passphrase=... on the command line. -->
+                                    <gpgArguments>
+                                        <arg>--pinentry-mode</arg>
+                                        <arg>loopback</arg>
+                                    </gpgArguments>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+
+    <distributionManagement>
+        <snapshotRepository>
+            <id>ossrh</id>
+            <url>https://oss.sonatype.org/content/repositories/snapshots</url>
+        </snapshotRepository>
+    </distributionManagement>
+
+</project>
diff --git a/core/src/main/java/com/facebook/ktfmt/cli/Main.kt b/core/src/main/java/com/facebook/ktfmt/cli/Main.kt
new file mode 100644
index 0000000..4c965bb
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/cli/Main.kt
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.facebook.ktfmt.cli
+
+import com.facebook.ktfmt.format.Formatter
+import com.facebook.ktfmt.format.ParseError
+import com.google.googlejavaformat.FormattingError
+import java.io.BufferedReader
+import java.io.File
+import java.io.IOException
+import java.io.InputStream
+import java.io.InputStreamReader
+import java.io.PrintStream
+import java.util.concurrent.atomic.AtomicInteger
+import kotlin.system.exitProcess
+
+class Main(
+    private val input: InputStream,
+    private val out: PrintStream,
+    private val err: PrintStream,
+    args: Array<String>
+) {
+  companion object {
+    @JvmStatic
+    fun main(args: Array<String>) {
+      exitProcess(Main(System.`in`, System.out, System.err, args).run())
+    }
+
+    /**
+     * expandArgsToFileNames expands 'args' to a list of .kt files to format.
+     *
+     * Most commonly, 'args' is either a list of .kt files, or a name of a directory whose contents
+     * the user wants to format.
+     */
+    fun expandArgsToFileNames(args: List<String>): List<File> {
+      if (args.size == 1 && File(args[0]).isFile) {
+        return listOf(File(args[0]))
+      }
+      val result = mutableListOf<File>()
+      for (arg in args) {
+        if (arg == "-") {
+          error(
+              "Error: '-', which causes ktfmt to read from stdin, should not be mixed with file name")
+        }
+        result.addAll(
+            File(arg).walkTopDown().filter {
+              it.isFile && (it.extension == "kt" || it.extension == "kts")
+            })
+      }
+      return result
+    }
+  }
+
+  private val parsedArgs: ParsedArgs = ParsedArgs.processArgs(err, args)
+
+  fun run(): Int {
+    if (parsedArgs.fileNames.isEmpty()) {
+      err.println(
+          "Usage: ktfmt [--dropbox-style | --google-style | --kotlinlang-style] [--dry-run] [--set-exit-if-changed] File1.kt File2.kt ...")
+      err.println("Or: ktfmt @file")
+      return 1
+    }
+
+    if (parsedArgs.fileNames.size == 1 && parsedArgs.fileNames[0] == "-") {
+      return try {
+        val alreadyFormatted = format(null)
+        if (!alreadyFormatted && parsedArgs.setExitIfChanged) 1 else 0
+      } catch (e: Exception) {
+        1
+      }
+    }
+
+    val files: List<File>
+    try {
+      files = expandArgsToFileNames(parsedArgs.fileNames)
+    } catch (e: java.lang.IllegalStateException) {
+      err.println(e.message)
+      return 1
+    }
+
+    if (files.isEmpty()) {
+      err.println("Error: no .kt files found")
+      return 1
+    }
+
+    val retval = AtomicInteger(0)
+    files.parallelStream().forEach {
+      try {
+        if (!format(it) && parsedArgs.setExitIfChanged) {
+          retval.set(1)
+        }
+      } catch (e: Exception) {
+        retval.set(1)
+      }
+    }
+    return retval.get()
+  }
+
+  /**
+   * Handles the logic for formatting and flags.
+   *
+   * If dry run mode is active, this simply prints the name of the [source] (file path or `<stdin>`)
+   * to [out]. Otherwise, this will run the appropriate formatting as normal.
+   *
+   * @param file The file to format. If null, the code is read from <stdin>.
+   * @return true iff input is valid and already formatted.
+   */
+  private fun format(file: File?): Boolean {
+    val fileName = file?.toString() ?: "<stdin>"
+    try {
+      val code = file?.readText() ?: BufferedReader(InputStreamReader(input)).readText()
+      val formattedCode = Formatter.format(parsedArgs.formattingOptions, code)
+      val alreadyFormatted = code == formattedCode
+
+      // stdin
+      if (file == null) {
+        if (parsedArgs.dryRun) {
+          if (!alreadyFormatted) {
+            out.println("<stdin>")
+          }
+        } else {
+          out.print(formattedCode)
+        }
+        return alreadyFormatted
+      }
+
+      if (parsedArgs.dryRun) {
+        if (!alreadyFormatted) {
+          out.println(fileName)
+        }
+      } else {
+        // TODO(T111284144): Add tests
+        if (!alreadyFormatted) {
+          file.writeText(formattedCode)
+        }
+        err.println("Done formatting $fileName")
+      }
+
+      return alreadyFormatted
+    } catch (e: IOException) {
+      err.println("Error formatting $fileName: ${e.message}; skipping.")
+      throw e
+    } catch (e: ParseError) {
+      handleParseError(fileName, e)
+      throw e
+    } catch (e: FormattingError) {
+      for (diagnostic in e.diagnostics()) {
+        System.err.println("$fileName:$diagnostic")
+      }
+      e.printStackTrace(err)
+      throw e
+    }
+  }
+
+  private fun handleParseError(fileName: String, e: ParseError) {
+    err.println("$fileName:${e.message}")
+  }
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/cli/ParsedArgs.kt b/core/src/main/java/com/facebook/ktfmt/cli/ParsedArgs.kt
new file mode 100644
index 0000000..503cb89
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/cli/ParsedArgs.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.facebook.ktfmt.cli
+
+import com.facebook.ktfmt.format.Formatter
+import com.facebook.ktfmt.format.FormattingOptions
+import java.io.File
+import java.io.PrintStream
+
+/** ParsedArgs holds the arguments passed to ktfmt on the command-line, after parsing. */
+data class ParsedArgs(
+    val fileNames: List<String>,
+    val formattingOptions: FormattingOptions,
+    /**
+     * Run the formatter without writing changes to any files. This will print the path of any files
+     * that would be changed if the formatter is run normally.
+     */
+    val dryRun: Boolean,
+
+    /** Return exit code 1 if any formatting changes are detected. */
+    val setExitIfChanged: Boolean,
+) {
+  companion object {
+
+    fun processArgs(err: PrintStream, args: Array<String>): ParsedArgs {
+      if (args.size == 1 && args[0].startsWith("@")) {
+        return parseOptions(err, File(args[0].substring(1)).readLines().toTypedArray())
+      } else {
+        return parseOptions(err, args)
+      }
+    }
+
+    /** parseOptions parses command-line arguments passed to ktfmt. */
+    fun parseOptions(err: PrintStream, args: Array<String>): ParsedArgs {
+      val fileNames = mutableListOf<String>()
+      var formattingOptions = FormattingOptions()
+      var dryRun = false
+      var setExitIfChanged = false
+
+      for (arg in args) {
+        when {
+          arg == "--dropbox-style" -> formattingOptions = Formatter.DROPBOX_FORMAT
+          arg == "--google-style" -> formattingOptions = Formatter.GOOGLE_FORMAT
+          arg == "--kotlinlang-style" -> formattingOptions = Formatter.KOTLINLANG_FORMAT
+          arg == "--dry-run" || arg == "-n" -> dryRun = true
+          arg == "--set-exit-if-changed" -> setExitIfChanged = true
+          arg.startsWith("--") -> err.println("Unexpected option: $arg")
+          arg.startsWith("@") -> err.println("Unexpected option: $arg")
+          else -> fileNames.add(arg)
+        }
+      }
+      return ParsedArgs(fileNames, formattingOptions, dryRun, setExitIfChanged)
+    }
+  }
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/debughelpers/OpsDebug.kt b/core/src/main/java/com/facebook/ktfmt/debughelpers/OpsDebug.kt
new file mode 100644
index 0000000..11f5ba4
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/debughelpers/OpsDebug.kt
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.facebook.ktfmt.debughelpers
+
+import com.google.common.collect.ImmutableList
+import com.google.common.collect.Range
+import com.google.googlejavaformat.CloseOp
+import com.google.googlejavaformat.CommentsHelper
+import com.google.googlejavaformat.Doc
+import com.google.googlejavaformat.Input
+import com.google.googlejavaformat.Op
+import com.google.googlejavaformat.OpenOp
+import com.google.googlejavaformat.OpsBuilder
+import com.google.googlejavaformat.Output
+import java.util.regex.Pattern
+
+/** A regex to extract the indent size from OpenOp.toString(). */
+private val OPENOP_STRING_FORM_REGEX = Pattern.compile("""OpenOp\{plusIndent=Const\{n=(\d+)}}""")
+
+/**
+ * printOps prints a list of Ops in a hierarchical way.
+ *
+ * It's useful when debugging incorrect newlines.
+ */
+fun printOps(ops: ImmutableList<Op>) {
+  println("Ops: ")
+  var indent = 0
+  for (op in ops) {
+    val line =
+        when (op) {
+          is OpenOp -> {
+            val matcher = OPENOP_STRING_FORM_REGEX.matcher(op.toString())
+            if (matcher.matches()) {
+              val opIndent = matcher.group(1)
+              "[ " + if (opIndent != "0") opIndent else ""
+            } else {
+              "[ $op"
+            }
+          }
+          is CloseOp -> "]"
+          is Doc.Token -> {
+            var result: String? = ""
+            val output =
+                object : Output() {
+                  override fun indent(indent: Int) = Unit
+
+                  override fun blankLine(k: Int, wanted: OpsBuilder.BlankLineWanted?) = Unit
+
+                  override fun markForPartialFormat(start: Input.Token?, end: Input.Token?) = Unit
+
+                  override fun getCommentsHelper(): CommentsHelper {
+                    throw Throwable()
+                  }
+
+                  override fun append(text: String?, range: Range<Int>?) {
+                    result = text
+                  }
+                }
+            op.write(output)
+            """"$result""""
+          }
+          else -> {
+            val result = op.toString()
+            if (result == "Space{}") "\" \"" else result
+          }
+        }
+    if (op is CloseOp) {
+      indent--
+    }
+    repeat(2 * indent) { print(" ") }
+    if (op is OpenOp) {
+      indent++
+    }
+    println(line)
+  }
+  println()
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/debughelpers/PrintAstVisitor.kt b/core/src/main/java/com/facebook/ktfmt/debughelpers/PrintAstVisitor.kt
new file mode 100644
index 0000000..489deba
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/debughelpers/PrintAstVisitor.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.facebook.ktfmt.debughelpers
+
+import org.jetbrains.kotlin.com.intellij.psi.PsiElement
+import org.jetbrains.kotlin.psi.KtTreeVisitorVoid
+
+class PrintAstVisitor : KtTreeVisitorVoid() {
+  private var depth = 0
+
+  override fun visitElement(element: PsiElement) {
+    print("  ".repeat(depth))
+    println("${element.javaClass.simpleName} ${element.text?.replace("\n", "\\n")}")
+    depth++
+    super.visitElement(element)
+    depth--
+  }
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/format/Formatter.kt b/core/src/main/java/com/facebook/ktfmt/format/Formatter.kt
new file mode 100644
index 0000000..1fa90f7
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/format/Formatter.kt
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.facebook.ktfmt.format
+
+import com.facebook.ktfmt.debughelpers.printOps
+import com.facebook.ktfmt.format.FormattingOptions.Style.DROPBOX
+import com.facebook.ktfmt.format.FormattingOptions.Style.GOOGLE
+import com.facebook.ktfmt.format.RedundantElementRemover.dropRedundantElements
+import com.facebook.ktfmt.format.WhitespaceTombstones.indexOfWhitespaceTombstone
+import com.facebook.ktfmt.kdoc.Escaping
+import com.facebook.ktfmt.kdoc.KDocCommentsHelper
+import com.google.common.collect.ImmutableList
+import com.google.common.collect.Range
+import com.google.googlejavaformat.Doc
+import com.google.googlejavaformat.DocBuilder
+import com.google.googlejavaformat.Newlines
+import com.google.googlejavaformat.OpsBuilder
+import com.google.googlejavaformat.java.FormatterException
+import com.google.googlejavaformat.java.JavaOutput
+import org.jetbrains.kotlin.com.intellij.openapi.util.text.StringUtil
+import org.jetbrains.kotlin.com.intellij.openapi.util.text.StringUtilRt
+import org.jetbrains.kotlin.com.intellij.psi.PsiComment
+import org.jetbrains.kotlin.com.intellij.psi.PsiElement
+import org.jetbrains.kotlin.com.intellij.psi.PsiElementVisitor
+import org.jetbrains.kotlin.com.intellij.psi.PsiWhiteSpace
+import org.jetbrains.kotlin.psi.KtImportDirective
+import org.jetbrains.kotlin.psi.psiUtil.endOffset
+import org.jetbrains.kotlin.psi.psiUtil.startOffset
+
+object Formatter {
+
+  @JvmField
+  val GOOGLE_FORMAT = FormattingOptions(style = GOOGLE, blockIndent = 2, continuationIndent = 2)
+
+  /** A format that attempts to reflect https://kotlinlang.org/docs/coding-conventions.html. */
+  @JvmField
+  val KOTLINLANG_FORMAT = FormattingOptions(style = GOOGLE, blockIndent = 4, continuationIndent = 4)
+
+  @JvmField
+  val DROPBOX_FORMAT = FormattingOptions(style = DROPBOX, blockIndent = 4, continuationIndent = 4)
+
+  private val MINIMUM_KOTLIN_VERSION = KotlinVersion(1, 4)
+
+  /**
+   * format formats the Kotlin code given in 'code' and returns it as a string. This method is
+   * accessed through Reflection.
+   */
+  @JvmStatic
+  @Throws(FormatterException::class, ParseError::class)
+  fun format(code: String): String = format(FormattingOptions(), code)
+
+  /**
+   * format formats the Kotlin code given in 'code' with 'removeUnusedImports' and returns it as a
+   * string. This method is accessed through Reflection.
+   */
+  @JvmStatic
+  @Throws(FormatterException::class, ParseError::class)
+  fun format(code: String, removeUnusedImports: Boolean): String =
+      format(FormattingOptions(removeUnusedImports = removeUnusedImports), code)
+
+  /**
+   * format formats the Kotlin code given in 'code' with the 'maxWidth' and returns it as a string.
+   */
+  @JvmStatic
+  @Throws(FormatterException::class, ParseError::class)
+  fun format(options: FormattingOptions, code: String): String {
+    val (shebang, kotlinCode) =
+        if (code.startsWith("#!")) {
+          code.split("\n".toRegex(), limit = 2)
+        } else {
+          listOf("", code)
+        }
+    checkEscapeSequences(kotlinCode)
+
+    val lfCode = StringUtilRt.convertLineSeparators(kotlinCode)
+    val sortedImports = sortedAndDistinctImports(lfCode)
+    val pretty = prettyPrint(sortedImports, options, "\n")
+    val noRedundantElements =
+        try {
+          dropRedundantElements(pretty, options)
+        } catch (e: ParseError) {
+          throw IllegalStateException("Failed to re-parse code after pretty printing:\n $pretty", e)
+        }
+    val prettyCode =
+        prettyPrint(noRedundantElements, options, Newlines.guessLineSeparator(kotlinCode)!!)
+    return if (shebang.isNotEmpty()) shebang + "\n" + prettyCode else prettyCode
+  }
+
+  /** prettyPrint reflows 'code' using google-java-format's engine. */
+  private fun prettyPrint(code: String, options: FormattingOptions, lineSeparator: String): String {
+    val file = Parser.parse(code)
+    val kotlinInput = KotlinInput(code, file)
+    val javaOutput =
+        JavaOutput(lineSeparator, kotlinInput, KDocCommentsHelper(lineSeparator, options.maxWidth))
+    val builder = OpsBuilder(kotlinInput, javaOutput)
+    file.accept(createAstVisitor(options, builder))
+    builder.sync(kotlinInput.text.length)
+    builder.drain()
+    val ops = builder.build()
+    if (options.debuggingPrintOpsAfterFormatting) {
+      printOps(ops)
+    }
+    val doc = DocBuilder().withOps(ops).build()
+    doc.computeBreaks(javaOutput.commentsHelper, options.maxWidth, Doc.State(+0, 0))
+    doc.write(javaOutput)
+    javaOutput.flush()
+
+    val tokenRangeSet =
+        kotlinInput.characterRangesToTokenRanges(ImmutableList.of(Range.closedOpen(0, code.length)))
+    return WhitespaceTombstones.replaceTombstoneWithTrailingWhitespace(
+        JavaOutput.applyReplacements(code, javaOutput.getFormatReplacements(tokenRangeSet)))
+  }
+
+  private fun createAstVisitor(options: FormattingOptions, builder: OpsBuilder): PsiElementVisitor {
+    if (KotlinVersion.CURRENT < MINIMUM_KOTLIN_VERSION) {
+      throw RuntimeException("Unsupported runtime Kotlin version: " + KotlinVersion.CURRENT)
+    }
+    return KotlinInputAstVisitor(options, builder)
+  }
+
+  private fun checkEscapeSequences(code: String) {
+    var index = code.indexOfWhitespaceTombstone()
+    if (index == -1) {
+      index = Escaping.indexOfCommentEscapeSequences(code)
+    }
+    if (index != -1) {
+      throw ParseError(
+          "ktfmt does not support code which contains one of {\\u0003, \\u0004, \\u0005} character" +
+              "; escape it",
+          StringUtil.offsetToLineColumn(code, index))
+    }
+  }
+
+  private fun sortedAndDistinctImports(code: String): String {
+    val file = Parser.parse(code)
+
+    val importList = file.importList ?: return code
+    if (importList.imports.isEmpty()) {
+      return code
+    }
+
+    val commentList = mutableListOf<PsiElement>()
+    // Find non-import elements; comments are moved, in order, to the top of the import list. Other
+    // non-import elements throw a ParseError.
+    var element = importList.firstChild
+    while (element != null) {
+      if (element is PsiComment) {
+        commentList.add(element)
+      } else if (element !is KtImportDirective && element !is PsiWhiteSpace) {
+        throw ParseError(
+            "Imports not contiguous: " + element.text,
+            StringUtil.offsetToLineColumn(code, element.startOffset))
+      }
+      element = element.nextSibling
+    }
+    fun canonicalText(importDirective: KtImportDirective) =
+        importDirective.importedFqName?.asString() +
+            " " +
+            importDirective.alias?.text?.replace("`", "") +
+            " " +
+            if (importDirective.isAllUnder) "*" else ""
+
+    val sortedImports = importList.imports.sortedBy(::canonicalText).distinctBy(::canonicalText)
+    val importsWithComments = commentList + sortedImports
+
+    return code.replaceRange(
+        importList.startOffset,
+        importList.endOffset,
+        importsWithComments.joinToString(separator = "\n") { imprt -> imprt.text } + "\n")
+  }
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/format/FormattingOptions.kt b/core/src/main/java/com/facebook/ktfmt/format/FormattingOptions.kt
new file mode 100644
index 0000000..791cde3
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/format/FormattingOptions.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.facebook.ktfmt.format
+
+data class FormattingOptions(
+    val style: Style = Style.FACEBOOK,
+
+    /** ktfmt breaks lines longer than maxWidth. */
+    val maxWidth: Int = DEFAULT_MAX_WIDTH,
+
+    /**
+     * blockIndent is the size of the indent used when a new block is opened, in spaces.
+     *
+     * For example,
+     * ```
+     * fun f() {
+     *   //
+     * }
+     * ```
+     */
+    val blockIndent: Int = 2,
+
+    /**
+     * continuationIndent is the size of the indent used when a line is broken because it's too
+     * long, in spaces.
+     *
+     * For example,
+     * ```
+     * val foo = bar(
+     *     1)
+     * ```
+     */
+    val continuationIndent: Int = 4,
+
+    /** Whether ktfmt should remove imports that are not used. */
+    val removeUnusedImports: Boolean = true,
+
+    /**
+     * Print the Ops generated by KotlinInputAstVisitor to help reason about formatting (i.e.,
+     * newline) decisions
+     */
+    val debuggingPrintOpsAfterFormatting: Boolean = false
+) {
+
+  companion object {
+    const val DEFAULT_MAX_WIDTH: Int = 100
+  }
+
+  enum class Style {
+    FACEBOOK,
+    DROPBOX,
+    GOOGLE
+  }
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/format/KotlinInput.kt b/core/src/main/java/com/facebook/ktfmt/format/KotlinInput.kt
new file mode 100644
index 0000000..52b01a4
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/format/KotlinInput.kt
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.facebook.ktfmt.format
+
+import com.google.common.collect.DiscreteDomain
+import com.google.common.collect.ImmutableList
+import com.google.common.collect.ImmutableMap
+import com.google.common.collect.ImmutableRangeMap
+import com.google.common.collect.Iterables.getLast
+import com.google.common.collect.Range
+import com.google.common.collect.RangeSet
+import com.google.common.collect.TreeRangeSet
+import com.google.googlejavaformat.Input
+import com.google.googlejavaformat.Newlines
+import com.google.googlejavaformat.java.FormatterException
+import com.google.googlejavaformat.java.JavaOutput
+import java.util.LinkedHashMap
+import org.jetbrains.kotlin.com.intellij.openapi.util.text.StringUtil
+import org.jetbrains.kotlin.lexer.KtTokens
+import org.jetbrains.kotlin.psi.KtFile
+
+// TODO: share the code with JavaInput instead of copy-pasting here.
+/**
+ * KotlinInput is for Kotlin what JavaInput is for Java.
+ *
+ * <p>KotlinInput is duplicating most of JavaInput's code, but uses the Kotlin compiler as a lexer
+ * instead of Javac. This is required because some valid Kotlin programs are not valid Java
+ * programs, e.g., "a..b".
+ *
+ * <p>See javadoc for JavaInput.
+ */
+class KotlinInput(private val text: String, file: KtFile) : Input() {
+  private val tokens: ImmutableList<Token> // The Tokens for this input.
+  private val positionToColumnMap: ImmutableMap<Int, Int> // Map Tok position to column.
+  private val positionTokenMap: ImmutableRangeMap<Int, Token> // Map position to Token.
+  private var kN = 0 // The number of numbered toks (tokens or comments), excluding the EOF.
+  private val kToToken: Array<Token?>
+
+  init {
+    setLines(ImmutableList.copyOf(Newlines.lineIterator(text)))
+    val toks = buildToks(file, text)
+    positionToColumnMap = makePositionToColumnMap(toks)
+    tokens = buildTokens(toks)
+    val tokenLocations = ImmutableRangeMap.builder<Int, Token>()
+    for (token in tokens) {
+      val end = JavaOutput.endTok(token)
+      var upper = end.position
+      if (end.text.isNotEmpty()) {
+        upper += end.length() - 1
+      }
+      tokenLocations.put(Range.closed(JavaOutput.startTok(token).position, upper), token)
+    }
+    positionTokenMap = tokenLocations.build()
+
+    // adjust kN for EOF
+    kToToken = arrayOfNulls(kN + 1)
+    for (token in tokens) {
+      for (tok in token.toksBefore) {
+        if (tok.index < 0) {
+          continue
+        }
+        kToToken[tok.index] = token
+      }
+      kToToken[token.tok.index] = token
+      for (tok in token.toksAfter) {
+        if (tok.index < 0) {
+          continue
+        }
+        kToToken[tok.index] = token
+      }
+    }
+  }
+
+  @Throws(FormatterException::class)
+  fun characterRangesToTokenRanges(characterRanges: Collection<Range<Int>>): RangeSet<Int> {
+    val tokenRangeSet = TreeRangeSet.create<Int>()
+    for (characterRange0 in characterRanges) {
+      val characterRange = characterRange0.canonical(DiscreteDomain.integers())
+      tokenRangeSet.add(
+          characterRangeToTokenRange(
+              characterRange.lowerEndpoint(),
+              characterRange.upperEndpoint() - characterRange.lowerEndpoint()))
+    }
+    return tokenRangeSet
+  }
+
+  /**
+   * Convert from an offset and length flag pair to a token range.
+   *
+   * @param offset the `0`-based offset in characters
+   * @param length the length in characters
+   * @return the `0`-based [Range] of tokens
+   * @throws FormatterException
+   */
+  @Throws(FormatterException::class)
+  internal fun characterRangeToTokenRange(offset: Int, length: Int): Range<Int> {
+    val requiredLength = offset + length
+    if (requiredLength > text.length) {
+      throw FormatterException(
+          String.format(
+              "error: invalid length %d, offset + length (%d) is outside the file",
+              length,
+              requiredLength))
+    }
+    val expandedLength =
+        when {
+          length < 0 -> return EMPTY_RANGE
+          length == 0 -> 1 // 0 stands for "format the line under the cursor"
+          else -> length
+        }
+    val enclosed =
+        getPositionTokenMap()
+            .subRangeMap(Range.closedOpen(offset, offset + expandedLength))
+            .asMapOfRanges()
+            .values
+    return if (enclosed.isEmpty()) {
+      EMPTY_RANGE
+    } else
+        Range.closedOpen(
+            enclosed.iterator().next().tok.index, getLast(enclosed).getTok().getIndex() + 1)
+  }
+
+  private fun makePositionToColumnMap(toks: List<KotlinTok>): ImmutableMap<Int, Int> {
+    val builder = LinkedHashMap<Int, Int>()
+    for (tok in toks) {
+      builder.put(tok.position, tok.column)
+    }
+    return ImmutableMap.copyOf(builder)
+  }
+
+  private fun buildToks(file: KtFile, fileText: String): ImmutableList<KotlinTok> {
+    val tokenizer = Tokenizer(fileText, file)
+    file.accept(tokenizer)
+    val toks = tokenizer.toks
+    toks.add(KotlinTok(tokenizer.index, "", "", fileText.length, 0, true, KtTokens.EOF))
+    kN = tokenizer.index
+    computeRanges(toks)
+    return ImmutableList.copyOf(toks)
+  }
+
+  private fun buildTokens(toks: List<KotlinTok>): ImmutableList<Token> {
+    val tokens = ImmutableList.builder<Token>()
+    var k = 0
+    val kN = toks.size
+
+    // Remaining non-tokens before the token go here.
+    var toksBefore: ImmutableList.Builder<KotlinTok> = ImmutableList.builder()
+
+    OUTERMOST@ while (k < kN) {
+      while (!toks[k].isToken) {
+        val tok = toks[k++]
+        toksBefore.add(tok)
+        if (isParamComment(tok)) {
+          while (toks[k].isNewline) {
+            // drop newlines after parameter comments
+            k++
+          }
+        }
+      }
+      val tok = toks[k++]
+
+      // Non-tokens starting on the same line go here too.
+      val toksAfter = ImmutableList.builder<KotlinTok>()
+      OUTER@ while (k < kN && !toks[k].isToken) {
+        // Don't attach inline comments to certain leading tokens, e.g. for `f(/*flag1=*/true).
+        //
+        // Attaching inline comments to the right token is hard, and this barely
+        // scratches the surface. But it's enough to do a better job with parameter
+        // name comments.
+        //
+        // TODO(cushon): find a better strategy.
+        if (toks[k].isSlashStarComment && (tok.text == "(" || tok.text == "<" || tok.text == "."))
+            break@OUTER
+        if (toks[k].isJavadocComment && tok.text == ";") break@OUTER
+        if (isParamComment(toks[k])) {
+          tokens.add(KotlinToken(toksBefore.build(), tok, toksAfter.build()))
+          toksBefore = ImmutableList.builder<KotlinTok>().add(toks[k++])
+          // drop newlines after parameter comments
+          while (toks[k].isNewline) {
+            k++
+          }
+          continue@OUTERMOST
+        }
+        val nonTokenAfter = toks[k++]
+        toksAfter.add(nonTokenAfter)
+        if (Newlines.containsBreaks(nonTokenAfter.text)) {
+          break
+        }
+      }
+      tokens.add(KotlinToken(toksBefore.build(), tok, toksAfter.build()))
+      toksBefore = ImmutableList.builder()
+    }
+    return tokens.build()
+  }
+
+  private fun isParamComment(tok: Tok): Boolean {
+    return tok.isSlashStarComment && tok.text.matches("/\\*[A-Za-z0-9\\s_\\-]+=\\s*\\*/".toRegex())
+  }
+
+  override fun getkN(): Int = kN
+
+  override fun getToken(k: Int): Token? = kToToken[k]
+
+  override fun getTokens(): ImmutableList<out Token> = tokens
+
+  override fun getPositionTokenMap(): ImmutableRangeMap<Int, out Token> = positionTokenMap
+
+  override fun getPositionToColumnMap(): ImmutableMap<Int, Int> = positionToColumnMap
+
+  override fun getText(): String = text
+
+  override fun getLineNumber(inputPosition: Int) =
+      StringUtil.offsetToLineColumn(text, inputPosition).line + 1
+
+  override fun getColumnNumber(inputPosition: Int) =
+      StringUtil.offsetToLineColumn(text, inputPosition).column
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/format/KotlinInputAstVisitor.kt b/core/src/main/java/com/facebook/ktfmt/format/KotlinInputAstVisitor.kt
new file mode 100644
index 0000000..358fe50
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/format/KotlinInputAstVisitor.kt
@@ -0,0 +1,2447 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.facebook.ktfmt.format
+
+import com.google.common.base.Throwables
+import com.google.common.collect.ImmutableList
+import com.google.googlejavaformat.Doc
+import com.google.googlejavaformat.FormattingError
+import com.google.googlejavaformat.Indent
+import com.google.googlejavaformat.Indent.Const.ZERO
+import com.google.googlejavaformat.OpsBuilder
+import com.google.googlejavaformat.Output
+import com.google.googlejavaformat.Output.BreakTag
+import java.util.ArrayDeque
+import java.util.Optional
+import org.jetbrains.kotlin.com.intellij.psi.PsiComment
+import org.jetbrains.kotlin.com.intellij.psi.PsiElement
+import org.jetbrains.kotlin.com.intellij.psi.PsiWhiteSpace
+import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
+import org.jetbrains.kotlin.lexer.KtTokens
+import org.jetbrains.kotlin.psi.KtAnnotatedExpression
+import org.jetbrains.kotlin.psi.KtAnnotation
+import org.jetbrains.kotlin.psi.KtAnnotationEntry
+import org.jetbrains.kotlin.psi.KtAnnotationUseSiteTarget
+import org.jetbrains.kotlin.psi.KtArrayAccessExpression
+import org.jetbrains.kotlin.psi.KtBinaryExpression
+import org.jetbrains.kotlin.psi.KtBinaryExpressionWithTypeRHS
+import org.jetbrains.kotlin.psi.KtBlockExpression
+import org.jetbrains.kotlin.psi.KtBreakExpression
+import org.jetbrains.kotlin.psi.KtCallExpression
+import org.jetbrains.kotlin.psi.KtCallableReferenceExpression
+import org.jetbrains.kotlin.psi.KtCatchClause
+import org.jetbrains.kotlin.psi.KtClass
+import org.jetbrains.kotlin.psi.KtClassInitializer
+import org.jetbrains.kotlin.psi.KtClassLiteralExpression
+import org.jetbrains.kotlin.psi.KtClassOrObject
+import org.jetbrains.kotlin.psi.KtCollectionLiteralExpression
+import org.jetbrains.kotlin.psi.KtConstantExpression
+import org.jetbrains.kotlin.psi.KtConstructorDelegationCall
+import org.jetbrains.kotlin.psi.KtContainerNode
+import org.jetbrains.kotlin.psi.KtContinueExpression
+import org.jetbrains.kotlin.psi.KtDelegatedSuperTypeEntry
+import org.jetbrains.kotlin.psi.KtDestructuringDeclaration
+import org.jetbrains.kotlin.psi.KtDestructuringDeclarationEntry
+import org.jetbrains.kotlin.psi.KtDoWhileExpression
+import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
+import org.jetbrains.kotlin.psi.KtDynamicType
+import org.jetbrains.kotlin.psi.KtElement
+import org.jetbrains.kotlin.psi.KtEnumEntry
+import org.jetbrains.kotlin.psi.KtExpression
+import org.jetbrains.kotlin.psi.KtFile
+import org.jetbrains.kotlin.psi.KtFileAnnotationList
+import org.jetbrains.kotlin.psi.KtFinallySection
+import org.jetbrains.kotlin.psi.KtForExpression
+import org.jetbrains.kotlin.psi.KtFunctionType
+import org.jetbrains.kotlin.psi.KtIfExpression
+import org.jetbrains.kotlin.psi.KtImportDirective
+import org.jetbrains.kotlin.psi.KtImportList
+import org.jetbrains.kotlin.psi.KtIsExpression
+import org.jetbrains.kotlin.psi.KtLabelReferenceExpression
+import org.jetbrains.kotlin.psi.KtLabeledExpression
+import org.jetbrains.kotlin.psi.KtLambdaArgument
+import org.jetbrains.kotlin.psi.KtLambdaExpression
+import org.jetbrains.kotlin.psi.KtModifierList
+import org.jetbrains.kotlin.psi.KtNamedFunction
+import org.jetbrains.kotlin.psi.KtNullableType
+import org.jetbrains.kotlin.psi.KtPackageDirective
+import org.jetbrains.kotlin.psi.KtParameter
+import org.jetbrains.kotlin.psi.KtParameterList
+import org.jetbrains.kotlin.psi.KtParenthesizedExpression
+import org.jetbrains.kotlin.psi.KtPostfixExpression
+import org.jetbrains.kotlin.psi.KtPrefixExpression
+import org.jetbrains.kotlin.psi.KtPrimaryConstructor
+import org.jetbrains.kotlin.psi.KtProjectionKind
+import org.jetbrains.kotlin.psi.KtProperty
+import org.jetbrains.kotlin.psi.KtPropertyAccessor
+import org.jetbrains.kotlin.psi.KtPropertyDelegate
+import org.jetbrains.kotlin.psi.KtQualifiedExpression
+import org.jetbrains.kotlin.psi.KtReferenceExpression
+import org.jetbrains.kotlin.psi.KtReturnExpression
+import org.jetbrains.kotlin.psi.KtScript
+import org.jetbrains.kotlin.psi.KtSecondaryConstructor
+import org.jetbrains.kotlin.psi.KtSimpleNameExpression
+import org.jetbrains.kotlin.psi.KtStringTemplateExpression
+import org.jetbrains.kotlin.psi.KtSuperExpression
+import org.jetbrains.kotlin.psi.KtSuperTypeCallEntry
+import org.jetbrains.kotlin.psi.KtSuperTypeList
+import org.jetbrains.kotlin.psi.KtThisExpression
+import org.jetbrains.kotlin.psi.KtThrowExpression
+import org.jetbrains.kotlin.psi.KtTreeVisitorVoid
+import org.jetbrains.kotlin.psi.KtTryExpression
+import org.jetbrains.kotlin.psi.KtTypeAlias
+import org.jetbrains.kotlin.psi.KtTypeArgumentList
+import org.jetbrains.kotlin.psi.KtTypeConstraint
+import org.jetbrains.kotlin.psi.KtTypeConstraintList
+import org.jetbrains.kotlin.psi.KtTypeParameter
+import org.jetbrains.kotlin.psi.KtTypeParameterList
+import org.jetbrains.kotlin.psi.KtTypeProjection
+import org.jetbrains.kotlin.psi.KtTypeReference
+import org.jetbrains.kotlin.psi.KtUserType
+import org.jetbrains.kotlin.psi.KtValueArgument
+import org.jetbrains.kotlin.psi.KtValueArgumentList
+import org.jetbrains.kotlin.psi.KtWhenConditionInRange
+import org.jetbrains.kotlin.psi.KtWhenConditionIsPattern
+import org.jetbrains.kotlin.psi.KtWhenConditionWithExpression
+import org.jetbrains.kotlin.psi.KtWhenExpression
+import org.jetbrains.kotlin.psi.KtWhileExpression
+import org.jetbrains.kotlin.psi.psiUtil.children
+import org.jetbrains.kotlin.psi.psiUtil.startOffset
+import org.jetbrains.kotlin.psi.psiUtil.startsWithComment
+
+/** An AST visitor that builds a stream of {@link Op}s to format. */
+class KotlinInputAstVisitor(
+    private val options: FormattingOptions,
+    private val builder: OpsBuilder
+) : KtTreeVisitorVoid() {
+
+  private val isGoogleStyle = options.style == FormattingOptions.Style.GOOGLE
+
+  /** Standard indentation for a block */
+  private val blockIndent: Indent.Const = Indent.Const.make(options.blockIndent, 1)
+
+  /**
+   * Standard indentation for a long expression or function call, it is different than block
+   * indentation on purpose
+   */
+  private val expressionBreakIndent: Indent.Const = Indent.Const.make(options.continuationIndent, 1)
+
+  private val blockPlusExpressionBreakIndent: Indent.Const =
+      Indent.Const.make(options.blockIndent + options.continuationIndent, 1)
+
+  private val doubleExpressionBreakIndent: Indent.Const =
+      Indent.Const.make(options.continuationIndent, 2)
+
+  private val expressionBreakNegativeIndent: Indent.Const =
+      Indent.Const.make(-options.continuationIndent, 1)
+
+  /** A record of whether we have visited into an expression. */
+  private val inExpression = ArrayDeque(ImmutableList.of(false))
+
+  /** Tracks whether we are handling an import directive */
+  private var inImport = false
+
+  /** Example: `fun foo(n: Int) { println(n) }` */
+  override fun visitNamedFunction(function: KtNamedFunction) {
+    builder.sync(function)
+    builder.block(ZERO) {
+      visitFunctionLikeExpression(
+          function.modifierList,
+          "fun",
+          function.typeParameterList,
+          function.receiverTypeReference,
+          function.nameIdentifier?.text,
+          true,
+          function.valueParameterList,
+          function.typeConstraintList,
+          function.bodyBlockExpression,
+          function.bodyExpression,
+          function.typeReference,
+          function.bodyBlockExpression?.lBrace != null)
+    }
+  }
+
+  /** Example `Int`, `(String)` or `() -> Int` */
+  override fun visitTypeReference(typeReference: KtTypeReference) {
+    builder.sync(typeReference)
+    // Normally we'd visit the children nodes through accessors on 'typeReference', and  we wouldn't
+    // loop over children.
+    // But, in this case the modifier list can either be inside the parenthesis:
+    // ... (@Composable (x) -> Unit)
+    // or outside of them:
+    // ... @Composable ((x) -> Unit)
+    val modifierList = typeReference.modifierList
+    val typeElement = typeReference.typeElement
+    for (child in typeReference.node.children()) {
+      when {
+        child.psi == modifierList -> visit(modifierList)
+        child.psi == typeElement -> visit(typeElement)
+        child.elementType == KtTokens.LPAR -> builder.token("(")
+        child.elementType == KtTokens.RPAR -> builder.token(")")
+      }
+    }
+  }
+
+  override fun visitDynamicType(type: KtDynamicType) {
+    builder.token("dynamic")
+  }
+
+  /** Example: `String?` or `((Int) -> Unit)?` */
+  override fun visitNullableType(nullableType: KtNullableType) {
+    builder.sync(nullableType)
+    val innerType = nullableType.innerType
+    val addParenthesis = innerType is KtFunctionType
+    if (addParenthesis) {
+      builder.token("(")
+    }
+    visit(nullableType.modifierList)
+    visit(innerType)
+    if (addParenthesis) {
+      builder.token(")")
+    }
+    builder.token("?")
+  }
+
+  /** Example: `String` or `List<Int>`, */
+  override fun visitUserType(type: KtUserType) {
+    builder.sync(type)
+
+    if (type.qualifier != null) {
+      visit(type.qualifier)
+      builder.token(".")
+    }
+    visit(type.referenceExpression)
+    val typeArgumentList = type.typeArgumentList
+    if (typeArgumentList != null) {
+      builder.block(expressionBreakIndent) { visit(typeArgumentList) }
+    }
+  }
+
+  /** Example `<Int, String>` in `List<Int, String>` */
+  override fun visitTypeArgumentList(typeArgumentList: KtTypeArgumentList) {
+    builder.sync(typeArgumentList)
+    builder.block(ZERO) {
+      builder.token("<")
+      builder.breakOp(Doc.FillMode.UNIFIED, "", ZERO)
+      builder.block(ZERO) {
+        emitParameterLikeList(
+            typeArgumentList.arguments, typeArgumentList.trailingComma != null, wrapInBlock = true)
+      }
+    }
+    builder.token(">")
+  }
+
+  override fun visitTypeProjection(typeProjection: KtTypeProjection) {
+    builder.sync(typeProjection)
+    val typeReference = typeProjection.typeReference
+    when (typeProjection.projectionKind) {
+      KtProjectionKind.IN -> {
+        builder.token("in")
+        builder.space()
+        visit(typeReference)
+      }
+      KtProjectionKind.OUT -> {
+        builder.token("out")
+        builder.space()
+        visit(typeReference)
+      }
+      KtProjectionKind.STAR -> builder.token("*")
+      KtProjectionKind.NONE -> visit(typeReference)
+    }
+  }
+
+  /**
+   * @param keyword e.g., "fun" or "class".
+   * @param typeOrDelegationCall for functions, the return typeOrDelegationCall; for classes, the
+   * list of supertypes.
+   */
+  private fun visitFunctionLikeExpression(
+      modifierList: KtModifierList?,
+      keyword: String,
+      typeParameters: KtTypeParameterList?,
+      receiverTypeReference: KtTypeReference?,
+      name: String?,
+      emitParenthesis: Boolean,
+      parameterList: KtParameterList?,
+      typeConstraintList: KtTypeConstraintList?,
+      bodyBlockExpression: KtBlockExpression?,
+      nonBlockBodyExpressions: KtExpression?,
+      typeOrDelegationCall: KtElement?,
+      emitBraces: Boolean
+  ) {
+    builder.block(ZERO) {
+      if (modifierList != null) {
+        visitModifierList(modifierList)
+      }
+      builder.token(keyword)
+      if (typeParameters != null) {
+        builder.space()
+        builder.block(ZERO) { visit(typeParameters) }
+      }
+
+      if (name != null || receiverTypeReference != null) {
+        builder.space()
+      }
+      builder.block(ZERO) {
+        if (receiverTypeReference != null) {
+          visit(receiverTypeReference)
+          builder.breakOp(Doc.FillMode.INDEPENDENT, "", expressionBreakIndent)
+          builder.token(".")
+        }
+        if (name != null) {
+          builder.token(name)
+        }
+      }
+      if (emitParenthesis) {
+        builder.token("(")
+      }
+      var paramBlockNeedsClosing = false
+      builder.block(ZERO) {
+        if (parameterList != null && parameterList.parameters.isNotEmpty()) {
+          paramBlockNeedsClosing = true
+          builder.open(expressionBreakIndent)
+          builder.breakOp(Doc.FillMode.UNIFIED, "", expressionBreakIndent)
+          visit(parameterList)
+        }
+        if (emitParenthesis) {
+          if (parameterList != null && parameterList.parameters.isNotEmpty()) {
+            builder.breakOp(Doc.FillMode.UNIFIED, "", expressionBreakNegativeIndent)
+          }
+          builder.token(")")
+        } else {
+          if (paramBlockNeedsClosing) {
+            builder.close()
+          }
+        }
+        if (typeOrDelegationCall != null) {
+          builder.block(ZERO) {
+            if (typeOrDelegationCall is KtConstructorDelegationCall) {
+              builder.space()
+            }
+            builder.token(":")
+            if (parameterList?.parameters.isNullOrEmpty()) {
+              builder.breakOp(Doc.FillMode.INDEPENDENT, " ", expressionBreakIndent)
+              builder.block(expressionBreakIndent) { visit(typeOrDelegationCall) }
+            } else {
+              builder.space()
+              builder.block(expressionBreakNegativeIndent) { visit(typeOrDelegationCall) }
+            }
+          }
+        }
+      }
+      if (paramBlockNeedsClosing) {
+        builder.close()
+      }
+      if (typeConstraintList != null) {
+        builder.space()
+        visit(typeConstraintList)
+      }
+      if (bodyBlockExpression != null) {
+        builder.space()
+        visitBlockBody(bodyBlockExpression, emitBraces)
+      } else if (nonBlockBodyExpressions != null) {
+        builder.space()
+        builder.block(ZERO) {
+          builder.token("=")
+          if (isLambdaOrScopingFunction(nonBlockBodyExpressions)) {
+            visitLambdaOrScopingFunction(nonBlockBodyExpressions)
+          } else {
+            builder.block(expressionBreakIndent) {
+              builder.breakOp(Doc.FillMode.INDEPENDENT, " ", ZERO)
+              builder.block(ZERO) { visit(nonBlockBodyExpressions) }
+            }
+          }
+        }
+      }
+      builder.guessToken(";")
+    }
+    if (name != null) {
+      builder.forcedBreak()
+    }
+  }
+
+  private fun genSym(): Output.BreakTag {
+    return Output.BreakTag()
+  }
+
+  private fun visitBlockBody(bodyBlockExpression: PsiElement, emitBraces: Boolean) {
+    if (emitBraces) {
+      builder.token("{", Doc.Token.RealOrImaginary.REAL, blockIndent, Optional.of(blockIndent))
+    }
+    val statements = bodyBlockExpression.children
+    if (statements.isNotEmpty()) {
+      builder.block(blockIndent) {
+        builder.forcedBreak()
+        builder.blankLineWanted(OpsBuilder.BlankLineWanted.PRESERVE)
+        visitStatements(statements)
+      }
+      builder.forcedBreak()
+      builder.blankLineWanted(OpsBuilder.BlankLineWanted.NO)
+    }
+    if (emitBraces) {
+      builder.token("}", blockIndent)
+    }
+  }
+
+  private fun visitStatement(statement: PsiElement) {
+    builder.block(ZERO) { visit(statement) }
+    builder.guessToken(";")
+  }
+
+  private fun visitStatements(statements: Array<PsiElement>) {
+    var first = true
+    builder.guessToken(";")
+    for (statement in statements) {
+      builder.forcedBreak()
+      if (!first) {
+        builder.blankLineWanted(OpsBuilder.BlankLineWanted.PRESERVE)
+      }
+      first = false
+      visitStatement(statement)
+    }
+  }
+
+  override fun visitProperty(property: KtProperty) {
+    builder.sync(property)
+    builder.block(ZERO) {
+      declareOne(
+          kind = DeclarationKind.FIELD,
+          modifiers = property.modifierList,
+          valOrVarKeyword = property.valOrVarKeyword.text,
+          typeParameters = property.typeParameterList,
+          receiver = property.receiverTypeReference,
+          name = property.nameIdentifier?.text,
+          type = property.typeReference,
+          typeConstraintList = property.typeConstraintList,
+          delegate = property.delegate,
+          initializer = property.initializer,
+          accessors = property.accessors)
+    }
+    builder.guessToken(";")
+    if (property.parent !is KtWhenExpression) {
+      builder.forcedBreak()
+    }
+  }
+
+  /**
+   * Example: "com.facebook.bla.bla" in imports or "a.b.c.d" in expressions.
+   *
+   * There's a few cases that are different. We deal with imports by keeping them on the same line.
+   * For regular chained expressions we go the left most descendant so we can start indentation only
+   * before the first break (a `.` or `?.`), and keep the seem indentation for this chain of calls.
+   */
+  override fun visitQualifiedExpression(expression: KtQualifiedExpression) {
+    builder.sync(expression)
+    val receiver = expression.receiverExpression
+    when {
+      inImport -> {
+        visit(receiver)
+        val selectorExpression = expression.selectorExpression
+        if (selectorExpression != null) {
+          builder.token(".")
+          visit(selectorExpression)
+        }
+      }
+      receiver is KtWhenExpression || receiver is KtStringTemplateExpression -> {
+        builder.block(ZERO) {
+          visit(receiver)
+          builder.token(expression.operationSign.value)
+          visit(expression.selectorExpression)
+        }
+      }
+      else -> {
+        emitQualifiedExpression(expression)
+      }
+    }
+  }
+
+  /** Extra data to help [emitQualifiedExpression] know when to open and close a group */
+  private class GroupingInfo {
+    var groupOpenCount = 0
+    var shouldCloseGroup = false
+  }
+
+  /**
+   * Handles a chain of qualified expressions, i.e. `a[5].b!!.c()[4].f()`
+   *
+   * This is by far the most complicated part of this formatter. We start by breaking the expression
+   * to the steps it is executed in (which are in the opposite order of how the syntax tree is
+   * built).
+   *
+   * We then calculate information to know which parts need to be groups, and finally go part by
+   * part, emitting it to the [builder] while closing and opening groups.
+   */
+  private fun emitQualifiedExpression(expression: KtExpression) {
+    val parts = breakIntoParts(expression)
+    // whether we want to make a lambda look like a block, this make Kotlin DSLs look as expected
+    val useBlockLikeLambdaStyle = parts.last().isLambda() && parts.count { it.isLambda() } == 1
+    val groupingInfos = computeGroupingInfo(parts, useBlockLikeLambdaStyle)
+    builder.block(expressionBreakIndent) {
+      val nameTag = genSym() // allows adjusting arguments indentation if a break will be made
+      for ((index, ktExpression) in parts.withIndex()) {
+        if (ktExpression is KtQualifiedExpression) {
+          builder.breakOp(Doc.FillMode.UNIFIED, "", ZERO, Optional.of(nameTag))
+        }
+        repeat(groupingInfos[index].groupOpenCount) { builder.open(ZERO) }
+        when (ktExpression) {
+          is KtQualifiedExpression -> {
+            builder.token(ktExpression.operationSign.value)
+            val selectorExpression = ktExpression.selectorExpression
+            if (selectorExpression !is KtCallExpression) {
+              // selector is a simple field access
+              visit(selectorExpression)
+              if (groupingInfos[index].shouldCloseGroup) {
+                builder.close()
+              }
+            } else {
+              // selector is a function call, we may close a group after its name
+              // emit `doIt` from `doIt(1, 2) { it }`
+              visit(selectorExpression.calleeExpression)
+              // close groups according to instructions
+              if (groupingInfos[index].shouldCloseGroup) {
+                builder.close()
+              }
+              // close group due to last lambda to allow block-like style in `as.forEach { ... }`
+              val isTrailingLambda = useBlockLikeLambdaStyle && index == parts.size - 1
+              if (isTrailingLambda) {
+                builder.close()
+              }
+              val argsIndentElse = if (index == parts.size - 1) ZERO else expressionBreakIndent
+              val lambdaIndentElse = if (isTrailingLambda) expressionBreakNegativeIndent else ZERO
+
+              // emit `(1, 2) { it }` from `doIt(1, 2) { it }`
+              visitCallElement(
+                  null,
+                  selectorExpression.typeArgumentList,
+                  selectorExpression.valueArgumentList,
+                  selectorExpression.lambdaArguments,
+                  argumentsIndent = Indent.If.make(nameTag, expressionBreakIndent, argsIndentElse),
+                  lambdaIndent = Indent.If.make(nameTag, ZERO, lambdaIndentElse),
+              )
+            }
+          }
+          is KtArrayAccessExpression -> {
+            visitArrayAccessBrackets(ktExpression)
+            builder.close()
+          }
+          is KtPostfixExpression -> {
+            builder.token(ktExpression.operationReference.text)
+            builder.close()
+          }
+          else -> {
+            check(index == 0)
+            visit(ktExpression)
+          }
+        }
+      }
+    }
+  }
+
+  /**
+   * Decomposes a qualified expression into parts, so `rainbow.red.orange.yellow` becomes `[rainbow,
+   * rainbow.red, rainbow.red.orange, rainbow.orange.yellow]`
+   */
+  private fun breakIntoParts(expression: KtExpression): List<KtExpression> {
+    val parts = ArrayDeque<KtExpression>()
+
+    // use an ArrayDeque and add elements to the beginning so the innermost expression comes first
+    // foo.bar.yay -> [yay, bar.yay, foo.bar.yay]
+
+    var node: KtExpression? = expression
+    while (node != null) {
+      parts.addFirst(node)
+      node =
+          when (node) {
+            is KtQualifiedExpression -> node.receiverExpression
+            is KtArrayAccessExpression -> node.arrayExpression
+            is KtPostfixExpression -> node.baseExpression
+            else -> null
+          }
+    }
+
+    return parts.toList()
+  }
+
+  /**
+   * Generates the [GroupingInfo] array to go with an array of [KtQualifiedExpression] parts
+   *
+   * For example, the expression `a.b[2].c.d()` is made of four expressions:
+   * 1. [KtQualifiedExpression] `a.b[2].c . d()` (this will be `parts[4]`)
+   * 1. [KtQualifiedExpression] `a.b[2] . c` (this will be `parts[3]`)
+   * 2. [KtArrayAccessExpression] `a.b [2]` (this will be `parts[2]`)
+   * 3. [KtQualifiedExpression] `a . b` (this will be `parts[1]`)
+   * 4. [KtSimpleNameExpression] `a` (this will be `parts[0]`)
+   *
+   * Once in parts, these are in the reverse order. To render the array correct we need to make sure
+   * `b` and [2] are in a group so we avoid splitting them. To do so we need to open a group for `b`
+   * (that will be done in part 2), and always close a group for an array.
+   *
+   * Here is the same expression, with justified braces marking the groupings it will get:
+   * ```
+   *  a . b [2] . c . d ()
+   * {a . b} --> Grouping `a.b` because it can be a package name or simple field access so we add 1
+   *             to the number of groups to open at groupingInfos[0], and mark to close a group at
+   *             groupingInfos[1]
+   * {a . b [2]} --> Grouping `a.b` with `[2]`, since otherwise we may break inside the brackets
+   *                 instead of preferring breaks before dots. So we open a group at [0], but since
+   *                 we always close a group after brackets, we don't store that information.
+   *             {c . d} --> another group to attach the first function name to the fields before it
+   *                         this time we don't start the group in the beginning, and use
+   *                         lastIndexToOpen to track the spot after the last time we stopped
+   *                         grouping.
+   * ```
+   * The final expression with groupings:
+   * ```
+   * {{a.b}[2]}.{c.d}()
+   * ```
+   */
+  private fun computeGroupingInfo(
+      parts: List<KtExpression>,
+      useBlockLikeLambdaStyle: Boolean
+  ): List<GroupingInfo> {
+    val groupingInfos = List(parts.size) { GroupingInfo() }
+    var lastIndexToOpen = 0
+    for ((index, part) in parts.withIndex()) {
+      when (part) {
+        is KtQualifiedExpression -> {
+          val receiverExpression = part.receiverExpression
+          val previous =
+              (receiverExpression as? KtQualifiedExpression)?.selectorExpression
+                  ?: receiverExpression
+          val current = checkNotNull(part.selectorExpression)
+          if (lastIndexToOpen == 0 &&
+              shouldGroupPartWithPrevious(parts, part, index, previous, current)) {
+            // this and the previous items should be grouped for better style
+            // we add another group to open in index 0
+            groupingInfos[0].groupOpenCount++
+            // we don't always close a group when emitting this node, so we need this flag to
+            // mark if we need to close a group
+            groupingInfos[index].shouldCloseGroup = true
+          } else {
+            // use this index in to open future groups for arrays and postfixes
+            // we will also stop grouping field access to the beginning of the expression
+            lastIndexToOpen = index
+          }
+        }
+        is KtArrayAccessExpression,
+        is KtPostfixExpression -> {
+          // we group these with the last item with a name, and we always close them
+          groupingInfos[lastIndexToOpen].groupOpenCount++
+        }
+      }
+    }
+    if (useBlockLikeLambdaStyle) {
+      // a trailing lambda adds a group that we stop before emitting the lambda
+      groupingInfos[0].groupOpenCount++
+    }
+    return groupingInfos
+  }
+
+  /** Decide whether a [KtQualifiedExpression] part should be grouped with the previous part */
+  private fun shouldGroupPartWithPrevious(
+      parts: List<KtExpression>,
+      part: KtExpression,
+      index: Int,
+      previous: KtExpression,
+      current: KtExpression
+  ): Boolean {
+    // this is the second, and the first is short, avoid `.` "hanging in air"
+    if (index == 1 && previous.text.length < options.continuationIndent) {
+      return true
+    }
+    // the previous part is `this` or `super`
+    if (previous is KtSuperExpression || previous is KtThisExpression) {
+      return true
+    }
+    // this and the previous part are a package name, type name, or property
+    if (previous is KtSimpleNameExpression &&
+        current is KtSimpleNameExpression &&
+        part is KtDotQualifiedExpression) {
+      return true
+    }
+    // this is `Foo` in `com.facebook.Foo`, so everything before it is a package name
+    if (current.text.first().isUpperCase() &&
+        current is KtSimpleNameExpression &&
+        part is KtDotQualifiedExpression) {
+      return true
+    }
+    // this is the `foo()` in `com.facebook.Foo.foo()` or in `Foo.foo()`
+    if (current is KtCallExpression &&
+        (previous !is KtCallExpression) &&
+        previous.text?.firstOrNull()?.isUpperCase() == true) {
+      return true
+    }
+    // this is an invocation and the last item, and the previous it not, i.e. `a.b.c()`
+    // keeping it grouped and splitting the arguments makes `a.b(...)` feel like `aab()`
+    return current is KtCallExpression &&
+        previous !is KtCallExpression &&
+        index == parts.indices.last
+  }
+
+  /** Returns true if the expression represents an invocation that is also a lambda */
+  private fun KtExpression.isLambda(): Boolean {
+    return extractCallExpression(this)?.lambdaArguments?.isNotEmpty() ?: false
+  }
+
+  /**
+   * emitQualifiedExpression formats call expressions that are either part of a qualified
+   * expression, or standing alone. This method makes it easier to handle both cases uniformly.
+   */
+  private fun extractCallExpression(expression: KtExpression): KtCallExpression? {
+    val ktExpression = (expression as? KtQualifiedExpression)?.selectorExpression ?: expression
+    return ktExpression as? KtCallExpression
+  }
+
+  override fun visitCallExpression(callExpression: KtCallExpression) {
+    builder.sync(callExpression)
+    with(callExpression) {
+      visitCallElement(
+          calleeExpression,
+          typeArgumentList,
+          valueArgumentList,
+          lambdaArguments,
+          lambdaIndent = ZERO)
+    }
+  }
+
+  /** Examples `foo<T>(a, b)`, `foo(a)`, `boo()`, `super(a)` */
+  private fun visitCallElement(
+      callee: KtExpression?,
+      typeArgumentList: KtTypeArgumentList?,
+      argumentList: KtValueArgumentList?,
+      lambdaArguments: List<KtLambdaArgument>,
+      argumentsIndent: Indent = expressionBreakIndent,
+      lambdaIndent: Indent = ZERO
+  ) {
+    builder.block(ZERO) {
+      visit(callee)
+      val arguments = argumentList?.arguments.orEmpty()
+      builder.block(argumentsIndent) { visit(typeArgumentList) }
+      builder.block(argumentsIndent) {
+        if (argumentList != null) {
+          builder.token("(")
+        }
+        if (arguments.isNotEmpty()) {
+          if (isGoogleStyle) {
+            visit(argumentList)
+            val first = arguments.first()
+            if (arguments.size != 1 ||
+                first?.isNamed() != false ||
+                first.getArgumentExpression() !is KtLambdaExpression) {
+              builder.breakOp(Doc.FillMode.UNIFIED, "", expressionBreakNegativeIndent)
+            }
+          } else {
+            builder.block(ZERO) { visit(argumentList) }
+          }
+        }
+        if (argumentList != null) {
+          builder.token(")")
+        }
+      }
+      val hasTrailingComma = argumentList?.trailingComma != null
+      if (lambdaArguments.isNotEmpty()) {
+        builder.space()
+        builder.block(lambdaIndent) {
+          lambdaArguments.forEach {
+            visitArgumentInternal(it, forceBreakLambdaBody = hasTrailingComma)
+          }
+        }
+      }
+    }
+  }
+
+  /** Example (`1, "hi"`) in a function call */
+  override fun visitValueArgumentList(list: KtValueArgumentList) {
+    builder.sync(list)
+    val arguments = list.arguments
+    val isSingleUnnamedLambda =
+        arguments.size == 1 &&
+            arguments.first().getArgumentExpression() is KtLambdaExpression &&
+            arguments.first().getArgumentName() == null
+    if (isSingleUnnamedLambda) {
+      builder.block(expressionBreakNegativeIndent) {
+        visit(arguments.first())
+        if (list.trailingComma != null) {
+          builder.token(",")
+        }
+      }
+    } else {
+      // Break before args.
+      builder.breakOp(Doc.FillMode.UNIFIED, "", ZERO)
+      emitParameterLikeList(
+          list.arguments, list.trailingComma != null, wrapInBlock = !isGoogleStyle)
+    }
+  }
+
+  /** Example `{ 1 + 1 }` (as lambda) or `{ (x, y) -> x + y }` */
+  override fun visitLambdaExpression(lambdaExpression: KtLambdaExpression) {
+    visitLambdaExpressionInternal(lambdaExpression, brokeBeforeBrace = null, forceBreakBody = false)
+  }
+
+  /**
+   * The internal version of [visitLambdaExpression].
+   *
+   * @param brokeBeforeBrace used for tracking if a break was taken right before the lambda
+   * expression. Useful for scoping functions where we want good looking indentation. For example,
+   * here we have correct indentation before `bar()` and `car()` because we can detect the break
+   * after the equals:
+   * ```
+   * fun foo() =
+   *     coroutineScope { x ->
+   *       bar()
+   *       car()
+   *     }
+   * ```
+   * @param forceBreakBody if true, forces the lambda to be multi-line. Useful for call expressions
+   * where it would look weird for the lambda to be on one-line. For example, here we avoid
+   * one-lining `{ x = 0 }` since the parameters have a trailing comma:
+   * ```
+   * foo.bar(
+   *   trailingComma,
+   * ) {
+   *   x = 0
+   * }
+   * ```
+   */
+  private fun visitLambdaExpressionInternal(
+      lambdaExpression: KtLambdaExpression,
+      brokeBeforeBrace: BreakTag?,
+      forceBreakBody: Boolean,
+  ) {
+    builder.sync(lambdaExpression)
+
+    val valueParams = lambdaExpression.valueParameters
+    val hasParams = valueParams.isNotEmpty()
+    val statements = (lambdaExpression.bodyExpression ?: fail()).children
+    val hasStatements = statements.isNotEmpty()
+    val hasArrow = lambdaExpression.functionLiteral.arrow != null
+
+    fun ifBrokeBeforeBrace(onTrue: Indent, onFalse: Indent): Indent {
+      if (brokeBeforeBrace == null) return onFalse
+      return Indent.If.make(brokeBeforeBrace, onTrue, onFalse)
+    }
+
+    /**
+     * Enable correct formatting of the `fun foo() = scope {` syntax.
+     *
+     * We can't denote the lambda (+ scope function) as a block, since (for multiline lambdas) the
+     * rectangle rule would force the entire lambda onto a lower line. Instead, we conditionally
+     * indent all the interior levels of the lambda based on whether we had to break before the
+     * opening brace (or scope function). This mimics the look of a block when the break is taken.
+     *
+     * These conditional indents should not be used inside interior blocks, since that would apply
+     * the condition twice.
+     */
+    val bracePlusBlockIndent = ifBrokeBeforeBrace(blockPlusExpressionBreakIndent, blockIndent)
+    val bracePlusExpressionIndent =
+        ifBrokeBeforeBrace(doubleExpressionBreakIndent, expressionBreakIndent)
+    val bracePlusZeroIndent = ifBrokeBeforeBrace(expressionBreakIndent, ZERO)
+
+    builder.token("{")
+
+    if (hasParams || hasArrow) {
+      builder.space()
+      builder.block(bracePlusExpressionIndent) {
+        forEachCommaSeparated(valueParams) { it.accept(this) }
+      }
+      builder.block(bracePlusBlockIndent) {
+        if (lambdaExpression.functionLiteral.valueParameterList?.trailingComma != null) {
+          builder.token(",")
+          builder.forcedBreak()
+        } else if (hasParams) {
+          builder.breakOp(Doc.FillMode.INDEPENDENT, " ", ZERO)
+        }
+        builder.token("->")
+      }
+      builder.breakOp(Doc.FillMode.UNIFIED, "", bracePlusZeroIndent)
+    }
+
+    if (forceBreakBody) {
+      builder.forcedBreak()
+    }
+
+    if (hasStatements) {
+      builder.breakOp(Doc.FillMode.UNIFIED, " ", bracePlusBlockIndent)
+      builder.block(bracePlusBlockIndent) {
+        builder.blankLineWanted(OpsBuilder.BlankLineWanted.NO)
+        if (statements.size == 1 &&
+            statements.first() !is KtReturnExpression &&
+            lambdaExpression.bodyExpression?.startsWithComment() != true) {
+          visitStatement(statements[0])
+        } else {
+          visitStatements(statements)
+        }
+      }
+    }
+
+    if (hasParams || hasArrow || hasStatements) {
+      // If we had to break in the body, ensure there is a break before the closing brace
+      builder.breakOp(Doc.FillMode.UNIFIED, " ", bracePlusZeroIndent)
+      builder.blankLineWanted(OpsBuilder.BlankLineWanted.NO)
+    }
+    builder.block(bracePlusZeroIndent) {
+      // If there are closing comments, make sure they and the brace are indented together
+      // The comments will indent themselves, so consume the previous break as a blank line
+      builder.breakOp(Doc.FillMode.INDEPENDENT, "", ZERO)
+      builder.token("}", blockIndent)
+    }
+  }
+
+  /** Example `this` or `this@Foo` */
+  override fun visitThisExpression(expression: KtThisExpression) {
+    builder.sync(expression)
+    builder.token("this")
+    visit(expression.getTargetLabel())
+  }
+
+  /** Example `Foo` or `@Foo` */
+  override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) {
+    builder.sync(expression)
+    when (expression) {
+      is KtLabelReferenceExpression -> {
+        if (expression.text[0] == '@') {
+          builder.token("@")
+          builder.token(expression.getIdentifier()?.text ?: fail())
+        } else {
+          builder.token(expression.getIdentifier()?.text ?: fail())
+          builder.token("@")
+        }
+      }
+      else -> {
+        if (expression.text.isNotEmpty()) {
+          builder.token(expression.text)
+        }
+      }
+    }
+  }
+
+  /** e.g., `a: Int, b: Int, c: Int` in `fun foo(a: Int, b: Int, c: Int) { ... }`. */
+  override fun visitParameterList(list: KtParameterList) {
+    emitParameterLikeList(list.parameters, list.trailingComma != null, wrapInBlock = false)
+  }
+
+  /**
+   * Emit a list of elements that look like function parameters or arguments, e.g., `a, b, c` in
+   * `foo(a, b, c)`
+   */
+  private fun <T : PsiElement> emitParameterLikeList(
+      list: List<T>?,
+      hasTrailingComma: Boolean,
+      wrapInBlock: Boolean
+  ) {
+    if (list.isNullOrEmpty()) {
+      return
+    }
+
+    forEachCommaSeparated(list, hasTrailingComma, wrapInBlock, trailingBreak = isGoogleStyle) {
+      visit(it)
+    }
+    if (hasTrailingComma) {
+      builder.breakOp(Doc.FillMode.UNIFIED, "", expressionBreakNegativeIndent)
+    }
+  }
+
+  /**
+   * Call `function` for each element in `list`, with comma (,) tokens inbetween.
+   *
+   * Example:
+   * ```
+   * a, b, c, 3, 4, 5
+   * ```
+   *
+   * Either the entire list fits in one line, or each element is put on its own line:
+   * ```
+   * a,
+   * b,
+   * c,
+   * 3,
+   * 4,
+   * 5
+   * ```
+   *
+   * @param hasTrailingComma if true, each element is placed on its own line (even if they could've
+   * fit in a single line), and a trailing comma is emitted.
+   *
+   * Example:
+   * ```
+   * a,
+   * b,
+   * ```
+   */
+  private fun <T> forEachCommaSeparated(
+      list: Iterable<T>,
+      hasTrailingComma: Boolean = false,
+      wrapInBlock: Boolean = true,
+      trailingBreak: Boolean = false,
+      function: (T) -> Unit
+  ) {
+    if (hasTrailingComma) {
+      builder.block(ZERO) {
+        builder.forcedBreak()
+        for (value in list) {
+          function(value)
+          builder.token(",")
+          builder.forcedBreak()
+        }
+      }
+      return
+    }
+
+    builder.block(ZERO, isEnabled = wrapInBlock) {
+      var first = true
+      builder.breakOp(Doc.FillMode.UNIFIED, "", ZERO)
+      for (value in list) {
+        if (!first) {
+          builder.token(",")
+          builder.breakOp(Doc.FillMode.UNIFIED, " ", ZERO)
+        }
+        first = false
+
+        function(value)
+      }
+    }
+    if (trailingBreak) {
+      builder.breakOp(Doc.FillMode.UNIFIED, "", expressionBreakNegativeIndent)
+    }
+  }
+
+  /** Example `a` in `foo(a)`, or `*a`, or `limit = 50` */
+  override fun visitArgument(argument: KtValueArgument) {
+    visitArgumentInternal(argument, forceBreakLambdaBody = false)
+  }
+
+  /**
+   * The internal version of [visitArgument].
+   *
+   * @param forceBreakLambdaBody if true (and [argument] is of type [KtLambdaExpression]), forces
+   * the lambda to be multi-line. See documentation of [visitLambdaExpressionInternal] for an
+   * example.
+   */
+  private fun visitArgumentInternal(
+      argument: KtValueArgument,
+      forceBreakLambdaBody: Boolean,
+  ) {
+    builder.sync(argument)
+    val hasArgName = argument.getArgumentName() != null
+    val isLambda = argument.getArgumentExpression() is KtLambdaExpression
+    builder.block(ZERO) {
+      if (hasArgName) {
+        visit(argument.getArgumentName())
+        builder.space()
+        builder.token("=")
+        if (isLambda) {
+          builder.space()
+        }
+      }
+      builder.block(if (hasArgName && !isLambda) expressionBreakIndent else ZERO) {
+        if (hasArgName && !isLambda) {
+          builder.breakOp(Doc.FillMode.INDEPENDENT, " ", ZERO)
+        }
+        if (argument.isSpread) {
+          builder.token("*")
+        }
+        if (isLambda) {
+          visitLambdaExpressionInternal(
+              argument.getArgumentExpression() as KtLambdaExpression,
+              brokeBeforeBrace = null,
+              forceBreakBody = forceBreakLambdaBody,
+          )
+        } else {
+          visit(argument.getArgumentExpression())
+        }
+      }
+    }
+  }
+
+  override fun visitReferenceExpression(expression: KtReferenceExpression) {
+    builder.sync(expression)
+    builder.token(expression.text)
+  }
+
+  override fun visitReturnExpression(expression: KtReturnExpression) {
+    builder.sync(expression)
+    builder.token("return")
+    visit(expression.getTargetLabel())
+    val returnedExpression = expression.returnedExpression
+    if (returnedExpression != null) {
+      builder.space()
+      visit(returnedExpression)
+    }
+    builder.guessToken(";")
+  }
+
+  /**
+   * For example `a + b`, `a + b + c` or `a..b`
+   *
+   * The extra handling here drills to the left most expression and handles it for long chains of
+   * binary expressions that are formatted not accordingly to the associative values That is, we
+   * want to think of `a + b + c` as `(a + b) + c`, whereas the AST parses it as `a + (b + c)`
+   */
+  override fun visitBinaryExpression(expression: KtBinaryExpression) {
+    builder.sync(expression)
+    val op = expression.operationToken
+
+    if (KtTokens.ALL_ASSIGNMENTS.contains(op) && isLambdaOrScopingFunction(expression.right)) {
+      // Assignments are statements in Kotlin; we don't have to worry about compound assignment.
+      visit(expression.left)
+      builder.space()
+      builder.token(expression.operationReference.text)
+      visitLambdaOrScopingFunction(expression.right)
+      return
+    }
+
+    val parts =
+        ArrayDeque<KtBinaryExpression>().apply {
+          var current: KtExpression? = expression
+          while (current is KtBinaryExpression && current.operationToken == op) {
+            addFirst(current)
+            current = current.left
+          }
+        }
+
+    val leftMostExpression = parts.first()
+    visit(leftMostExpression.left)
+    for (leftExpression in parts) {
+      when (leftExpression.operationToken) {
+        KtTokens.RANGE -> {}
+        KtTokens.ELVIS -> builder.breakOp(Doc.FillMode.INDEPENDENT, " ", expressionBreakIndent)
+        else -> builder.space()
+      }
+      builder.token(leftExpression.operationReference.text)
+      val isFirst = leftExpression === leftMostExpression
+      if (isFirst) {
+        builder.open(expressionBreakIndent)
+      }
+      when (leftExpression.operationToken) {
+        KtTokens.RANGE -> {}
+        KtTokens.ELVIS -> builder.space()
+        else -> builder.breakOp(Doc.FillMode.UNIFIED, " ", ZERO)
+      }
+      visit(leftExpression.right)
+    }
+    builder.close()
+  }
+
+  override fun visitPostfixExpression(expression: KtPostfixExpression) {
+    builder.sync(expression)
+    builder.block(ZERO) {
+      val baseExpression = expression.baseExpression
+      val operator = expression.operationReference.text
+
+      visit(baseExpression)
+      if (baseExpression is KtPostfixExpression &&
+          baseExpression.operationReference.text.last() == operator.first()) {
+        builder.space()
+      }
+      builder.token(operator)
+    }
+  }
+
+  override fun visitPrefixExpression(expression: KtPrefixExpression) {
+    builder.sync(expression)
+    builder.block(ZERO) {
+      val baseExpression = expression.baseExpression
+      val operator = expression.operationReference.text
+
+      builder.token(operator)
+      if (baseExpression is KtPrefixExpression &&
+          operator.last() == baseExpression.operationReference.text.first()) {
+        builder.space()
+      }
+      visit(baseExpression)
+    }
+  }
+
+  override fun visitLabeledExpression(expression: KtLabeledExpression) {
+    builder.sync(expression)
+    visit(expression.labelQualifier)
+    if (expression.baseExpression !is KtLambdaExpression) {
+      builder.space()
+    }
+    visit(expression.baseExpression)
+  }
+
+  internal enum class DeclarationKind {
+    FIELD,
+    PARAMETER
+  }
+
+  /**
+   * Declare one variable or variable-like thing.
+   *
+   * Examples:
+   * - `var a: Int = 5`
+   * - `a: Int`
+   * - `private val b:
+   */
+  private fun declareOne(
+      kind: DeclarationKind,
+      modifiers: KtModifierList?,
+      valOrVarKeyword: String?,
+      typeParameters: KtTypeParameterList? = null,
+      receiver: KtTypeReference? = null,
+      name: String?,
+      type: KtTypeReference?,
+      typeConstraintList: KtTypeConstraintList? = null,
+      initializer: KtExpression?,
+      delegate: KtPropertyDelegate? = null,
+      accessors: List<KtPropertyAccessor>? = null
+  ): Int {
+    val verticalAnnotationBreak = genSym()
+
+    val isField = kind == DeclarationKind.FIELD
+
+    if (isField) {
+      builder.blankLineWanted(OpsBuilder.BlankLineWanted.conditional(verticalAnnotationBreak))
+    }
+
+    visit(modifiers)
+    builder.block(ZERO) {
+      builder.block(ZERO) {
+        if (valOrVarKeyword != null) {
+          builder.token(valOrVarKeyword)
+          builder.space()
+        }
+
+        if (typeParameters != null) {
+          visit(typeParameters)
+          builder.space()
+        }
+
+        // conditionally indent the name and initializer +4 if the type spans
+        // multiple lines
+        if (name != null) {
+          if (receiver != null) {
+            visit(receiver)
+            builder.token(".")
+          }
+          builder.token(name)
+          builder.op("")
+        }
+      }
+
+      if (name != null) {
+        builder.open(expressionBreakIndent) // open block for named values
+      }
+      // For example `: String` in `val thisIsALongName: String` or `fun f(): String`
+      if (type != null) {
+        if (name != null) {
+          builder.token(":")
+          builder.breakOp(Doc.FillMode.UNIFIED, " ", ZERO)
+        }
+        visit(type)
+      }
+    }
+
+    // For example `where T : Int` in a generic method
+    if (typeConstraintList != null) {
+      builder.space()
+      visit(typeConstraintList)
+      builder.space()
+    }
+
+    // for example `by lazy { compute() }`
+    if (delegate != null) {
+      builder.space()
+      builder.token("by")
+      if (isLambdaOrScopingFunction(delegate.expression)) {
+        builder.space()
+        visit(delegate)
+      } else {
+        builder.breakOp(Doc.FillMode.UNIFIED, " ", expressionBreakIndent)
+        builder.block(expressionBreakIndent) { visit(delegate) }
+      }
+    } else if (initializer != null) {
+      builder.space()
+      builder.token("=")
+      if (isLambdaOrScopingFunction(initializer)) {
+        visitLambdaOrScopingFunction(initializer)
+      } else {
+        builder.breakOp(Doc.FillMode.UNIFIED, " ", expressionBreakIndent)
+        builder.block(expressionBreakIndent) { visit(initializer) }
+      }
+    }
+    if (name != null) {
+      builder.close() // close block for named values
+    }
+    // for example `private set` or `get = 2 * field`
+    if (accessors?.isNotEmpty() == true) {
+      builder.block(blockIndent) {
+        for (accessor in accessors) {
+          builder.forcedBreak()
+          // The semicolon must come after the newline, or the output code will not parse.
+          builder.guessToken(";")
+
+          builder.block(ZERO) {
+            visitFunctionLikeExpression(
+                accessor.modifierList,
+                accessor.namePlaceholder.text,
+                null,
+                null,
+                null,
+                accessor.bodyExpression != null || accessor.bodyBlockExpression != null,
+                accessor.parameterList,
+                null,
+                accessor.bodyBlockExpression,
+                accessor.bodyExpression,
+                accessor.returnTypeReference,
+                accessor.bodyBlockExpression?.lBrace != null)
+          }
+        }
+      }
+    }
+
+    builder.guessToken(";")
+
+    if (isField) {
+      builder.blankLineWanted(OpsBuilder.BlankLineWanted.conditional(verticalAnnotationBreak))
+    }
+
+    return 0
+  }
+
+  /**
+   * Returns whether an expression is a lambda or initializer expression in which case we will want
+   * to avoid indenting the lambda block
+   *
+   * Examples:
+   * 1. '... = { ... }' is a lambda expression
+   * 2. '... = Runnable { ... }' is considered a scoping function
+   * 3. '... = scope { ... }' '... = apply { ... }' is a scoping function
+   *
+   * but not:
+   * 1. '... = foo() { ... }' due to the empty parenthesis
+   * 2. '... = Runnable @Annotation { ... }' due to the annotation
+   */
+  private fun isLambdaOrScopingFunction(expression: KtExpression?): Boolean {
+    if (expression is KtLambdaExpression) {
+      return true
+    }
+    if (expression is KtCallExpression &&
+        expression.valueArgumentList?.leftParenthesis == null &&
+        expression.lambdaArguments.isNotEmpty() &&
+        expression.typeArgumentList?.arguments.isNullOrEmpty() &&
+        expression.lambdaArguments.first().getArgumentExpression() is KtLambdaExpression) {
+      return true
+    }
+    return false
+  }
+
+  /** See [isLambdaOrScopingFunction] for examples. */
+  private fun visitLambdaOrScopingFunction(expr: PsiElement?) {
+    val breakToExpr = genSym()
+    builder.breakOp(Doc.FillMode.INDEPENDENT, " ", expressionBreakIndent, Optional.of(breakToExpr))
+
+    val lambdaExpression =
+        when (expr) {
+          is KtLambdaExpression -> expr
+          is KtCallExpression -> {
+            visit(expr.calleeExpression)
+            builder.space()
+            expr.lambdaArguments[0].getLambdaExpression() ?: fail()
+          }
+          else -> throw AssertionError(expr)
+        }
+
+    visitLambdaExpressionInternal(
+        lambdaExpression,
+        brokeBeforeBrace = breakToExpr,
+        forceBreakBody = false,
+    )
+  }
+
+  override fun visitClassOrObject(classOrObject: KtClassOrObject) {
+    builder.sync(classOrObject)
+    val modifierList = classOrObject.modifierList
+    builder.block(ZERO) {
+      if (modifierList != null) {
+        visitModifierList(modifierList)
+      }
+      val declarationKeyword = classOrObject.getDeclarationKeyword()
+      if (declarationKeyword != null) {
+        builder.token(declarationKeyword.text ?: fail())
+      }
+      val name = classOrObject.nameIdentifier
+      if (name != null) {
+        builder.space()
+        builder.token(name.text)
+        visit(classOrObject.typeParameterList)
+      }
+      visit(classOrObject.primaryConstructor)
+      val superTypes = classOrObject.getSuperTypeList()
+      if (superTypes != null) {
+        builder.space()
+        builder.block(ZERO) {
+          builder.token(":")
+          builder.breakOp(Doc.FillMode.UNIFIED, " ", expressionBreakIndent)
+          visit(superTypes)
+        }
+      }
+      builder.space()
+      val typeConstraintList = classOrObject.typeConstraintList
+      if (typeConstraintList != null) {
+        if (superTypes?.entries?.lastOrNull() is KtDelegatedSuperTypeEntry) {
+          builder.forcedBreak()
+        }
+        visit(typeConstraintList)
+        builder.space()
+      }
+      val body = classOrObject.body
+      if (classOrObject.hasModifier(KtTokens.ENUM_KEYWORD)) {
+        visitEnumBody(classOrObject as KtClass)
+      } else if (body != null) {
+        visitBlockBody(body, true)
+      }
+    }
+    if (classOrObject.nameIdentifier != null) {
+      builder.forcedBreak()
+    }
+  }
+
+  /** Example `{ RED, GREEN; fun foo() { ... } }` for an enum class */
+  private fun visitEnumBody(enumClass: KtClass) {
+    val body = enumClass.body
+    if (body == null) {
+      return
+    }
+    builder.token("{", Doc.Token.RealOrImaginary.REAL, blockIndent, Optional.of(blockIndent))
+    builder.open(ZERO)
+    builder.block(blockIndent) {
+      builder.breakOp(Doc.FillMode.UNIFIED, "", ZERO)
+      val (enumEntries, nonEnumEntryStatements) = body.children.partition { it is KtEnumEntry }
+      builder.forcedBreak()
+      visitEnumEntries(enumEntries)
+
+      if (nonEnumEntryStatements.isNotEmpty()) {
+        builder.forcedBreak()
+        builder.blankLineWanted(OpsBuilder.BlankLineWanted.PRESERVE)
+        visitStatements(nonEnumEntryStatements.toTypedArray())
+      }
+    }
+    builder.forcedBreak()
+    builder.blankLineWanted(OpsBuilder.BlankLineWanted.NO)
+    builder.token("}", blockIndent)
+    builder.close()
+  }
+
+  /** Example `RED, GREEN, BLUE,` in an enum class, or `RED, GREEN;` */
+  private fun visitEnumEntries(enumEntries: List<PsiElement>) {
+    builder.block(ZERO) {
+      builder.breakOp(Doc.FillMode.UNIFIED, "", ZERO)
+      for (value in enumEntries) {
+        visit(value)
+        if (builder.peekToken() == Optional.of(",")) {
+          builder.token(",")
+          builder.forcedBreak()
+        }
+      }
+    }
+    builder.guessToken(";")
+  }
+
+  override fun visitPrimaryConstructor(constructor: KtPrimaryConstructor) {
+    builder.sync(constructor)
+    builder.block(ZERO) {
+      if (constructor.hasConstructorKeyword()) {
+        builder.open(ZERO)
+        builder.breakOp(Doc.FillMode.UNIFIED, " ", ZERO)
+        visit(constructor.modifierList)
+        builder.token("constructor")
+      }
+
+      builder.block(ZERO) {
+        builder.token("(")
+        builder.block(expressionBreakIndent) {
+          builder.breakOp(Doc.FillMode.UNIFIED, "", expressionBreakIndent)
+          visit(constructor.valueParameterList)
+          builder.breakOp(Doc.FillMode.UNIFIED, "", expressionBreakNegativeIndent)
+          if (constructor.hasConstructorKeyword()) {
+            builder.close()
+          }
+        }
+        builder.token(")")
+      }
+    }
+  }
+
+  /** Example `private constructor(n: Int) : this(4, 5) { ... }` inside a class's body */
+  override fun visitSecondaryConstructor(constructor: KtSecondaryConstructor) {
+    val delegationCall = constructor.getDelegationCall()
+    val bodyExpression = constructor.bodyExpression
+
+    builder.sync(constructor)
+
+    visitFunctionLikeExpression(
+        constructor.modifierList,
+        "constructor",
+        null,
+        null,
+        null,
+        true,
+        constructor.valueParameterList,
+        null,
+        bodyExpression,
+        null,
+        if (!delegationCall.isImplicit) delegationCall else null,
+        true)
+  }
+
+  override fun visitConstructorDelegationCall(call: KtConstructorDelegationCall) {
+    // Work around a misfeature in kotlin-compiler: call.calleeExpression.accept doesn't call
+    // visitReferenceExpression, but calls visitElement instead.
+    builder.block(ZERO) {
+      builder.token(if (call.isCallToThis) "this" else "super")
+      visitCallElement(
+          null,
+          call.typeArgumentList,
+          call.valueArgumentList,
+          call.lambdaArguments,
+          lambdaIndent = ZERO)
+    }
+  }
+
+  override fun visitClassInitializer(initializer: KtClassInitializer) {
+    builder.sync(initializer)
+    builder.token("init")
+    builder.space()
+    visit(initializer.body)
+  }
+
+  override fun visitConstantExpression(expression: KtConstantExpression) {
+    builder.sync(expression)
+    builder.token(expression.text)
+  }
+
+  /** Example `(1 + 1)` */
+  override fun visitParenthesizedExpression(expression: KtParenthesizedExpression) {
+    builder.sync(expression)
+    builder.token("(")
+    visit(expression.expression)
+    builder.token(")")
+  }
+
+  override fun visitPackageDirective(directive: KtPackageDirective) {
+    builder.sync(directive)
+    if (directive.packageKeyword == null) {
+      return
+    }
+    builder.token("package")
+    builder.space()
+    var first = true
+    for (packageName in directive.packageNames) {
+      if (first) {
+        first = false
+      } else {
+        builder.token(".")
+      }
+      builder.token(packageName.getIdentifier()?.text ?: packageName.getReferencedName())
+    }
+
+    builder.guessToken(";")
+    builder.forcedBreak()
+  }
+
+  /** Example `import com.foo.A; import com.bar.B` */
+  override fun visitImportList(importList: KtImportList) {
+    builder.sync(importList)
+    importList.imports.forEach { visit(it) }
+  }
+
+  /** Example `import com.foo.A` */
+  override fun visitImportDirective(directive: KtImportDirective) {
+    builder.sync(directive)
+    builder.token("import")
+    builder.space()
+
+    val importedReference = directive.importedReference
+    if (importedReference != null) {
+      inImport = true
+      visit(importedReference)
+      inImport = false
+    }
+    if (directive.isAllUnder) {
+      builder.token(".")
+      builder.token("*")
+    }
+
+    // Possible alias.
+    val alias = directive.alias?.nameIdentifier
+    if (alias != null) {
+      builder.space()
+      builder.token("as")
+      builder.space()
+      builder.token(alias.text ?: fail())
+    }
+
+    // Force a newline afterwards.
+    builder.guessToken(";")
+    builder.forcedBreak()
+  }
+
+  /** For example `@Magic private final` */
+  override fun visitModifierList(list: KtModifierList) {
+    builder.sync(list)
+    var onlyAnnotationsSoFar = true
+
+    for (child in list.node.children()) {
+      val psi = child.psi
+      if (psi is PsiWhiteSpace) {
+        continue
+      }
+
+      if (child.elementType is KtModifierKeywordToken) {
+        onlyAnnotationsSoFar = false
+        builder.token(child.text)
+      } else {
+        visit(psi)
+      }
+
+      if (onlyAnnotationsSoFar) {
+        builder.breakOp(Doc.FillMode.UNIFIED, " ", ZERO)
+      } else {
+        builder.space()
+      }
+    }
+  }
+
+  /**
+   * Example:
+   * ```
+   * @SuppressLint("MagicNumber")
+   * print(10)
+   * ```
+   *
+   * in
+   *
+   * ```
+   * fun f() {
+   *   @SuppressLint("MagicNumber")
+   *   print(10)
+   * }
+   * ```
+   */
+  override fun visitAnnotatedExpression(expression: KtAnnotatedExpression) {
+    builder.sync(expression)
+    builder.block(ZERO) {
+      val baseExpression = expression.baseExpression
+
+      builder.block(ZERO) {
+        val annotationEntries = expression.annotationEntries
+        for (annotationEntry in annotationEntries) {
+          if (annotationEntry !== annotationEntries.first()) {
+            builder.breakOp(Doc.FillMode.UNIFIED, " ", ZERO)
+          }
+          visit(annotationEntry)
+        }
+      }
+
+      // Binary expressions in a block have a different meaning according to their formatting.
+      // If there in the line above, they refer to the entire expression, if they're in the same
+      // line then only to the first operand of the operator.
+      // We force a break to avoid such semantic changes
+      when {
+        (baseExpression is KtBinaryExpression || baseExpression is KtBinaryExpressionWithTypeRHS) &&
+            expression.parent is KtBlockExpression -> builder.forcedBreak()
+        baseExpression is KtLambdaExpression -> builder.space()
+        else -> builder.breakOp(Doc.FillMode.UNIFIED, " ", ZERO)
+      }
+
+      visit(expression.baseExpression)
+    }
+  }
+
+  /**
+   * For example, @field:[Inject Named("WEB_VIEW")]
+   *
+   * A KtAnnotation is used only to group multiple annotations with the same use-site-target. It
+   * only appears in a modifier list since annotated expressions do not have use-site-targets.
+   */
+  override fun visitAnnotation(annotation: KtAnnotation) {
+    builder.sync(annotation)
+    builder.block(ZERO) {
+      builder.token("@")
+      val useSiteTarget = annotation.useSiteTarget
+      if (useSiteTarget != null) {
+        visit(useSiteTarget)
+        builder.token(":")
+      }
+      builder.block(expressionBreakIndent) {
+        builder.token("[")
+
+        builder.block(ZERO) {
+          var first = true
+          builder.breakOp(Doc.FillMode.UNIFIED, "", ZERO)
+          for (value in annotation.entries) {
+            if (!first) {
+              builder.breakOp(Doc.FillMode.UNIFIED, " ", ZERO)
+            }
+            first = false
+
+            visit(value)
+          }
+        }
+      }
+      builder.token("]")
+    }
+    builder.forcedBreak()
+  }
+
+  /** For example, 'field' in @field:[Inject Named("WEB_VIEW")] */
+  override fun visitAnnotationUseSiteTarget(
+      annotationTarget: KtAnnotationUseSiteTarget,
+      data: Void?
+  ): Void? {
+    builder.token(annotationTarget.getAnnotationUseSiteTarget().renderName)
+    return null
+  }
+
+  /** For example `@Magic` or `@Fred(1, 5)` */
+  override fun visitAnnotationEntry(annotationEntry: KtAnnotationEntry) {
+    builder.sync(annotationEntry)
+    if (annotationEntry.atSymbol != null) {
+      builder.token("@")
+    }
+    val useSiteTarget = annotationEntry.useSiteTarget
+    if (useSiteTarget != null && useSiteTarget.parent == annotationEntry) {
+      visit(useSiteTarget)
+      builder.token(":")
+    }
+    visitCallElement(
+        annotationEntry.calleeExpression,
+        null, // Type-arguments are included in the annotation's callee expression.
+        annotationEntry.valueArgumentList,
+        listOf())
+  }
+
+  override fun visitFileAnnotationList(
+      fileAnnotationList: KtFileAnnotationList,
+      data: Void?
+  ): Void? {
+    for (child in fileAnnotationList.node.children()) {
+      if (child is PsiElement) {
+        continue
+      }
+      visit(child.psi)
+      builder.forcedBreak()
+    }
+
+    return null
+  }
+
+  override fun visitSuperTypeList(list: KtSuperTypeList) {
+    builder.sync(list)
+    builder.block(expressionBreakIndent) { forEachCommaSeparated(list.entries) { visit(it) } }
+  }
+
+  override fun visitSuperTypeCallEntry(call: KtSuperTypeCallEntry) {
+    builder.sync(call)
+    visitCallElement(call.calleeExpression, null, call.valueArgumentList, call.lambdaArguments)
+  }
+
+  /**
+   * Example `Collection<Int> by list` in `class MyList(list: List<Int>) : Collection<Int> by list`
+   */
+  override fun visitDelegatedSuperTypeEntry(specifier: KtDelegatedSuperTypeEntry) {
+    builder.sync(specifier)
+    visit(specifier.typeReference)
+    builder.space()
+    builder.token("by")
+    builder.space()
+    visit(specifier.delegateExpression)
+  }
+
+  override fun visitWhenExpression(expression: KtWhenExpression) {
+    builder.sync(expression)
+    builder.block(ZERO) {
+      emitKeywordWithCondition("when", expression.subjectExpression)
+
+      builder.space()
+      builder.token("{", Doc.Token.RealOrImaginary.REAL, blockIndent, Optional.of(blockIndent))
+
+      expression.entries.forEach { whenEntry ->
+        builder.block(blockIndent) {
+          builder.forcedBreak()
+          if (whenEntry.isElse) {
+            builder.token("else")
+          } else {
+            builder.block(ZERO) {
+              val conditions = whenEntry.conditions
+              for ((index, condition) in conditions.withIndex()) {
+                visit(condition)
+                builder.guessToken(",")
+                if (index != conditions.lastIndex) {
+                  builder.forcedBreak()
+                }
+              }
+            }
+          }
+          val whenExpression = whenEntry.expression
+          builder.space()
+          builder.token("->")
+          if (whenExpression is KtBlockExpression) {
+            builder.space()
+            visit(whenExpression)
+          } else {
+            builder.block(expressionBreakIndent) {
+              builder.breakOp(Doc.FillMode.INDEPENDENT, " ", ZERO)
+              visit(whenExpression)
+            }
+          }
+          builder.guessToken(";")
+        }
+        builder.forcedBreak()
+      }
+      builder.token("}")
+    }
+  }
+
+  override fun visitBlockExpression(expression: KtBlockExpression) {
+    builder.sync(expression)
+    visitBlockBody(expression, true)
+  }
+
+  override fun visitWhenConditionWithExpression(condition: KtWhenConditionWithExpression) {
+    builder.sync(condition)
+    visit(condition.expression)
+  }
+
+  override fun visitWhenConditionIsPattern(condition: KtWhenConditionIsPattern) {
+    builder.sync(condition)
+    builder.token(if (condition.isNegated) "!is" else "is")
+    builder.space()
+    visit(condition.typeReference)
+  }
+
+  /** Example `in 1..2` as part of a when expression */
+  override fun visitWhenConditionInRange(condition: KtWhenConditionInRange) {
+    builder.sync(condition)
+    // TODO: replace with 'condition.isNegated' once https://youtrack.jetbrains.com/issue/KT-34395
+    // is fixed.
+    val isNegated = condition.firstChild?.node?.findChildByType(KtTokens.NOT_IN) != null
+    builder.token(if (isNegated) "!in" else "in")
+    builder.space()
+    visit(condition.rangeExpression)
+  }
+
+  override fun visitIfExpression(expression: KtIfExpression) {
+    builder.sync(expression)
+    builder.block(ZERO) {
+      emitKeywordWithCondition("if", expression.condition)
+
+      if (expression.then is KtBlockExpression) {
+        builder.space()
+        builder.block(ZERO) { visit(expression.then) }
+      } else {
+        builder.breakOp(Doc.FillMode.INDEPENDENT, " ", expressionBreakIndent)
+        builder.block(expressionBreakIndent) { visit(expression.then) }
+      }
+
+      if (expression.elseKeyword != null) {
+        if (expression.then is KtBlockExpression) {
+          builder.space()
+        } else {
+          builder.breakOp(Doc.FillMode.UNIFIED, " ", ZERO)
+        }
+
+        builder.block(ZERO) {
+          builder.token("else")
+          if (expression.`else` is KtBlockExpression || expression.`else` is KtIfExpression) {
+            builder.space()
+            builder.block(ZERO) { visit(expression.`else`) }
+          } else {
+            builder.breakOp(Doc.FillMode.INDEPENDENT, " ", expressionBreakIndent)
+            builder.block(expressionBreakIndent) { visit(expression.`else`) }
+          }
+        }
+      }
+    }
+  }
+
+  /** Example `a[3]`, `b["a", 5]` or `a.b.c[4]` */
+  override fun visitArrayAccessExpression(expression: KtArrayAccessExpression) {
+    builder.sync(expression)
+    if (expression.arrayExpression is KtQualifiedExpression) {
+      emitQualifiedExpression(expression)
+    } else {
+      visit(expression.arrayExpression)
+      visitArrayAccessBrackets(expression)
+    }
+  }
+
+  /**
+   * Example `[3]` in `a[3]` or `a[3].b` Separated since it needs to be used from a top level array
+   * expression (`a[3]`) and from within a qualified chain (`a[3].b)
+   */
+  private fun visitArrayAccessBrackets(expression: KtArrayAccessExpression) {
+    builder.block(ZERO) {
+      builder.token("[")
+      builder.breakOp(Doc.FillMode.UNIFIED, "", expressionBreakIndent)
+      builder.block(expressionBreakIndent) {
+        emitParameterLikeList(
+            expression.indexExpressions, expression.trailingComma != null, wrapInBlock = true)
+      }
+    }
+    builder.token("]")
+  }
+
+  /** Example `val (a, b: Int) = Pair(1, 2)` */
+  override fun visitDestructuringDeclaration(destructuringDeclaration: KtDestructuringDeclaration) {
+    builder.sync(destructuringDeclaration)
+    val valOrVarKeyword = destructuringDeclaration.valOrVarKeyword
+    if (valOrVarKeyword != null) {
+      builder.token(valOrVarKeyword.text)
+      builder.space()
+    }
+    val hasTrailingComma = destructuringDeclaration.trailingComma != null
+    builder.block(ZERO) {
+      builder.token("(")
+      builder.breakOp(Doc.FillMode.UNIFIED, "", expressionBreakIndent)
+      builder.block(expressionBreakIndent) {
+        emitParameterLikeList(
+            destructuringDeclaration.entries, hasTrailingComma, wrapInBlock = true)
+      }
+    }
+    builder.token(")")
+    val initializer = destructuringDeclaration.initializer
+    if (initializer != null) {
+      builder.space()
+      builder.token("=")
+      if (hasTrailingComma) {
+        builder.space()
+      } else {
+        builder.breakOp(Doc.FillMode.INDEPENDENT, " ", expressionBreakIndent)
+      }
+      builder.block(expressionBreakIndent, !hasTrailingComma) { visit(initializer) }
+    }
+  }
+
+  /** Example `a: String` which is part of `(a: String, b: String)` */
+  override fun visitDestructuringDeclarationEntry(
+      multiDeclarationEntry: KtDestructuringDeclarationEntry
+  ) {
+    builder.sync(multiDeclarationEntry)
+    declareOne(
+        initializer = null,
+        kind = DeclarationKind.PARAMETER,
+        modifiers = multiDeclarationEntry.modifierList,
+        name = multiDeclarationEntry.nameIdentifier?.text ?: fail(),
+        type = multiDeclarationEntry.typeReference,
+        valOrVarKeyword = null,
+    )
+  }
+
+  /** Example `"Hello $world!"` or `"""Hello world!"""` */
+  override fun visitStringTemplateExpression(expression: KtStringTemplateExpression) {
+    builder.sync(expression)
+    builder.token(WhitespaceTombstones.replaceTrailingWhitespaceWithTombstone(expression.text))
+  }
+
+  /** Example `super` in `super.doIt(5)` or `super<Foo>` in `super<Foo>.doIt(5)` */
+  override fun visitSuperExpression(expression: KtSuperExpression) {
+    builder.sync(expression)
+    builder.token("super")
+    val superTypeQualifier = expression.superTypeQualifier
+    if (superTypeQualifier != null) {
+      builder.token("<")
+      visit(superTypeQualifier)
+      builder.token(">")
+    }
+    visit(expression.labelQualifier)
+  }
+
+  /** Example `<T, S>` */
+  override fun visitTypeParameterList(list: KtTypeParameterList) {
+    builder.sync(list)
+    builder.block(ZERO) {
+      builder.token("<")
+      val parameters = list.parameters
+      if (parameters.isNotEmpty()) {
+        // Break before args.
+        builder.breakOp(Doc.FillMode.UNIFIED, "", expressionBreakIndent)
+        builder.block(expressionBreakIndent) {
+          emitParameterLikeList(list.parameters, list.trailingComma != null, wrapInBlock = true)
+        }
+      }
+      builder.token(">")
+    }
+  }
+
+  override fun visitTypeParameter(parameter: KtTypeParameter) {
+    builder.sync(parameter)
+    visit(parameter.modifierList)
+    builder.token(parameter.nameIdentifier?.text ?: "")
+    val extendsBound = parameter.extendsBound
+    if (extendsBound != null) {
+      builder.space()
+      builder.token(":")
+      builder.space()
+      visit(extendsBound)
+    }
+  }
+
+  /** Example `where T : View, T : Listener` */
+  override fun visitTypeConstraintList(list: KtTypeConstraintList) {
+    builder.token("where")
+    builder.space()
+    builder.sync(list)
+    forEachCommaSeparated(list.constraints) { visit(it) }
+  }
+
+  /** Example `T : Foo` */
+  override fun visitTypeConstraint(constraint: KtTypeConstraint) {
+    builder.sync(constraint)
+    // TODO(nreid260): What about annotations on the type reference? `where @A T : Int`
+    visit(constraint.subjectTypeParameterName)
+    builder.space()
+    builder.token(":")
+    builder.space()
+    visit(constraint.boundTypeReference)
+  }
+
+  /** Example `for (i in items) { ... }` */
+  override fun visitForExpression(expression: KtForExpression) {
+    builder.sync(expression)
+    builder.block(ZERO) {
+      builder.token("for")
+      builder.space()
+      builder.token("(")
+      visit(expression.loopParameter)
+      builder.space()
+      builder.token("in")
+      builder.block(ZERO) {
+        builder.breakOp(Doc.FillMode.UNIFIED, " ", expressionBreakIndent)
+        builder.block(expressionBreakIndent) { visit(expression.loopRange) }
+      }
+      builder.token(")")
+      builder.space()
+      visit(expression.body)
+    }
+  }
+
+  /** Example `while (a < b) { ... }` */
+  override fun visitWhileExpression(expression: KtWhileExpression) {
+    builder.sync(expression)
+    emitKeywordWithCondition("while", expression.condition)
+    builder.space()
+    visit(expression.body)
+  }
+
+  /** Example `do { ... } while (a < b)` */
+  override fun visitDoWhileExpression(expression: KtDoWhileExpression) {
+    builder.sync(expression)
+    builder.token("do")
+    builder.space()
+    if (expression.body != null) {
+      visit(expression.body)
+      builder.space()
+    }
+    emitKeywordWithCondition("while", expression.condition)
+  }
+
+  /** Example `break` or `break@foo` in a loop */
+  override fun visitBreakExpression(expression: KtBreakExpression) {
+    builder.sync(expression)
+    builder.token("break")
+    visit(expression.labelQualifier)
+  }
+
+  /** Example `continue` or `continue@foo` in a loop */
+  override fun visitContinueExpression(expression: KtContinueExpression) {
+    builder.sync(expression)
+    builder.token("continue")
+    visit(expression.labelQualifier)
+  }
+
+  /** Example `f: String`, or `private val n: Int` or `(a: Int, b: String)` (in for-loops) */
+  override fun visitParameter(parameter: KtParameter) {
+    builder.sync(parameter)
+    builder.block(ZERO) {
+      val destructuringDeclaration = parameter.destructuringDeclaration
+      val typeReference = parameter.typeReference
+      if (destructuringDeclaration != null) {
+        builder.block(ZERO) {
+          visit(destructuringDeclaration)
+          if (typeReference != null) {
+            builder.token(":")
+            builder.space()
+            visit(typeReference)
+          }
+        }
+      } else {
+        declareOne(
+            kind = DeclarationKind.PARAMETER,
+            modifiers = parameter.modifierList,
+            valOrVarKeyword = parameter.valOrVarKeyword?.text,
+            name = parameter.nameIdentifier?.text,
+            type = typeReference,
+            initializer = parameter.defaultValue)
+      }
+    }
+  }
+
+  /** Example `String::isNullOrEmpty` */
+  override fun visitCallableReferenceExpression(expression: KtCallableReferenceExpression) {
+    builder.sync(expression)
+    visit(expression.receiverExpression)
+
+    // For some reason, expression.receiverExpression doesn't contain the question-mark token in
+    // case of a nullable type, e.g., in String?::isNullOrEmpty.
+    // Instead, KtCallableReferenceExpression exposes a method that looks for the QUEST token in
+    // its children.
+    if (expression.hasQuestionMarks) {
+      builder.token("?")
+    }
+
+    builder.block(expressionBreakIndent) {
+      builder.token("::")
+      builder.breakOp(Doc.FillMode.INDEPENDENT, "", ZERO)
+      visit(expression.callableReference)
+    }
+  }
+
+  override fun visitClassLiteralExpression(expression: KtClassLiteralExpression) {
+    builder.sync(expression)
+    val receiverExpression = expression.receiverExpression
+    if (receiverExpression is KtCallExpression) {
+      visitCallElement(
+          receiverExpression.calleeExpression,
+          receiverExpression.typeArgumentList,
+          receiverExpression.valueArgumentList,
+          receiverExpression.lambdaArguments)
+    } else {
+      visit(receiverExpression)
+    }
+    builder.token("::")
+    builder.token("class")
+  }
+
+  override fun visitFunctionType(type: KtFunctionType) {
+    builder.sync(type)
+    val receiver = type.receiver
+    if (receiver != null) {
+      visit(receiver)
+      builder.token(".")
+    }
+    builder.block(expressionBreakIndent) {
+      builder.token("(")
+      visit(type.parameterList)
+    }
+    builder.token(")")
+    builder.space()
+    builder.token("->")
+    builder.space()
+    builder.block(expressionBreakIndent) { visit(type.returnTypeReference) }
+  }
+
+  /** Example `a is Int` or `b !is Int` */
+  override fun visitIsExpression(expression: KtIsExpression) {
+    builder.sync(expression)
+    val openGroupBeforeLeft = expression.leftHandSide !is KtQualifiedExpression
+    if (openGroupBeforeLeft) builder.open(ZERO)
+    visit(expression.leftHandSide)
+    if (!openGroupBeforeLeft) builder.open(ZERO)
+    val parent = expression.parent
+    if (parent is KtValueArgument ||
+        parent is KtParenthesizedExpression ||
+        parent is KtContainerNode) {
+      builder.breakOp(Doc.FillMode.UNIFIED, " ", expressionBreakIndent)
+    } else {
+      builder.space()
+    }
+    visit(expression.operationReference)
+    builder.breakOp(Doc.FillMode.INDEPENDENT, " ", expressionBreakIndent)
+    builder.block(expressionBreakIndent) { visit(expression.typeReference) }
+    builder.close()
+  }
+
+  /** Example `a as Int` or `a as? Int` */
+  override fun visitBinaryWithTypeRHSExpression(expression: KtBinaryExpressionWithTypeRHS) {
+    builder.sync(expression)
+    val openGroupBeforeLeft = expression.left !is KtQualifiedExpression
+    if (openGroupBeforeLeft) builder.open(ZERO)
+    visit(expression.left)
+    if (!openGroupBeforeLeft) builder.open(ZERO)
+    builder.breakOp(Doc.FillMode.UNIFIED, " ", expressionBreakIndent)
+    visit(expression.operationReference)
+    builder.breakOp(Doc.FillMode.INDEPENDENT, " ", expressionBreakIndent)
+    builder.block(expressionBreakIndent) { visit(expression.right) }
+    builder.close()
+  }
+
+  /**
+   * Example:
+   * ```
+   * fun f() {
+   *   val a: Array<Int> = [1, 2, 3]
+   * }
+   * ```
+   */
+  override fun visitCollectionLiteralExpression(expression: KtCollectionLiteralExpression) {
+    builder.sync(expression)
+    builder.block(ZERO) {
+      builder.token("[")
+      builder.breakOp(Doc.FillMode.UNIFIED, "", expressionBreakIndent)
+      builder.block(expressionBreakIndent) {
+        emitParameterLikeList(
+            expression.getInnerExpressions(), expression.trailingComma != null, wrapInBlock = true)
+      }
+    }
+    builder.token("]")
+  }
+
+  override fun visitTryExpression(expression: KtTryExpression) {
+    builder.sync(expression)
+    builder.token("try")
+    builder.space()
+    visit(expression.tryBlock)
+    for (catchClause in expression.catchClauses) {
+      visit(catchClause)
+    }
+    visit(expression.finallyBlock)
+  }
+
+  override fun visitCatchSection(catchClause: KtCatchClause) {
+    builder.sync(catchClause)
+    builder.space()
+    builder.token("catch")
+    builder.space()
+    builder.block(ZERO) {
+      builder.token("(")
+      builder.block(expressionBreakIndent) {
+        builder.breakOp(Doc.FillMode.UNIFIED, "", ZERO)
+        visit(catchClause.catchParameter)
+        builder.guessToken(",")
+      }
+    }
+    builder.token(")")
+    builder.space()
+    visit(catchClause.catchBody)
+  }
+
+  override fun visitFinallySection(finallySection: KtFinallySection) {
+    builder.sync(finallySection)
+    builder.space()
+    builder.token("finally")
+    builder.space()
+    visit(finallySection.finalExpression)
+  }
+
+  override fun visitThrowExpression(expression: KtThrowExpression) {
+    builder.sync(expression)
+    builder.token("throw")
+    builder.space()
+    visit(expression.thrownExpression)
+  }
+
+  /** Example `RED(0xFF0000)` in an enum class */
+  override fun visitEnumEntry(enumEntry: KtEnumEntry) {
+    builder.sync(enumEntry)
+    builder.block(ZERO) {
+      visit(enumEntry.modifierList)
+      builder.token(enumEntry.nameIdentifier?.text ?: fail())
+      enumEntry.initializerList?.initializers?.forEach { visit(it) }
+      val body = enumEntry.body
+      if (body != null) {
+        builder.space()
+        visitBlockBody(body, true)
+      }
+    }
+  }
+
+  /** Example `private typealias TextChangedListener = (string: String) -> Unit` */
+  override fun visitTypeAlias(typeAlias: KtTypeAlias) {
+    builder.sync(typeAlias)
+    builder.block(ZERO) {
+      visit(typeAlias.modifierList)
+      builder.token("typealias")
+      builder.space()
+      builder.token(typeAlias.nameIdentifier?.text ?: fail())
+      visit(typeAlias.typeParameterList)
+
+      builder.space()
+      builder.token("=")
+      builder.breakOp(Doc.FillMode.INDEPENDENT, " ", expressionBreakIndent)
+      builder.block(expressionBreakIndent) {
+        visit(typeAlias.getTypeReference())
+        visit(typeAlias.typeConstraintList)
+        builder.guessToken(";")
+      }
+      builder.forcedBreak()
+    }
+  }
+
+  /**
+   * visitElement is called for almost all types of AST nodes. We use it to keep track of whether
+   * we're currently inside an expression or not.
+   *
+   * @throws FormattingError
+   */
+  override fun visitElement(element: PsiElement) {
+    inExpression.addLast(element is KtExpression || inExpression.peekLast())
+    val previous = builder.depth()
+    try {
+      super.visitElement(element)
+    } catch (e: FormattingError) {
+      throw e
+    } catch (t: Throwable) {
+      throw FormattingError(builder.diagnostic(Throwables.getStackTraceAsString(t)))
+    } finally {
+      inExpression.removeLast()
+    }
+    builder.checkClosed(previous)
+  }
+
+  override fun visitKtFile(file: KtFile) {
+    markForPartialFormat()
+    var importListEmpty = false
+    var isFirst = true
+    for (child in file.children) {
+      if (child.text.isBlank()) {
+        importListEmpty = child is KtImportList
+        continue
+      }
+      if (!isFirst && child !is PsiComment && (child !is KtScript || !importListEmpty)) {
+        builder.blankLineWanted(OpsBuilder.BlankLineWanted.YES)
+      }
+      visit(child)
+      isFirst = false
+    }
+    markForPartialFormat()
+  }
+
+  override fun visitScript(script: KtScript) {
+    markForPartialFormat()
+    var lastChildHadBlankLineBefore = false
+    var first = true
+    for (child in script.blockExpression.children) {
+      if (child.text.isBlank()) {
+        continue
+      }
+      builder.forcedBreak()
+      val childGetsBlankLineBefore = child !is KtProperty
+      if (first) {
+        builder.blankLineWanted(OpsBuilder.BlankLineWanted.PRESERVE)
+      } else if (child !is PsiComment &&
+          (childGetsBlankLineBefore || lastChildHadBlankLineBefore)) {
+        builder.blankLineWanted(OpsBuilder.BlankLineWanted.YES)
+      }
+      visit(child)
+      builder.guessToken(";")
+      lastChildHadBlankLineBefore = childGetsBlankLineBefore
+      first = false
+    }
+    markForPartialFormat()
+  }
+
+  private fun inExpression(): Boolean {
+    return inExpression.peekLast()
+  }
+
+  /**
+   * markForPartialFormat is used to delineate the smallest areas of code that must be formatted
+   * together.
+   *
+   * When only parts of the code are being formatted, the requested area is expanded until it's
+   * covered by an area marked by this method.
+   */
+  private fun markForPartialFormat() {
+    if (!inExpression()) {
+      builder.markForPartialFormat()
+    }
+  }
+
+  /**
+   * Emit a [Doc.Token].
+   *
+   * @param token the [String] to wrap in a [Doc.Token]
+   * @param plusIndentCommentsBefore extra block for comments before this token
+   */
+  private fun OpsBuilder.token(token: String, plusIndentCommentsBefore: Indent = ZERO) {
+    token(
+        token,
+        Doc.Token.RealOrImaginary.REAL,
+        plusIndentCommentsBefore,
+        /* breakAndIndentTrailingComment */ Optional.empty())
+  }
+
+  /**
+   * Opens a new level, emits into it and closes it.
+   *
+   * This is a helper method to make it easier to keep track of [OpsBuilder.open] and
+   * [OpsBuilder.close] calls
+   *
+   * @param plusIndent the block level to pass to the block
+   * @param block a code block to be run in this block level
+   */
+  private inline fun OpsBuilder.block(
+      plusIndent: Indent,
+      isEnabled: Boolean = true,
+      block: () -> Unit
+  ) {
+    if (isEnabled) {
+      open(plusIndent)
+    }
+    block()
+    if (isEnabled) {
+      close()
+    }
+  }
+
+  /** Helper method to sync the current offset to match any element in the AST */
+  private fun OpsBuilder.sync(psiElement: PsiElement) {
+    sync(psiElement.startOffset)
+  }
+
+  /**
+   * Throws a formatting error
+   *
+   * This is used as `expr ?: fail()` to avoid using the !! operator and provide better error
+   * messages.
+   */
+  private fun fail(message: String = "Unexpected"): Nothing {
+    throw FormattingError(builder.diagnostic(message))
+  }
+
+  /** Helper function to improve readability */
+  private fun visit(element: PsiElement?) {
+    element?.accept(this)
+  }
+
+  /** Emits a key word followed by a condition, e.g. `if (b)` or `while (c < d )` */
+  private fun emitKeywordWithCondition(keyword: String, condition: KtExpression?) {
+    if (condition == null) {
+      builder.token(keyword)
+      return
+    }
+
+    builder.block(ZERO) {
+      builder.token(keyword)
+      builder.space()
+      builder.token("(")
+      if (isGoogleStyle) {
+        builder.block(expressionBreakIndent) {
+          builder.breakOp(Doc.FillMode.UNIFIED, "", ZERO)
+          visit(condition)
+          builder.breakOp(Doc.FillMode.UNIFIED, "", expressionBreakNegativeIndent)
+        }
+      } else {
+        builder.block(ZERO) { visit(condition) }
+      }
+    }
+    builder.token(")")
+  }
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/format/KotlinTok.kt b/core/src/main/java/com/facebook/ktfmt/format/KotlinTok.kt
new file mode 100644
index 0000000..d03a7e4
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/format/KotlinTok.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.facebook.ktfmt.format
+
+import com.google.common.base.MoreObjects
+import com.google.googlejavaformat.Input
+import com.google.googlejavaformat.Newlines
+import org.jetbrains.kotlin.lexer.KtToken
+
+class KotlinTok(
+    private val index: Int,
+    private val originalText: String,
+    private val text: String,
+    private val position: Int,
+    private val columnI: Int,
+    val isToken: Boolean,
+    private val kind: KtToken
+) : Input.Tok {
+
+  override fun getIndex(): Int = index
+
+  override fun getText(): String = text
+
+  override fun getOriginalText(): String = originalText
+
+  override fun length(): Int = originalText.length
+
+  override fun getPosition(): Int = position
+
+  override fun getColumn(): Int = columnI
+
+  override fun isNewline(): Boolean = Newlines.isNewline(text)
+
+  override fun isSlashSlashComment(): Boolean = text.startsWith("//")
+
+  override fun isSlashStarComment(): Boolean = text.startsWith("/*")
+
+  override fun isJavadocComment(): Boolean = text.startsWith("/**") && text.length > 4
+
+  override fun isComment(): Boolean = isSlashSlashComment || isSlashStarComment
+
+  fun kind(): KtToken = kind
+
+  override fun toString(): String {
+    return MoreObjects.toStringHelper(this)
+        .add("index", index)
+        .add("text", text)
+        .add("position", position)
+        .add("columnI", columnI)
+        .add("isToken", isToken)
+        .toString()
+  }
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/format/KotlinToken.kt b/core/src/main/java/com/facebook/ktfmt/format/KotlinToken.kt
new file mode 100644
index 0000000..eacc431
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/format/KotlinToken.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.facebook.ktfmt.format
+
+import com.google.common.base.MoreObjects
+import com.google.common.collect.ImmutableList
+import com.google.googlejavaformat.Input
+
+class KotlinToken(
+    private val toksBefore: ImmutableList<KotlinTok>,
+    private val kotlinTok: KotlinTok,
+    private val toksAfter: ImmutableList<KotlinTok>
+) : Input.Token {
+
+  override fun getTok(): KotlinTok = kotlinTok
+
+  override fun getToksBefore(): ImmutableList<out Input.Tok> = toksBefore
+
+  override fun getToksAfter(): ImmutableList<out Input.Tok> = toksAfter
+
+  override fun toString(): String {
+    return MoreObjects.toStringHelper(this)
+        .add("tok", kotlinTok)
+        .add("toksBefore", toksBefore)
+        .add("toksAfter", toksAfter)
+        .toString()
+  }
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/format/ParseError.kt b/core/src/main/java/com/facebook/ktfmt/format/ParseError.kt
new file mode 100644
index 0000000..216dfb6
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/format/ParseError.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.facebook.ktfmt.format
+
+import org.jetbrains.kotlin.com.intellij.openapi.util.text.LineColumn
+
+class ParseError(val errorDescription: String, val lineColumn: LineColumn) :
+    IllegalArgumentException(
+        "${lineColumn.line + 1}:${lineColumn.column + 1}: error: $errorDescription")
diff --git a/core/src/main/java/com/facebook/ktfmt/format/Parser.kt b/core/src/main/java/com/facebook/ktfmt/format/Parser.kt
new file mode 100644
index 0000000..d8e4791
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/format/Parser.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.facebook.ktfmt.format
+
+import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys
+import org.jetbrains.kotlin.cli.common.messages.MessageRenderer.PLAIN_RELATIVE_PATHS
+import org.jetbrains.kotlin.cli.common.messages.PrintingMessageCollector
+import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
+import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
+import org.jetbrains.kotlin.com.intellij.openapi.util.Disposer
+import org.jetbrains.kotlin.com.intellij.openapi.util.text.StringUtil
+import org.jetbrains.kotlin.com.intellij.psi.PsiErrorElement
+import org.jetbrains.kotlin.com.intellij.psi.PsiManager
+import org.jetbrains.kotlin.com.intellij.testFramework.LightVirtualFile
+import org.jetbrains.kotlin.config.CompilerConfiguration
+import org.jetbrains.kotlin.idea.KotlinFileType
+import org.jetbrains.kotlin.psi.KtFile
+import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
+import org.jetbrains.kotlin.psi.psiUtil.startOffset
+
+/** Parser parses a Kotlin file given as a string and returns its parse tree. */
+object Parser {
+
+  /**
+   * The environment used to open a KtFile
+   *
+   * We allocate it once, and ignore the Diposable. This causes a memory leak, but it was also
+   * causing a worse memory leak before when we created new ones and disposed them. This leak comes
+   * from [KotlinCoreEnvironment.createForProduction]:
+   * https://github.com/JetBrains/kotlin/blob/master/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt#L544
+   */
+  val env: KotlinCoreEnvironment
+
+  init {
+    // To hide annoying warning on Windows
+    System.setProperty("idea.use.native.fs.for.win", "false")
+    val disposable = Disposer.newDisposable()
+    val configuration = CompilerConfiguration()
+    configuration.put(
+        CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY,
+        PrintingMessageCollector(System.err, PLAIN_RELATIVE_PATHS, false))
+    env =
+        KotlinCoreEnvironment.createForProduction(
+            disposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES)
+  }
+
+  fun parse(code: String): KtFile {
+    val virtualFile = LightVirtualFile("temp.kts", KotlinFileType.INSTANCE, code)
+    val ktFile = PsiManager.getInstance(env.project).findFile(virtualFile) as KtFile
+    ktFile.collectDescendantsOfType<PsiErrorElement>().let {
+      if (it.isNotEmpty()) throwParseError(code, it[0])
+    }
+    return ktFile
+  }
+
+  private fun throwParseError(fileContents: String, error: PsiErrorElement): Nothing {
+    throw ParseError(
+        error.errorDescription, StringUtil.offsetToLineColumn(fileContents, error.startOffset))
+  }
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/format/RedundantElementRemover.kt b/core/src/main/java/com/facebook/ktfmt/format/RedundantElementRemover.kt
new file mode 100644
index 0000000..a073438
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/format/RedundantElementRemover.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.facebook.ktfmt.format
+
+import org.jetbrains.kotlin.com.intellij.psi.PsiElement
+import org.jetbrains.kotlin.kdoc.psi.impl.KDocImpl
+import org.jetbrains.kotlin.psi.KtImportList
+import org.jetbrains.kotlin.psi.KtPackageDirective
+import org.jetbrains.kotlin.psi.KtReferenceExpression
+import org.jetbrains.kotlin.psi.KtTreeVisitorVoid
+import org.jetbrains.kotlin.psi.psiUtil.endOffset
+import org.jetbrains.kotlin.psi.psiUtil.startOffset
+
+/** Removes elements that are not needed in the code, such as semicolons and unused imports. */
+object RedundantElementRemover {
+  /** Remove extra semicolons and unused imports, if enabled in the [options] */
+  fun dropRedundantElements(code: String, options: FormattingOptions): String {
+    val file = Parser.parse(code)
+    val redundantImportDetector = RedundantImportDetector(enabled = options.removeUnusedImports)
+    val redundantSemicolonDetector = RedundantSemicolonDetector()
+
+    file.accept(
+        object : KtTreeVisitorVoid() {
+          override fun visitElement(element: PsiElement) {
+            if (element is KDocImpl) {
+              redundantImportDetector.takeKdoc(element)
+            } else {
+              redundantSemicolonDetector.takeElement(element) { super.visitElement(element) }
+            }
+          }
+
+          override fun visitPackageDirective(directive: KtPackageDirective) {
+            redundantImportDetector.takePackageDirective(directive) {
+              super.visitPackageDirective(directive)
+            }
+          }
+
+          override fun visitImportList(importList: KtImportList) {
+            redundantImportDetector.takeImportList(importList) { super.visitImportList(importList) }
+          }
+
+          override fun visitReferenceExpression(expression: KtReferenceExpression) {
+            redundantImportDetector.takeReferenceExpression(expression)
+            super.visitReferenceExpression(expression)
+          }
+        })
+
+    val result = StringBuilder(code)
+    val elementsToRemove =
+        redundantSemicolonDetector.getRedundantSemicolonElements() +
+            redundantImportDetector.getRedundantImportElements()
+
+    for (element in elementsToRemove.sortedByDescending(PsiElement::endOffset)) {
+      result.replace(element.startOffset, element.endOffset, "")
+    }
+
+    return result.toString()
+  }
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/format/RedundantImportDetector.kt b/core/src/main/java/com/facebook/ktfmt/format/RedundantImportDetector.kt
new file mode 100644
index 0000000..7efa893
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/format/RedundantImportDetector.kt
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.facebook.ktfmt.format
+
+import org.jetbrains.kotlin.com.intellij.psi.PsiElement
+import org.jetbrains.kotlin.kdoc.psi.impl.KDocImpl
+import org.jetbrains.kotlin.kdoc.psi.impl.KDocLink
+import org.jetbrains.kotlin.kdoc.psi.impl.KDocName
+import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection
+import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag
+import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.psi.KtImportDirective
+import org.jetbrains.kotlin.psi.KtImportList
+import org.jetbrains.kotlin.psi.KtPackageDirective
+import org.jetbrains.kotlin.psi.KtReferenceExpression
+import org.jetbrains.kotlin.psi.psiUtil.getChildrenOfType
+
+internal class RedundantImportDetector(val enabled: Boolean) {
+  companion object {
+    private val OPERATORS =
+        setOf(
+            // Unary prefix operators
+            "unaryPlus",
+            "unaryMinus",
+            "not",
+            // Increments and decrements
+            "inc",
+            "dec",
+            // Arithmetic operators
+            "plus",
+            "minus",
+            "times",
+            "div",
+            "rem",
+            "mod", // deprecated
+            "rangeTo",
+            // 'In' operator
+            "contains",
+            // Indexed access operator
+            "get",
+            "set",
+            // Invoke operator
+            "invoke",
+            // Augmented assignments
+            "plusAssign",
+            "minusAssign",
+            "timesAssign",
+            "divAssign",
+            "remAssign",
+            "modAssign", // deprecated
+            // Equality and inequality operators
+            "equals",
+            // Comparison operators
+            "compareTo",
+            // Iterator operators
+            "iterator",
+            "next",
+            "hasNext",
+            // Bitwise operators
+            "and",
+            "or",
+            // Property delegation operators
+            "getValue",
+            "setValue",
+            "provideDelegate")
+
+    private val COMPONENT_OPERATOR_REGEX = Regex("component\\d+")
+
+    private val KDOC_TAG_SKIP_FIRST_REFERENCE_REGEX = Regex("^@(param|property) (.+)")
+  }
+
+  private var thisPackage: FqName? = null
+
+  private val usedReferences = OPERATORS.toMutableSet()
+
+  private lateinit var importCleanUpCandidates: Set<KtImportDirective>
+
+  private var isPackageElement = false
+  private var isImportElement = false
+
+  fun takePackageDirective(directive: KtPackageDirective, superBlock: () -> Unit) {
+    if (!enabled) {
+      return superBlock.invoke()
+    }
+
+    thisPackage = directive.fqName
+
+    isPackageElement = true
+    superBlock.invoke()
+    isPackageElement = false
+  }
+
+  fun takeImportList(importList: KtImportList, superBlock: () -> Unit) {
+    if (!enabled) {
+      return superBlock.invoke()
+    }
+
+    importCleanUpCandidates =
+        importList.imports
+            .filter { import ->
+              import.isValidImport &&
+                  !import.isAllUnder &&
+                  import.identifier != null &&
+                  requireNotNull(import.identifier) !in OPERATORS &&
+                  !COMPONENT_OPERATOR_REGEX.matches(import.identifier.orEmpty())
+            }
+            .toSet()
+
+    isImportElement = true
+    superBlock.invoke()
+    isImportElement = false
+  }
+
+  fun takeKdoc(kdoc: KDocImpl) {
+    kdoc.getChildrenOfType<KDocSection>().forEach { kdocSection ->
+      val tagLinks =
+          kdocSection.getChildrenOfType<KDocTag>().flatMap { tag ->
+            val tagLinks = tag.getChildrenOfType<KDocLink>().toList()
+            when {
+              KDOC_TAG_SKIP_FIRST_REFERENCE_REGEX.matches(tag.text) -> tagLinks.drop(1)
+              else -> tagLinks
+            }
+          }
+
+      val links = kdocSection.getChildrenOfType<KDocLink>() + tagLinks
+
+      val references =
+          links.flatMap { link ->
+            link.getChildrenOfType<KDocName>().mapNotNull {
+              it.getQualifiedName().firstOrNull()?.trim('[', ']')
+            }
+          }
+
+      usedReferences += references
+    }
+  }
+
+  fun takeReferenceExpression(expression: KtReferenceExpression) {
+    if (!enabled) return
+
+    if (!isPackageElement && !isImportElement && expression.children.isEmpty()) {
+      usedReferences += expression.text.trim('`')
+    }
+  }
+
+  fun getRedundantImportElements(): List<PsiElement> {
+    if (!enabled) return emptyList()
+
+    val redundantImports = mutableListOf<PsiElement>()
+
+    // Collect unused imports
+    for (import in importCleanUpCandidates) {
+      val isUnused = import.aliasName !in usedReferences && import.identifier !in usedReferences
+      val isFromSamePackage = import.importedFqName?.parent() == thisPackage && import.alias == null
+      if (isUnused || isFromSamePackage) {
+        redundantImports += import
+      }
+    }
+
+    return redundantImports
+  }
+
+  private inline val KtImportDirective.identifier: String?
+    get() = importPath?.importedName?.identifier?.trim('`')
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/format/RedundantSemicolonDetector.kt b/core/src/main/java/com/facebook/ktfmt/format/RedundantSemicolonDetector.kt
new file mode 100644
index 0000000..3f6601b
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/format/RedundantSemicolonDetector.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.facebook.ktfmt.format
+
+import org.jetbrains.kotlin.com.intellij.psi.PsiElement
+import org.jetbrains.kotlin.psi.KtContainerNodeForControlStructureBody
+import org.jetbrains.kotlin.psi.KtDeclaration
+import org.jetbrains.kotlin.psi.KtEnumEntry
+import org.jetbrains.kotlin.psi.KtIfExpression
+import org.jetbrains.kotlin.psi.KtLambdaExpression
+import org.jetbrains.kotlin.psi.KtStringTemplateEntry
+import org.jetbrains.kotlin.psi.KtStringTemplateExpression
+import org.jetbrains.kotlin.psi.KtWhileExpression
+import org.jetbrains.kotlin.psi.psiUtil.getNextSiblingIgnoringWhitespaceAndComments
+import org.jetbrains.kotlin.psi.psiUtil.getPrevSiblingIgnoringWhitespaceAndComments
+import org.jetbrains.kotlin.psi.psiUtil.prevLeaf
+import org.jetbrains.kotlin.psi.psiUtil.siblings
+
+internal class RedundantSemicolonDetector {
+  private val extraSemicolons = mutableListOf<PsiElement>()
+
+  fun getRedundantSemicolonElements(): List<PsiElement> = extraSemicolons
+
+  /** returns **true** if this element was an extra comma, **false** otherwise. */
+  fun takeElement(element: PsiElement, superBlock: () -> Unit) {
+    if (isExtraSemicolon(element)) {
+      extraSemicolons += element
+    } else {
+      superBlock.invoke()
+    }
+  }
+
+  private fun isExtraSemicolon(element: PsiElement): Boolean {
+    if (element.text != ";") {
+      return false
+    }
+
+    val parent = element.parent
+    if (parent is KtStringTemplateExpression || parent is KtStringTemplateEntry) {
+      return false
+    }
+    if (parent is KtEnumEntry &&
+        parent.siblings(forward = true, withItself = false).any { it is KtDeclaration }) {
+      return false
+    }
+
+    val prevLeaf = element.prevLeaf(false)
+    val prevConcreteSibling = element.getPrevSiblingIgnoringWhitespaceAndComments()
+    if ((prevConcreteSibling is KtIfExpression || prevConcreteSibling is KtWhileExpression) &&
+        prevLeaf is KtContainerNodeForControlStructureBody &&
+        prevLeaf.text.isEmpty()) {
+      return false
+    }
+
+    val nextConcreteSibling = element.getNextSiblingIgnoringWhitespaceAndComments()
+    if (nextConcreteSibling is KtLambdaExpression) {
+      /**
+       * Example: `val x = foo(0) ; { dead -> lambda }`
+       *
+       * There are a huge number of cases here because the trailing lambda syntax is so flexible.
+       * Therefore, we just assume that all semicolons followed by lambdas are meaningful. The cases
+       * where they could be removed are too rare to justify the risk of changing behaviour.
+       */
+      return false
+    }
+
+    return true
+  }
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/format/Tokenizer.kt b/core/src/main/java/com/facebook/ktfmt/format/Tokenizer.kt
new file mode 100644
index 0000000..ababba0
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/format/Tokenizer.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.facebook.ktfmt.format
+
+import java.util.regex.Pattern
+import org.jetbrains.kotlin.com.intellij.psi.PsiComment
+import org.jetbrains.kotlin.com.intellij.psi.PsiElement
+import org.jetbrains.kotlin.com.intellij.psi.PsiWhiteSpace
+import org.jetbrains.kotlin.com.intellij.psi.impl.source.tree.LeafPsiElement
+import org.jetbrains.kotlin.lexer.KtTokens
+import org.jetbrains.kotlin.psi.KtFile
+import org.jetbrains.kotlin.psi.KtStringTemplateExpression
+import org.jetbrains.kotlin.psi.KtTreeVisitorVoid
+import org.jetbrains.kotlin.psi.psiUtil.endOffset
+import org.jetbrains.kotlin.psi.psiUtil.startOffset
+
+/**
+ * Tokenizer traverses a Kotlin parse tree (which blessedly contains whitespaces and comments,
+ * unlike Javac) and constructs a list of 'Tok's.
+ *
+ * <p>The google-java-format infra expects newline Toks to be separate from maximal-whitespace Toks,
+ * but Kotlin emits them together. So, we split them using Java's \R regex matcher. We don't use
+ * 'split' et al. because we want Toks for the newlines themselves.
+ */
+class Tokenizer(private val fileText: String, val file: KtFile) : KtTreeVisitorVoid() {
+
+  companion object {
+    private val WHITESPACE_NEWLINE_REGEX: Pattern = Pattern.compile("\\R|( )+")
+  }
+
+  val toks = mutableListOf<KotlinTok>()
+  var index = 0
+
+  override fun visitElement(element: PsiElement) {
+    val startIndex = element.startOffset
+    when (element) {
+      is PsiComment -> {
+        toks.add(
+            KotlinTok(
+                index,
+                fileText.substring(startIndex, element.endOffset),
+                element.text,
+                startIndex,
+                0,
+                false,
+                KtTokens.EOF))
+        index++
+        return
+      }
+      is KtStringTemplateExpression -> {
+        toks.add(
+            KotlinTok(
+                index,
+                WhitespaceTombstones.replaceTrailingWhitespaceWithTombstone(
+                    fileText.substring(startIndex, element.endOffset)),
+                element.text,
+                startIndex,
+                0,
+                true,
+                KtTokens.EOF))
+        index++
+        return
+      }
+      is LeafPsiElement -> {
+        val elementText = element.text
+        val endIndex = element.endOffset
+        if (element is PsiWhiteSpace) {
+          val matcher = WHITESPACE_NEWLINE_REGEX.matcher(elementText)
+          while (matcher.find()) {
+            val text = matcher.group()
+            toks.add(
+                KotlinTok(
+                    -1,
+                    fileText.substring(startIndex + matcher.start(), startIndex + matcher.end()),
+                    text,
+                    startIndex + matcher.start(),
+                    0,
+                    false,
+                    KtTokens.EOF))
+          }
+        } else {
+          toks.add(
+              KotlinTok(
+                  index,
+                  fileText.substring(startIndex, endIndex),
+                  elementText,
+                  startIndex,
+                  0,
+                  true,
+                  KtTokens.EOF))
+          index++
+        }
+      }
+    }
+    super.visitElement(element)
+  }
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/format/TypeNameClassifier.kt b/core/src/main/java/com/facebook/ktfmt/format/TypeNameClassifier.kt
new file mode 100644
index 0000000..eba3ccc
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/format/TypeNameClassifier.kt
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+// This was copied from https://github.com/google/google-java-format and converted to Kotlin,
+// because the original is package-private.
+
+package com.facebook.ktfmt.format
+
+import com.google.common.base.Verify
+import java.util.Optional
+
+/** Heuristics for classifying qualified names as types. */
+object TypeNameClassifier {
+
+  /** A state machine for classifying qualified names. */
+  private enum class TyParseState(val isSingleUnit: Boolean) {
+
+    /** The start state. */
+    START(false) {
+      override fun next(n: JavaCaseFormat): TyParseState {
+        return when (n) {
+          JavaCaseFormat.UPPERCASE ->
+              // if we see an UpperCamel later, assume this was a class
+              // e.g. com.google.FOO.Bar
+              AMBIGUOUS
+          JavaCaseFormat.LOWER_CAMEL -> REJECT
+          JavaCaseFormat.LOWERCASE ->
+              // could be a package
+              START
+          JavaCaseFormat.UPPER_CAMEL -> TYPE
+        }
+      }
+    },
+
+    /** The current prefix is a type. */
+    TYPE(true) {
+      override fun next(n: JavaCaseFormat): TyParseState {
+        return when (n) {
+          JavaCaseFormat.UPPERCASE,
+          JavaCaseFormat.LOWER_CAMEL,
+          JavaCaseFormat.LOWERCASE -> FIRST_STATIC_MEMBER
+          JavaCaseFormat.UPPER_CAMEL -> TYPE
+        }
+      }
+    },
+
+    /** The current prefix is a type, followed by a single static member access. */
+    FIRST_STATIC_MEMBER(true) {
+      override fun next(n: JavaCaseFormat): TyParseState {
+        return REJECT
+      }
+    },
+
+    /** Anything not represented by one of the other states. */
+    REJECT(false) {
+      override fun next(n: JavaCaseFormat): TyParseState {
+        return REJECT
+      }
+    },
+
+    /** An ambiguous type prefix. */
+    AMBIGUOUS(false) {
+      override fun next(n: JavaCaseFormat): TyParseState {
+        return when (n) {
+          JavaCaseFormat.UPPERCASE -> AMBIGUOUS
+          JavaCaseFormat.LOWER_CAMEL,
+          JavaCaseFormat.LOWERCASE -> REJECT
+          JavaCaseFormat.UPPER_CAMEL -> TYPE
+        }
+      }
+    };
+
+    /** Transition function. */
+    abstract fun next(n: JavaCaseFormat): TyParseState
+  }
+
+  /**
+   * Returns the end index (inclusive) of the longest prefix that matches the naming conventions of
+   * a type or static field access, or -1 if no such prefix was found.
+   *
+   * Examples:
+   *
+   * * ClassName
+   * * ClassName.staticMemberName
+   * * com.google.ClassName.InnerClass.staticMemberName
+   */
+  internal fun typePrefixLength(nameParts: List<String>): Optional<Int> {
+    var state = TyParseState.START
+    var typeLength = Optional.empty<Int>()
+    for (i in nameParts.indices) {
+      state = state.next(JavaCaseFormat.from(nameParts[i]))
+      if (state === TyParseState.REJECT) {
+        break
+      }
+      if (state.isSingleUnit) {
+        typeLength = Optional.of(i)
+      }
+    }
+    return typeLength
+  }
+
+  /** Case formats used in Java identifiers. */
+  enum class JavaCaseFormat {
+    UPPERCASE,
+    LOWERCASE,
+    UPPER_CAMEL,
+    LOWER_CAMEL;
+
+    companion object {
+
+      /** Classifies an identifier's case format. */
+      internal fun from(name: String): JavaCaseFormat {
+        Verify.verify(name.isNotEmpty())
+        var firstUppercase = false
+        var hasUppercase = false
+        var hasLowercase = false
+        var first = true
+        for (char in name) {
+          if (!Character.isAlphabetic(char.code)) {
+            continue
+          }
+          if (first) {
+            firstUppercase = Character.isUpperCase(char)
+            first = false
+          }
+          hasUppercase = hasUppercase or Character.isUpperCase(char)
+          hasLowercase = hasLowercase or Character.isLowerCase(char)
+        }
+        return if (firstUppercase) {
+          if (hasLowercase) UPPER_CAMEL else UPPERCASE
+        } else {
+          if (hasUppercase) LOWER_CAMEL else LOWERCASE
+        }
+      }
+    }
+  }
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/format/WhitespaceTombstones.kt b/core/src/main/java/com/facebook/ktfmt/format/WhitespaceTombstones.kt
new file mode 100644
index 0000000..816e137
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/format/WhitespaceTombstones.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.facebook.ktfmt.format
+
+import java.util.regex.Pattern
+import java.util.regex.Pattern.MULTILINE
+
+object WhitespaceTombstones {
+  /** See [replaceTrailingWhitespaceWithTombstone]. */
+  const val SPACE_TOMBSTONE = '\u0003'
+
+  fun String.indexOfWhitespaceTombstone() = this.indexOf(SPACE_TOMBSTONE)
+
+  /**
+   * Google-java-format removes trailing spaces when it emits formatted code, which is a problem for
+   * multiline string literals. We trick it by replacing the last trailing space in such cases with
+   * a tombstone, a character that's unlikely to be used in a regular program. After formatting, we
+   * replace it back to a space.
+   */
+  fun replaceTrailingWhitespaceWithTombstone(s: String): String {
+    return Pattern.compile(" ($)", MULTILINE).matcher(s).replaceAll("$SPACE_TOMBSTONE$1")
+  }
+
+  /** See [replaceTrailingWhitespaceWithTombstone]. */
+  fun replaceTombstoneWithTrailingWhitespace(s: String): String {
+    return s.replace(SPACE_TOMBSTONE, ' ')
+  }
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/kdoc/Escaping.kt b/core/src/main/java/com/facebook/ktfmt/kdoc/Escaping.kt
new file mode 100644
index 0000000..85c9938
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/kdoc/Escaping.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.facebook.ktfmt.kdoc
+
+object Escaping {
+
+  private const val SLASH_STAR_ESCAPE = "\u0004\u0005"
+
+  private const val STAR_SLASH_ESCAPE = "\u0005\u0004"
+
+  fun indexOfCommentEscapeSequences(s: String) =
+      s.indexOfAny(listOf(SLASH_STAR_ESCAPE, STAR_SLASH_ESCAPE))
+
+  /**
+   * kotlin-compiler's KDoc lexer doesn't correctly handle nested slash-star comments, so we escape
+   * them into tombstones, format, then unescape.
+   */
+  fun escapeKDoc(s: String): String {
+    val startMarkerIndex = s.indexOf("/*")
+    val endMarkerIndex = s.lastIndexOf("*/")
+
+    if (startMarkerIndex == -1 || endMarkerIndex == -1) {
+      throw RuntimeException("KDoc with no /** and/or */")
+    }
+
+    return s.substring(0, startMarkerIndex + 3) +
+        s.substring(startMarkerIndex + 3, endMarkerIndex)
+            .replace("/*", SLASH_STAR_ESCAPE)
+            .replace("*/", STAR_SLASH_ESCAPE) +
+        s.substring(endMarkerIndex)
+  }
+
+  /**
+   *
+   * See [escapeKDoc].
+   */
+  fun unescapeKDoc(s: String): String =
+      s.replace(SLASH_STAR_ESCAPE, "/*").replace(STAR_SLASH_ESCAPE, "*/")
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/kdoc/KDocCommentsHelper.kt b/core/src/main/java/com/facebook/ktfmt/kdoc/KDocCommentsHelper.kt
new file mode 100644
index 0000000..a63a332
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/kdoc/KDocCommentsHelper.kt
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+/*
+ * This was copied from https://github.com/google/google-java-format and modified extensively to
+ * work for Kotlin formatting
+ */
+
+package com.facebook.ktfmt.kdoc
+
+import com.google.common.base.CharMatcher
+import com.google.common.base.Strings
+import com.google.googlejavaformat.CommentsHelper
+import com.google.googlejavaformat.Input.Tok
+import com.google.googlejavaformat.Newlines
+import com.google.googlejavaformat.java.Formatter
+import java.util.ArrayList
+import java.util.regex.Pattern
+
+/** `KDocCommentsHelper` extends [CommentsHelper] to rewrite KDoc comments. */
+class KDocCommentsHelper(private val lineSeparator: String, private val maxLineLength: Int) :
+    CommentsHelper {
+
+  override fun rewrite(tok: Tok, maxWidth: Int, column0: Int): String {
+    if (!tok.isComment) {
+      return tok.originalText
+    }
+    var text = tok.originalText
+    if (tok.isJavadocComment) {
+      text = KDocFormatter.formatKDoc(text, column0, maxLineLength)
+    }
+    val lines = ArrayList<String>()
+    val it = Newlines.lineIterator(text)
+    while (it.hasNext()) {
+      lines.add(CharMatcher.whitespace().trimTrailingFrom(it.next()))
+    }
+    return if (tok.isSlashSlashComment) {
+      indentLineComments(lines, column0)
+    } else if (javadocShaped(lines)) {
+      indentJavadoc(lines, column0)
+    } else {
+      preserveIndentation(lines, column0)
+    }
+  }
+
+  // For non-javadoc-shaped block comments, shift the entire block to the correct
+  // column, but do not adjust relative indentation.
+  private fun preserveIndentation(lines: List<String>, column0: Int): String {
+    val builder = StringBuilder()
+
+    // find the leftmost non-whitespace character in all trailing lines
+    var startCol = -1
+    for (i in 1 until lines.size) {
+      val lineIdx = CharMatcher.whitespace().negate().indexIn(lines[i])
+      if (lineIdx >= 0 && (startCol == -1 || lineIdx < startCol)) {
+        startCol = lineIdx
+      }
+    }
+
+    // output the first line at the current column
+    builder.append(lines[0])
+
+    // output all trailing lines with plausible indentation
+    for (i in 1 until lines.size) {
+      builder.append(lineSeparator).append(Strings.repeat(" ", column0))
+      // check that startCol is valid index, e.g. for blank lines
+      if (lines[i].length >= startCol) {
+        builder.append(lines[i].substring(startCol))
+      } else {
+        builder.append(lines[i])
+      }
+    }
+    return builder.toString()
+  }
+
+  // Wraps and re-indents line comments.
+  private fun indentLineComments(lines: List<String>, column0: Int): String {
+    val wrappedLines = wrapLineComments(lines, column0)
+    val builder = StringBuilder()
+    builder.append(wrappedLines[0].trim())
+    val indentString = Strings.repeat(" ", column0)
+    for (i in 1 until wrappedLines.size) {
+      builder.append(lineSeparator).append(indentString).append(wrappedLines[i].trim())
+    }
+    return builder.toString()
+  }
+
+  private fun wrapLineComments(lines: List<String>, column0: Int): List<String> {
+    val result = ArrayList<String>()
+    for (originalLine in lines) {
+      var line = originalLine
+      // Add missing leading spaces to line comments: `//foo` -> `// foo`.
+      val matcher = LINE_COMMENT_MISSING_SPACE_PREFIX.matcher(line)
+      if (matcher.find()) {
+        val length = matcher.group(1).length
+        line = Strings.repeat("/", length) + " " + line.substring(length)
+      }
+      if (line.startsWith("// MOE:")) {
+        // don't wrap comments for https://github.com/google/MOE
+        result.add(line)
+        continue
+      }
+      while (line.length + column0 > Formatter.MAX_LINE_LENGTH) {
+        var idx = Formatter.MAX_LINE_LENGTH - column0
+        // only break on whitespace characters, and ignore the leading `// `
+        while (idx >= 2 && !CharMatcher.whitespace().matches(line[idx])) {
+          idx--
+        }
+        if (idx <= 2) {
+          break
+        }
+        result.add(line.substring(0, idx))
+        line = "//" + line.substring(idx)
+      }
+      result.add(line)
+    }
+    return result
+  }
+
+  // Remove leading whitespace (trailing was already removed), and re-indent.
+  // Add a +1 indent before '*', and add the '*' if necessary.
+  private fun indentJavadoc(lines: List<String>, column0: Int): String {
+    val builder = StringBuilder()
+    builder.append(lines[0].trim())
+    val indent = column0 + 1
+    val indentString = Strings.repeat(" ", indent)
+    for (i in 1 until lines.size) {
+      builder.append(lineSeparator).append(indentString)
+      val line = lines[i].trim()
+      if (!line.startsWith("*")) {
+        builder.append("* ")
+      }
+      builder.append(line)
+    }
+    return builder.toString()
+  }
+
+  // Preserve special `//noinspection` and `//$NON-NLS-x$` comments used by IDEs, which cannot
+  // contain leading spaces.
+  private val LINE_COMMENT_MISSING_SPACE_PREFIX =
+      Pattern.compile("^(//+)(?!noinspection|\\\$NON-NLS-\\d+\\$)[^\\s/]")
+
+  // Returns true if the comment looks like javadoc
+  private fun javadocShaped(lines: List<String>): Boolean {
+    val it = lines.iterator()
+    if (!it.hasNext()) {
+      return false
+    }
+    val first = it.next().trim()
+    // if it's actually javadoc, we're done
+    if (first.startsWith("/**")) {
+      return true
+    }
+    // if it's a block comment, check all trailing lines for '*'
+    if (!first.startsWith("/*")) {
+      return false
+    }
+    while (it.hasNext()) {
+      if (!it.next().trim().startsWith("*")) {
+        return false
+      }
+    }
+    return true
+  }
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/kdoc/KDocFormatter.kt b/core/src/main/java/com/facebook/ktfmt/kdoc/KDocFormatter.kt
new file mode 100644
index 0000000..8c0af07
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/kdoc/KDocFormatter.kt
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+/*
+ * This was copied from https://github.com/google/google-java-format and modified extensively to
+ * work for Kotlin formatting
+ */
+
+package com.facebook.ktfmt.kdoc
+
+import com.facebook.ktfmt.kdoc.KDocToken.Type.BEGIN_KDOC
+import com.facebook.ktfmt.kdoc.KDocToken.Type.BLANK_LINE
+import com.facebook.ktfmt.kdoc.KDocToken.Type.CODE
+import com.facebook.ktfmt.kdoc.KDocToken.Type.CODE_BLOCK_MARKER
+import com.facebook.ktfmt.kdoc.KDocToken.Type.CODE_CLOSE_TAG
+import com.facebook.ktfmt.kdoc.KDocToken.Type.CODE_OPEN_TAG
+import com.facebook.ktfmt.kdoc.KDocToken.Type.END_KDOC
+import com.facebook.ktfmt.kdoc.KDocToken.Type.LIST_ITEM_OPEN_TAG
+import com.facebook.ktfmt.kdoc.KDocToken.Type.LITERAL
+import com.facebook.ktfmt.kdoc.KDocToken.Type.MARKDOWN_LINK
+import com.facebook.ktfmt.kdoc.KDocToken.Type.PRE_CLOSE_TAG
+import com.facebook.ktfmt.kdoc.KDocToken.Type.PRE_OPEN_TAG
+import com.facebook.ktfmt.kdoc.KDocToken.Type.TABLE_CLOSE_TAG
+import com.facebook.ktfmt.kdoc.KDocToken.Type.TABLE_OPEN_TAG
+import com.facebook.ktfmt.kdoc.KDocToken.Type.TAG
+import com.facebook.ktfmt.kdoc.KDocToken.Type.WHITESPACE
+import java.util.regex.Pattern.compile
+import org.jetbrains.kotlin.com.intellij.psi.tree.IElementType
+import org.jetbrains.kotlin.kdoc.lexer.KDocLexer
+import org.jetbrains.kotlin.kdoc.lexer.KDocTokens
+import org.jetbrains.kotlin.lexer.KtTokens.WHITE_SPACE
+
+/**
+ * Entry point for formatting KDoc.
+ *
+ * This stateless class reads tokens from the stateful lexer and translates them to "requests" and
+ * "writes" to the stateful writer. It also munges tokens into "standardized" forms. Finally, it
+ * performs postprocessing to convert the written KDoc to a one-liner if possible or to leave a
+ * single blank line if it's empty.
+ */
+object KDocFormatter {
+
+  private val ONE_CONTENT_LINE_PATTERN = compile(" */[*][*]\n *[*] (.*)\n *[*]/")
+
+  private val NUMBERED_LIST_PATTERN = "[0-9]+\\.".toRegex()
+
+  /**
+   * Formats the given Javadoc comment, which must start with ∕✱✱ and end with ✱∕. The output will
+   * start and end with the same characters.
+   */
+  fun formatKDoc(input: String, blockIndent: Int, maxLineLength: Int): String {
+    val escapedInput = Escaping.escapeKDoc(input)
+    val kDocLexer = KDocLexer()
+    kDocLexer.start(escapedInput)
+    val tokens = mutableListOf<KDocToken>()
+    var previousType: IElementType? = null
+    while (kDocLexer.tokenType != null) {
+      val tokenType = kDocLexer.tokenType
+      val tokenText =
+          with(kDocLexer.tokenText) {
+            if (previousType == KDocTokens.LEADING_ASTERISK && first() == ' ') substring(1)
+            else this
+          }
+
+      processToken(tokenType, tokens, tokenText, previousType)
+
+      previousType = tokenType
+      kDocLexer.advance()
+    }
+    val result = render(tokens, blockIndent, maxLineLength)
+    return makeSingleLineIfPossible(blockIndent, result, maxLineLength)
+  }
+
+  private fun processToken(
+      tokenType: IElementType?,
+      tokens: MutableList<KDocToken>,
+      tokenText: String,
+      previousType: IElementType?
+  ) {
+    when (tokenType) {
+      KDocTokens.START -> tokens.add(KDocToken(BEGIN_KDOC, tokenText))
+      KDocTokens.END -> tokens.add(KDocToken(END_KDOC, tokenText))
+      KDocTokens.LEADING_ASTERISK -> Unit // Ignore, no need to output anything
+      KDocTokens.TAG_NAME -> tokens.add(KDocToken(TAG, tokenText))
+      KDocTokens.CODE_BLOCK_TEXT -> tokens.add(KDocToken(CODE, tokenText))
+      KDocTokens.MARKDOWN_INLINE_LINK,
+      KDocTokens.MARKDOWN_LINK -> {
+        tokens.add(KDocToken(MARKDOWN_LINK, tokenText))
+      }
+      KDocTokens.MARKDOWN_ESCAPED_CHAR,
+      KDocTokens.TEXT -> {
+        var first = true
+        for (word in tokenizeKdocText(tokenText)) {
+          if (word.first().isWhitespace()) {
+            tokens.add(KDocToken(WHITESPACE, " "))
+            continue
+          }
+          if (first) {
+            if (word == "-" || word == "*" || word.matches(NUMBERED_LIST_PATTERN)) {
+              tokens.add(KDocToken(LIST_ITEM_OPEN_TAG, ""))
+            }
+            first = false
+          }
+          // If the KDoc is malformed (e.g. unclosed code block) KDocLexer doesn't report an
+          // END_KDOC properly. We want to recover in such cases
+          if (word == "*/") {
+            tokens.add(KDocToken(END_KDOC, word))
+          } else if (word.startsWith("```")) {
+            tokens.add(KDocToken(CODE_BLOCK_MARKER, word))
+          } else {
+            tokens.add(KDocToken(LITERAL, word))
+          }
+        }
+      }
+      WHITE_SPACE -> {
+        if (previousType == KDocTokens.LEADING_ASTERISK || tokenText.count { it == '\n' } >= 2) {
+          tokens.add(KDocToken(BLANK_LINE, ""))
+        } else {
+          tokens.add(KDocToken(WHITESPACE, " "))
+        }
+      }
+      else -> throw RuntimeException("Unexpected: $tokenType")
+    }
+  }
+  private fun render(input: List<KDocToken>, blockIndent: Int, maxLineLength: Int): String {
+    val output = KDocWriter(blockIndent, maxLineLength)
+    for (token in input) {
+      when (token.type) {
+        BEGIN_KDOC -> output.writeBeginJavadoc()
+        END_KDOC -> {
+          output.writeEndJavadoc()
+          return Escaping.unescapeKDoc(output.toString())
+        }
+        LIST_ITEM_OPEN_TAG -> output.writeListItemOpen(token)
+        PRE_OPEN_TAG -> output.writePreOpen(token)
+        PRE_CLOSE_TAG -> output.writePreClose(token)
+        CODE_OPEN_TAG -> output.writeCodeOpen(token)
+        CODE_CLOSE_TAG -> output.writeCodeClose(token)
+        TABLE_OPEN_TAG -> output.writeTableOpen(token)
+        TABLE_CLOSE_TAG -> output.writeTableClose(token)
+        TAG -> output.writeTag(token)
+        CODE -> output.writeCodeLine(token)
+        CODE_BLOCK_MARKER -> output.writeExplicitCodeBlockMarker(token)
+        BLANK_LINE -> output.requestBlankLine()
+        WHITESPACE -> output.requestWhitespace()
+        LITERAL -> output.writeLiteral(token)
+        MARKDOWN_LINK -> output.writeMarkdownLink(token)
+        else -> throw AssertionError(token.type)
+      }
+    }
+    throw AssertionError()
+  }
+
+  /**
+   * Returns the given string or a one-line version of it (e.g., "∕✱✱ Tests for foos. ✱∕") if it
+   * fits on one line.
+   */
+  private fun makeSingleLineIfPossible(
+      blockIndent: Int,
+      input: String,
+      maxLineLength: Int
+  ): String {
+    val oneLinerContentLength = maxLineLength - "/**  */".length - blockIndent
+    val matcher = ONE_CONTENT_LINE_PATTERN.matcher(input)
+    if (matcher.matches() && matcher.group(1).isEmpty()) {
+      return "/** */"
+    } else if (matcher.matches() && matcher.group(1).length <= oneLinerContentLength) {
+      return "/** " + matcher.group(1) + " */"
+    }
+    return input
+  }
+
+  /**
+   * tokenizeKdocText splits 's' by whitespace, and returns both whitespace and non-whitespace
+   * parts.
+   *
+   * Multiple adjacent whitespace characters are collapsed into one. Trailing and leading spaces are
+   * included in the result.
+   *
+   * Example: `" one two three "` becomes `[" ", "one", " ", "two", " ", "three", " "]`. See tests
+   * for more examples.
+   */
+  fun tokenizeKdocText(s: String) = sequence {
+    if (s.isEmpty()) {
+      return@sequence
+    }
+    var mark = 0
+    var inWhitespace = s[0].isWhitespace()
+    for (i in 1..s.lastIndex) {
+      if (inWhitespace == s[i].isWhitespace()) {
+        continue
+      }
+      val result = if (inWhitespace) " " else s.substring(mark, i)
+      inWhitespace = s[i].isWhitespace()
+      mark = i
+      yield(result)
+    }
+    yield(if (inWhitespace) " " else s.substring(mark, s.length))
+  }
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/kdoc/KDocToken.kt b/core/src/main/java/com/facebook/ktfmt/kdoc/KDocToken.kt
new file mode 100644
index 0000000..51801c8
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/kdoc/KDocToken.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+/*
+ * This was copied from https://github.com/google/google-java-format and modified extensively to
+ * work for Kotlin formatting
+ */
+
+package com.facebook.ktfmt.kdoc
+
+/**
+ * KDoc token. Our idea of what constitutes a token is often larger or smaller than what you'd
+ * naturally expect. The decision is usually pragmatic rather than theoretical. Most of the details
+ * are in [KDocFormatter].
+ */
+internal class KDocToken(val type: Type, val value: String) {
+  /**
+   * KDoc token type.
+   *
+   * The general idea is that every token that requires special handling (extra line breaks,
+   * indentation, forcing or forbidding whitespace) from [KDocWriter] gets its own type. But I
+   * haven't been super careful about it, so I'd imagine that we could merge or remove some of these
+   * if we wanted. (For example, PARAGRAPH_CLOSE_TAG and LIST_ITEM_CLOSE_TAG could share a common
+   * IGNORABLE token type. But their corresponding OPEN tags exist, so I've kept the CLOSE tags.)
+   *
+   * Note, though, that tokens of the same type may still have been handled differently by [ ] when
+   * it created them. For example, LITERAL is used for both plain text and inline tags, even though
+   * the two affect the lexer's state differently.
+   */
+  internal enum class Type {
+    /** ∕✱✱ */
+    BEGIN_KDOC,
+    /** ✱∕ */
+    END_KDOC,
+    LIST_ITEM_OPEN_TAG,
+    HEADER_OPEN_TAG,
+    PARAGRAPH_OPEN_TAG,
+    PRE_OPEN_TAG,
+    PRE_CLOSE_TAG,
+    CODE_OPEN_TAG,
+    CODE_CLOSE_TAG,
+    TABLE_OPEN_TAG,
+    TABLE_CLOSE_TAG,
+    /** Things such as `@param` or `@see` */
+    TAG,
+    /** Code between two markers of three backticks */
+    CODE,
+    /** three backticks */
+    CODE_BLOCK_MARKER,
+    /** A link in brackets such as [KDocToken] */
+    MARKDOWN_LINK,
+    BLANK_LINE,
+    /**
+     * Whitespace that is not in a `<pre>` or `<table>` section. Whitespace includes leading
+     * newlines, asterisks, and tabs and spaces. In the output, it is translated to newlines (with
+     * leading spaces and asterisks) or spaces.
+     */
+    WHITESPACE,
+    /**
+     * Anything else: `foo`, `<b>`, `{ foo}` etc. [KDocFormatter] sometimes creates adjacent literal
+     * tokens, which it then merges into a single, larger literal token before returning its output.
+     *
+     * This also includes whitespace in a `<pre>` or `<table>` section. We preserve user formatting
+     * in these sections, including arbitrary numbers of spaces. By treating such whitespace as a
+     * literal, we can merge it with adjacent literals, preventing us from autowrapping inside these
+     * sections -- and doing so naively, to boot. The wrapped line would have no indentation after
+     * "* " or, possibly worse, it might begin with an arbitrary amount of whitespace that didn't
+     * fit on the previous line. Of course, by doing this, we're potentially creating lines of more
+     * than 100 characters. But it seems fair to call in the humans to resolve such problems.
+     */
+    LITERAL
+  }
+
+  fun length(): Int = value.length
+
+  override fun toString(): String = "KDocToken{$type: \"$value\"}"
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/kdoc/KDocWriter.kt b/core/src/main/java/com/facebook/ktfmt/kdoc/KDocWriter.kt
new file mode 100644
index 0000000..8d7db5d
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/kdoc/KDocWriter.kt
@@ -0,0 +1,277 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+/*
+ * This was copied from https://github.com/google/google-java-format and modified extensively to
+ * work for Kotlin formatting
+ */
+
+package com.facebook.ktfmt.kdoc
+
+import com.facebook.ktfmt.kdoc.KDocToken.Type.CODE_BLOCK_MARKER
+import com.facebook.ktfmt.kdoc.KDocToken.Type.HEADER_OPEN_TAG
+import com.facebook.ktfmt.kdoc.KDocToken.Type.LIST_ITEM_OPEN_TAG
+import com.facebook.ktfmt.kdoc.KDocToken.Type.PARAGRAPH_OPEN_TAG
+import com.facebook.ktfmt.kdoc.KDocWriter.RequestedWhitespace.BLANK_LINE
+import com.facebook.ktfmt.kdoc.KDocWriter.RequestedWhitespace.CONDITIONAL_WHITESPACE
+import com.facebook.ktfmt.kdoc.KDocWriter.RequestedWhitespace.NEWLINE
+import com.facebook.ktfmt.kdoc.KDocWriter.RequestedWhitespace.NONE
+import com.facebook.ktfmt.kdoc.KDocWriter.RequestedWhitespace.WHITESPACE
+import com.google.common.base.Strings
+import com.google.common.collect.Ordering
+import com.google.common.collect.Sets.immutableEnumSet
+
+/**
+ * Stateful object that accepts "requests" and "writes," producing formatted Javadoc.
+ *
+ * Our Javadoc formatter doesn't ever generate a parse tree, only a stream of tokens, so the writer
+ * must compute and store the answer to questions like "How many levels of nested HTML list are we
+ * inside?"
+ */
+internal class KDocWriter(blockIndentCount: Int, private val maxLineLength: Int) {
+
+  /**
+   * Tokens that are always pinned to the following token. For example, `<p>` in `<p>Foo bar` (never
+   * `<p> Foo bar` or `<p>\nFoo bar`).
+   *
+   * This is not the only kind of "pinning" that we do: See also the joining of LITERAL tokens done
+   * by the lexer. The special pinning here is necessary because these tokens are not of type
+   * LITERAL (because they require other special handling).
+   */
+  private val START_OF_LINE_TOKENS =
+      immutableEnumSet(LIST_ITEM_OPEN_TAG, PARAGRAPH_OPEN_TAG, HEADER_OPEN_TAG)
+
+  private val output = StringBuilder()
+  private val blockIndent = Strings.repeat(" ", blockIndentCount + 1)
+
+  private var remainingOnLine: Int = 0
+  private var atStartOfLine: Boolean = false
+  private var inCodeBlock: Boolean = false
+  private var requestedWhitespace = NONE
+
+  /**
+   * Requests whitespace between the previously written token and the next written token. The
+   * request may be honored, or it may be overridden by a request for "more significant" whitespace,
+   * like a newline.
+   */
+  fun requestWhitespace() {
+    requestWhitespace(WHITESPACE)
+  }
+
+  fun writeBeginJavadoc() {
+    /*
+     * JavaCommentsHelper will make sure this is indented right. But it seems sensible enough that,
+     * if our input starts with ∕✱✱, so too does our output.
+     */
+    appendTrackingLength("/**")
+  }
+
+  fun writeEndJavadoc() {
+    requestCloseCodeBlockMarker()
+    output.append("\n")
+    appendTrackingLength(blockIndent)
+    appendTrackingLength("*/")
+  }
+
+  fun writeListItemOpen(token: KDocToken) {
+    requestCloseCodeBlockMarker()
+    requestNewline()
+    writeToken(token)
+  }
+
+  fun writePreOpen(token: KDocToken) {
+    requestBlankLine()
+
+    writeToken(token)
+  }
+
+  fun writePreClose(token: KDocToken) {
+    writeToken(token)
+
+    requestBlankLine()
+  }
+
+  fun writeCodeOpen(token: KDocToken) {
+    writeToken(token)
+  }
+
+  fun writeCodeClose(token: KDocToken) {
+    writeToken(token)
+  }
+
+  fun writeTableOpen(token: KDocToken) {
+    requestBlankLine()
+
+    writeToken(token)
+  }
+
+  fun writeTableClose(token: KDocToken) {
+    writeToken(token)
+
+    requestBlankLine()
+  }
+
+  fun writeTag(token: KDocToken) {
+    requestNewline()
+    writeToken(token)
+  }
+
+  fun writeCodeLine(token: KDocToken) {
+    requestOpenCodeBlockMarker()
+    requestNewline()
+    if (token.value.isNotEmpty()) {
+      writeToken(token)
+    }
+  }
+
+  /** Adds a code block marker if we are not in a code block currently */
+  private fun requestCloseCodeBlockMarker() {
+    if (inCodeBlock) {
+      this.requestedWhitespace = NEWLINE
+      writeExplicitCodeBlockMarker(KDocToken(CODE_BLOCK_MARKER, "```"))
+    }
+  }
+
+  /** Adds a code block marker if we are in a code block currently */
+  private fun requestOpenCodeBlockMarker() {
+    if (!inCodeBlock) {
+      this.requestedWhitespace = NEWLINE
+      writeExplicitCodeBlockMarker(KDocToken(CODE_BLOCK_MARKER, "```"))
+    }
+  }
+
+  fun writeExplicitCodeBlockMarker(token: KDocToken) {
+    requestNewline()
+    writeToken(token)
+    requestNewline()
+    inCodeBlock = !inCodeBlock
+  }
+
+  fun writeLiteral(token: KDocToken) {
+    requestCloseCodeBlockMarker()
+
+    writeToken(token)
+  }
+
+  fun writeMarkdownLink(token: KDocToken) {
+    writeToken(token)
+  }
+
+  override fun toString(): String {
+    return output.toString()
+  }
+
+  fun requestBlankLine() {
+    requestWhitespace(BLANK_LINE)
+  }
+
+  fun requestNewline() {
+    requestWhitespace(NEWLINE)
+  }
+
+  private fun requestWhitespace(requestedWhitespace: RequestedWhitespace) {
+    this.requestedWhitespace =
+        Ordering.natural<Comparable<*>>().max(requestedWhitespace, this.requestedWhitespace)
+  }
+
+  /**
+   * The kind of whitespace that has been requested between the previous and next tokens. The order
+   * of the values is significant: It goes from lowest priority to highest. For example, if the
+   * previous token requests [.BLANK_LINE] after it but the next token requests only [ ][.NEWLINE]
+   * before it, we insert [.BLANK_LINE].
+   */
+  internal enum class RequestedWhitespace {
+    NONE,
+
+    /**
+     * Add one space, only if the next token seems like a word In contrast, punctuation like a dot
+     * does need a space before it.
+     */
+    CONDITIONAL_WHITESPACE,
+
+    /** Add one space, e.g. " " */
+    WHITESPACE,
+
+    /** Break to the next line */
+    NEWLINE,
+
+    /** Add a whole blank line between the two lines of content */
+    BLANK_LINE,
+  }
+
+  private fun writeToken(token: KDocToken) {
+    if (requestedWhitespace == BLANK_LINE) {
+      writeBlankLine()
+      requestedWhitespace = NONE
+    } else if (requestedWhitespace == NEWLINE) {
+      writeNewline()
+      requestedWhitespace = NONE
+    }
+
+    val needWhitespace =
+        when (requestedWhitespace) {
+          WHITESPACE -> true
+          CONDITIONAL_WHITESPACE -> token.value.first().isLetterOrDigit()
+          else -> false
+        }
+    /*
+     * Write a newline if necessary to respect the line limit. (But if we're at the beginning of the
+     * line, a newline won't help. Or it might help but only by separating "<p>veryverylongword,"
+     * which goes against our style.)
+     */
+    if (!atStartOfLine && token.length() + (if (needWhitespace) 1 else 0) > remainingOnLine) {
+      writeNewline()
+    }
+    if (!atStartOfLine && needWhitespace) {
+      appendTrackingLength(" ")
+    }
+
+    appendTrackingLength(token.value)
+    requestedWhitespace = NONE
+
+    if (!START_OF_LINE_TOKENS.contains(token.type)) {
+      atStartOfLine = false
+    }
+  }
+
+  private fun writeBlankLine() {
+    output.append("\n")
+    appendTrackingLength(blockIndent)
+    appendTrackingLength("*")
+    writeNewline()
+  }
+
+  private fun writeNewline() {
+    output.append("\n")
+    remainingOnLine = maxLineLength
+    appendTrackingLength(blockIndent)
+    appendTrackingLength("* ")
+    atStartOfLine = true
+  }
+
+  /*
+   * TODO(cpovirk): We really want the number of "characters," not chars. Figure out what the
+   * right way of measuring that is (grapheme count (with BreakIterator?)? sum of widths of all
+   * graphemes? I don't think that our style guide is specific about this.). Moreover, I am
+   * probably brushing other problems with surrogates, etc. under the table. Hopefully I mostly
+   * get away with it by joining all non-space, non-tab characters together.
+   *
+   * Possibly the "width" question has no right answer:
+   * http://denisbider.blogspot.com/2015/09/when-monospace-fonts-arent-unicode.html
+   */
+  private fun appendTrackingLength(str: String) {
+    output.append(str)
+    remainingOnLine -= str.length
+  }
+}
diff --git a/core/src/main/java/com/facebook/ktfmt/kdoc/NestingCounter.kt b/core/src/main/java/com/facebook/ktfmt/kdoc/NestingCounter.kt
new file mode 100644
index 0000000..e744089
--- /dev/null
+++ b/core/src/main/java/com/facebook/ktfmt/kdoc/NestingCounter.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+/*
+ * This was copied from https://github.com/google/google-java-format and modified extensively to
+ * work for Kotlin formatting
+ */
+
+package com.facebook.ktfmt.kdoc
+
+/** Mutable integer for tracking the level of nesting. */
+internal class NestingCounter {
+  private var value: Int = 0
+
+  val isPositive: Boolean
+    get() = value > 0
+
+  fun value(): Int {
+    return value
+  }
+
+  fun increment() {
+    value++
+  }
+
+  fun incrementIfPositive() {
+    if (value > 0) {
+      value++
+    }
+  }
+
+  fun decrementIfPositive() {
+    if (value > 0) {
+      value--
+    }
+  }
+
+  fun reset() {
+    value = 0
+  }
+}
diff --git a/core/src/test/java/com/facebook/ktfmt/cli/MainTest.kt b/core/src/test/java/com/facebook/ktfmt/cli/MainTest.kt
new file mode 100644
index 0000000..c02aca6
--- /dev/null
+++ b/core/src/test/java/com/facebook/ktfmt/cli/MainTest.kt
@@ -0,0 +1,432 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.facebook.ktfmt.cli
+
+import com.google.common.truth.Truth.assertThat
+import java.io.ByteArrayOutputStream
+import java.io.File
+import java.io.PrintStream
+import java.lang.IllegalStateException
+import java.nio.file.Files
+import java.util.concurrent.ForkJoinPool
+import kotlin.io.path.createTempDirectory
+import org.junit.After
+import org.junit.Assert.fail
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@Suppress("FunctionNaming")
+@RunWith(JUnit4::class)
+class MainTest {
+
+  private val root = createTempDirectory().toFile()
+
+  private val emptyInput = "".byteInputStream()
+  private val out = ByteArrayOutputStream()
+  private val err = ByteArrayOutputStream()
+
+  @After
+  fun tearDown() {
+    root.deleteRecursively()
+  }
+
+  /**
+   * Scenario: someone _really_ wants to format this file, regardless of its extension. When a
+   * single argument file is given, it is used as is without filtering by extension.
+   */
+  @Test
+  fun `expandArgsToFileNames - single file arg is used as is`() {
+    val fooBar = root.resolve("foo.bar")
+    fooBar.writeText("hi")
+    assertThat(Main.expandArgsToFileNames(listOf(fooBar.toString()))).containsExactly(fooBar)
+  }
+
+  @Test
+  fun `expandArgsToFileNames - single arg which is not a file is not returned`() {
+    val fooBar = root.resolve("foo.bar")
+    assertThat(Main.expandArgsToFileNames(listOf(fooBar.toString()))).isEmpty()
+  }
+
+  @Test
+  fun `expandArgsToFileNames - single arg which is a directory is resolved to its recursively contained kt files`() {
+    val dir = root.resolve("dir")
+    dir.mkdirs()
+    val foo = dir.resolve("foo.kt")
+    foo.writeText("")
+    val bar = dir.resolve("bar.kt")
+    bar.writeText("")
+    assertThat(Main.expandArgsToFileNames(listOf(dir.toString()))).containsExactly(foo, bar)
+  }
+
+  @Test
+  fun `expandArgsToFileNames - multiple directory args are resolved to their recursively contained kt files`() {
+    val dir1 = root.resolve("dir1")
+    dir1.mkdirs()
+    val foo1 = dir1.resolve("foo1.kt")
+    foo1.writeText("")
+    val bar1 = dir1.resolve("bar1.kt")
+    bar1.writeText("")
+
+    val dir2 = root.resolve("dir2")
+    dir1.mkdirs()
+    val foo2 = dir1.resolve("foo2.kt")
+    foo2.writeText("")
+    val bar2 = dir1.resolve("bar2.kt")
+    bar2.writeText("")
+
+    assertThat(Main.expandArgsToFileNames(listOf(dir1.toString(), dir2.toString())))
+        .containsExactly(foo1, bar1, foo2, bar2)
+  }
+
+  @Test
+  fun `expandArgsToFileNames - a dash is an error`() {
+    try {
+      Main.expandArgsToFileNames(listOf(root.resolve("foo.bar").toString(), File("-").toString()))
+      fail("expected exception, but nothing was thrown")
+    } catch (e: IllegalStateException) {
+      assertThat(e.message).contains("Error")
+    }
+  }
+
+  @Test
+  fun `Using '-' as the filename formats an InputStream`() {
+    val code = "fun    f1 (  ) :    Int =    0"
+    Main(code.byteInputStream(), PrintStream(out), PrintStream(err), arrayOf("-")).run()
+
+    val expected = "fun f1(): Int = 0\n"
+    assertThat(out.toString("UTF-8")).isEqualTo(expected)
+  }
+
+  @Test
+  fun `Parsing errors are reported (stdin)`() {
+    val code = "fun    f1 (  "
+    val returnValue =
+        Main(code.byteInputStream(), PrintStream(out), PrintStream(err), arrayOf("-")).run()
+
+    assertThat(returnValue).isEqualTo(1)
+    assertThat(err.toString("UTF-8")).startsWith("<stdin>:1:14: error: ")
+  }
+
+  @Test
+  fun `Parsing errors are reported (file)`() {
+    val fooBar = root.resolve("foo.kt")
+    fooBar.writeText("fun    f1 (  ")
+    val returnValue =
+        Main(emptyInput, PrintStream(out), PrintStream(err), arrayOf(fooBar.toString())).run()
+
+    assertThat(returnValue).isEqualTo(1)
+    assertThat(err.toString("UTF-8")).contains("foo.kt:1:14: error: ")
+  }
+
+  @Test
+  fun `all files in args are processed, even if one of them has an error`() {
+    val file1 = root.resolve("file1.kt")
+    val file2Broken = root.resolve("file2.kt")
+    val file3 = root.resolve("file3.kt")
+    file1.writeText("fun    f1 ()  ")
+    file2Broken.writeText("fun    f1 (  ")
+    file3.writeText("fun    f1 ()  ")
+
+    // Make Main() process files serially.
+    val forkJoinPool = ForkJoinPool(1)
+
+    val returnValue: Int =
+        forkJoinPool
+            .submit<Int> {
+              Main(
+                      emptyInput,
+                      PrintStream(out),
+                      PrintStream(err),
+                      arrayOf(file1.toString(), file2Broken.toString(), file3.toString()))
+                  .run()
+            }
+            .get()
+
+    assertThat(returnValue).isEqualTo(1)
+    assertThat(err.toString("UTF-8")).contains("Done formatting $file1")
+    assertThat(err.toString("UTF-8")).contains("file2.kt:1:14: error: ")
+    assertThat(err.toString("UTF-8")).contains("Done formatting $file3")
+  }
+
+  @Test
+  fun `file is not modified if it is already formatted`() {
+    val code = """fun f() = println("hello, world")""" + "\n"
+    val formattedFile = root.resolve("formatted_file.kt")
+    formattedFile.writeText(code)
+    val formattedFilePath = formattedFile.toPath()
+
+    val lastModifiedTimeBeforeRunningFormatter =
+        Files.getLastModifiedTime(formattedFilePath).toMillis()
+    Main(emptyInput, PrintStream(out), PrintStream(err), arrayOf(formattedFile.toString())).run()
+    val lastModifiedTimeAfterRunningFormatter =
+        Files.getLastModifiedTime(formattedFilePath).toMillis()
+
+    assertThat(lastModifiedTimeBeforeRunningFormatter)
+        .isEqualTo(lastModifiedTimeAfterRunningFormatter)
+  }
+
+  @Test
+  fun `file is modified if it is not formatted`() {
+    val code = """fun f() =   println(  "hello, world")""" + "\n"
+    val unformattedFile = root.resolve("unformatted_file.kt")
+    unformattedFile.writeText(code)
+    val unformattedFilePath = unformattedFile.toPath()
+
+    val lastModifiedTimeBeforeRunningFormatter =
+        Files.getLastModifiedTime(unformattedFilePath).toMillis()
+    // The test may run under 1ms, and we need to make sure the new file timestamp will be different
+    Thread.sleep(100)
+    Main(emptyInput, PrintStream(out), PrintStream(err), arrayOf(unformattedFile.toString())).run()
+    val lastModifiedTimeAfterRunningFormatter =
+        Files.getLastModifiedTime(unformattedFilePath).toMillis()
+
+    assertThat(lastModifiedTimeBeforeRunningFormatter)
+        .isLessThan(lastModifiedTimeAfterRunningFormatter)
+  }
+
+  @Test
+  fun `dropbox-style is passed to formatter (file)`() {
+    val code =
+        """fun f() {
+    for (child in
+        node.next.next.next.next.next.next.next.next.next.next.next.next.next.next.data()) {
+        println(child)
+    }
+}
+"""
+    val fooBar = root.resolve("foo.kt")
+    fooBar.writeText(code)
+
+    Main(
+            emptyInput,
+            PrintStream(out),
+            PrintStream(err),
+            arrayOf("--dropbox-style", fooBar.toString()))
+        .run()
+
+    assertThat(fooBar.readText()).isEqualTo(code)
+  }
+
+  @Test
+  fun `dropbox-style is passed to formatter (stdin)`() {
+    val code =
+        """fun f() {
+          |for (child in
+          |node.next.next.next.next.next.next.next.next.next.next.next.next.next.next.data()) {
+          |println(child)
+          |}
+          |}
+          |""".trimMargin()
+    val formatted =
+        """fun f() {
+          |    for (child in
+          |        node.next.next.next.next.next.next.next.next.next.next.next.next.next.next.data()) {
+          |        println(child)
+          |    }
+          |}
+          |""".trimMargin()
+    Main(
+            code.byteInputStream(),
+            PrintStream(out),
+            PrintStream(err),
+            arrayOf("--dropbox-style", "-"))
+        .run()
+
+    assertThat(out.toString("UTF-8")).isEqualTo(formatted)
+  }
+
+  @Test
+  fun `expandArgsToFileNames - resolves 'kt' and 'kts' filenames only (recursively)`() {
+    val f1 = root.resolve("1.kt")
+    val f2 = root.resolve("2.kt")
+    val f3 = root.resolve("3")
+    val f4 = root.resolve("4.dummyext")
+    val f5 = root.resolve("5.kts")
+
+    val dir = root.resolve("foo")
+    dir.mkdirs()
+    val f6 = root.resolve("foo/1.kt")
+    val f7 = root.resolve("foo/2.kts")
+    val f8 = root.resolve("foo/3.dummyext")
+    val files = listOf(f1, f2, f3, f4, f5, f6, f7, f8)
+    for (f in files) {
+      f.createNewFile()
+    }
+    assertThat(Main.expandArgsToFileNames(files.map { it.toString() }))
+        .containsExactly(f1, f2, f5, f6, f7)
+  }
+
+  @Test
+  fun `formatting from stdin prints formatted code to stdout regardless of whether it was already formatted`() {
+    val expected = """fun f() = println("hello, world")""" + "\n"
+
+    Main(
+            """fun f (   ) =    println("hello, world")""".byteInputStream(),
+            PrintStream(out),
+            PrintStream(err),
+            arrayOf("-"))
+        .run()
+    assertThat(out.toString("UTF-8")).isEqualTo(expected)
+
+    out.reset()
+
+    Main(
+            """fun f () = println("hello, world")""".byteInputStream(),
+            PrintStream(out),
+            PrintStream(err),
+            arrayOf("-"))
+        .run()
+    assertThat(out.toString("UTF-8")).isEqualTo(expected)
+  }
+
+  @Test
+  fun `--dry-run prints filename and does not change file`() {
+    val code = """fun f () =    println( "hello, world" )"""
+    val file = root.resolve("foo.kt")
+    file.writeText(code)
+
+    Main(emptyInput, PrintStream(out), PrintStream(err), arrayOf("--dry-run", file.toString()))
+        .run()
+
+    assertThat(file.readText()).isEqualTo(code)
+    assertThat(out.toString("UTF-8")).contains(file.toString())
+  }
+
+  @Test
+  fun `--dry-run prints 'stdin' and does not reformat code from stdin`() {
+    val code = """fun f () =    println( "hello, world" )"""
+
+    Main(code.byteInputStream(), PrintStream(out), PrintStream(err), arrayOf("--dry-run", "-"))
+        .run()
+
+    assertThat(out.toString("UTF-8")).doesNotContain("hello, world")
+    assertThat(out.toString("UTF-8")).isEqualTo("<stdin>\n")
+  }
+
+  @Test
+  fun `--dry-run prints nothing when there are no changes needed (file)`() {
+    val code = """fun f() = println("hello, world")\n"""
+    val file = root.resolve("foo.kt")
+    file.writeText(code)
+
+    Main(emptyInput, PrintStream(out), PrintStream(err), arrayOf("--dry-run", file.toString()))
+        .run()
+
+    assertThat(out.toString("UTF-8")).isEmpty()
+  }
+
+  @Test
+  fun `--dry-run prints nothing when there are no changes needed (stdin)`() {
+    val code = """fun f() = println("hello, world")\n"""
+
+    Main(code.byteInputStream(), PrintStream(out), PrintStream(err), arrayOf("--dry-run", "-"))
+        .run()
+
+    assertThat(out.toString("UTF-8")).isEmpty()
+  }
+
+  @Test
+  fun `Exit code is 0 when there are changes (file)`() {
+    val code = """fun f () =    println( "hello, world" )"""
+    val file = root.resolve("foo.kt")
+    file.writeText(code)
+
+    val exitCode =
+        Main(emptyInput, PrintStream(out), PrintStream(err), arrayOf(file.toString())).run()
+
+    assertThat(exitCode).isEqualTo(0)
+  }
+
+  @Test
+  fun `Exits with 0 when there are changes (stdin)`() {
+    val code = """fun f () =    println( "hello, world" )"""
+
+    val exitCode =
+        Main(code.byteInputStream(), PrintStream(out), PrintStream(err), arrayOf("-")).run()
+
+    assertThat(exitCode).isEqualTo(0)
+  }
+
+  @Test
+  fun `Exit code is 1 when there are changes and --set-exit-if-changed is set (file)`() {
+    val code = """fun f () =    println( "hello, world" )"""
+    val file = root.resolve("foo.kt")
+    file.writeText(code)
+
+    val exitCode =
+        Main(
+                emptyInput,
+                PrintStream(out),
+                PrintStream(err),
+                arrayOf("--set-exit-if-changed", file.toString()))
+            .run()
+
+    assertThat(exitCode).isEqualTo(1)
+  }
+
+  @Test
+  fun `Exit code is 1 when there are changes and --set-exit-if-changed is set (stdin)`() {
+    val code = """fun f () =    println( "hello, world" )"""
+
+    val exitCode =
+        Main(
+                code.byteInputStream(),
+                PrintStream(out),
+                PrintStream(err),
+                arrayOf("--set-exit-if-changed", "-"))
+            .run()
+
+    assertThat(exitCode).isEqualTo(1)
+  }
+
+  @Test
+  fun `--set-exit-if-changed and --dry-run changes nothing, prints filenames, and exits with 1 (file)`() {
+    val code = """fun f () =    println( "hello, world" )"""
+    val file = root.resolve("foo.kt")
+    file.writeText(code)
+
+    val exitCode =
+        Main(
+                emptyInput,
+                PrintStream(out),
+                PrintStream(err),
+                arrayOf("--dry-run", "--set-exit-if-changed", file.toString()))
+            .run()
+
+    assertThat(file.readText()).isEqualTo(code)
+    assertThat(out.toString("UTF-8")).contains(file.toString())
+    assertThat(exitCode).isEqualTo(1)
+  }
+
+  @Test
+  fun `--set-exit-if-changed and --dry-run changes nothing, prints filenames, and exits with 1 (stdin)`() {
+    val code = """fun f () =    println( "hello, world" )"""
+
+    val exitCode =
+        Main(
+                code.byteInputStream(),
+                PrintStream(out),
+                PrintStream(err),
+                arrayOf("--dry-run", "--set-exit-if-changed", "-"))
+            .run()
+
+    assertThat(out.toString("UTF-8")).doesNotContain("hello, world")
+    assertThat(out.toString("UTF-8")).isEqualTo("<stdin>\n")
+    assertThat(exitCode).isEqualTo(1)
+  }
+}
diff --git a/core/src/test/java/com/facebook/ktfmt/cli/ParsedArgsTest.kt b/core/src/test/java/com/facebook/ktfmt/cli/ParsedArgsTest.kt
new file mode 100644
index 0000000..40f3646
--- /dev/null
+++ b/core/src/test/java/com/facebook/ktfmt/cli/ParsedArgsTest.kt
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.facebook.ktfmt.cli
+
+import com.facebook.ktfmt.format.Formatter
+import com.facebook.ktfmt.format.FormattingOptions
+import com.google.common.truth.Truth.assertThat
+import java.io.ByteArrayOutputStream
+import java.io.FileNotFoundException
+import java.io.PrintStream
+import kotlin.io.path.createTempDirectory
+import kotlin.test.assertFailsWith
+import org.junit.After
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@Suppress("FunctionNaming")
+@RunWith(JUnit4::class)
+class ParsedArgsTest {
+
+  private val root = createTempDirectory().toFile()
+
+  @After
+  fun tearDown() {
+    root.deleteRecursively()
+  }
+
+  @Test
+  fun `files to format are returned and unknown flags are reported`() {
+    val out = ByteArrayOutputStream()
+
+    val (fileNames, _) = ParsedArgs.parseOptions(PrintStream(out), arrayOf("foo.kt", "--unknown"))
+
+    assertThat(fileNames).containsExactly("foo.kt")
+    assertThat(out.toString()).isEqualTo("Unexpected option: --unknown\n")
+  }
+
+  @Test
+  fun `files to format are returned and flags starting with @ are reported`() {
+    val out = ByteArrayOutputStream()
+
+    val (fileNames, _) = ParsedArgs.parseOptions(PrintStream(out), arrayOf("foo.kt", "@unknown"))
+
+    assertThat(fileNames).containsExactly("foo.kt")
+    assertThat(out.toString()).isEqualTo("Unexpected option: @unknown\n")
+  }
+
+  @Test
+  fun `parseOptions uses default values when args are empty`() {
+    val out = ByteArrayOutputStream()
+
+    val parsed = ParsedArgs.parseOptions(PrintStream(out), arrayOf("foo.kt"))
+
+    val formattingOptions = parsed.formattingOptions
+    assertThat(formattingOptions.style).isEqualTo(FormattingOptions.Style.FACEBOOK)
+    assertThat(formattingOptions.maxWidth).isEqualTo(100)
+    assertThat(formattingOptions.blockIndent).isEqualTo(2)
+    assertThat(formattingOptions.continuationIndent).isEqualTo(4)
+    assertThat(formattingOptions.removeUnusedImports).isTrue()
+    assertThat(formattingOptions.debuggingPrintOpsAfterFormatting).isFalse()
+
+    assertThat(parsed.dryRun).isFalse()
+    assertThat(parsed.setExitIfChanged).isFalse()
+  }
+
+  @Test
+  fun `parseOptions recognizes --dropbox-style and rejects unknown flags`() {
+    val out = ByteArrayOutputStream()
+
+    val (fileNames, formattingOptions) =
+        ParsedArgs.parseOptions(PrintStream(out), arrayOf("--dropbox-style", "foo.kt", "--unknown"))
+
+    assertThat(fileNames).containsExactly("foo.kt")
+    assertThat(formattingOptions.blockIndent).isEqualTo(4)
+    assertThat(formattingOptions.continuationIndent).isEqualTo(4)
+    assertThat(out.toString()).isEqualTo("Unexpected option: --unknown\n")
+  }
+
+  @Test
+  fun `parseOptions recognizes --google-style`() {
+    val out = ByteArrayOutputStream()
+
+    val (_, formattingOptions) =
+        ParsedArgs.parseOptions(PrintStream(out), arrayOf("--google-style", "foo.kt"))
+
+    assertThat(formattingOptions).isEqualTo(Formatter.GOOGLE_FORMAT)
+  }
+
+  @Test
+  fun `parseOptions recognizes --dry-run`() {
+    val out = ByteArrayOutputStream()
+
+    val parsed = ParsedArgs.parseOptions(PrintStream(out), arrayOf("--dry-run", "foo.kt"))
+
+    assertThat(parsed.dryRun).isTrue()
+  }
+
+  @Test
+  fun `parseOptions recognizes -n as --dry-run`() {
+    val out = ByteArrayOutputStream()
+
+    val parsed = ParsedArgs.parseOptions(PrintStream(out), arrayOf("-n", "foo.kt"))
+
+    assertThat(parsed.dryRun).isTrue()
+  }
+
+  @Test
+  fun `parseOptions recognizes --set-exit-if-changed`() {
+    val out = ByteArrayOutputStream()
+
+    val parsed =
+        ParsedArgs.parseOptions(PrintStream(out), arrayOf("--set-exit-if-changed", "foo.kt"))
+
+    assertThat(parsed.setExitIfChanged).isTrue()
+  }
+
+  @Test
+  fun `processArgs use the @file option with non existing file`() {
+    val out = ByteArrayOutputStream()
+
+    val e =
+        assertFailsWith<FileNotFoundException> {
+          ParsedArgs.processArgs(PrintStream(out), arrayOf("@non-existing-file"))
+        }
+    assertThat(e.message).contains("non-existing-file (No such file or directory)")
+  }
+
+  @Test
+  fun `processArgs use the @file option with file containing arguments`() {
+    val out = ByteArrayOutputStream()
+    val file = root.resolve("existing-file")
+    file.writeText("--google-style\n--dry-run\n--set-exit-if-changed\nFile1.kt\nFile2.kt\n")
+
+    val parsed = ParsedArgs.processArgs(PrintStream(out), arrayOf("@" + file.absolutePath))
+
+    assertThat(parsed.formattingOptions).isEqualTo(Formatter.GOOGLE_FORMAT)
+    assertThat(parsed.dryRun).isTrue()
+    assertThat(parsed.setExitIfChanged).isTrue()
+    assertThat(parsed.fileNames).containsExactlyElementsIn(listOf("File1.kt", "File2.kt"))
+  }
+}
diff --git a/core/src/test/java/com/facebook/ktfmt/format/FormatterTest.kt b/core/src/test/java/com/facebook/ktfmt/format/FormatterTest.kt
new file mode 100644
index 0000000..2096f50
--- /dev/null
+++ b/core/src/test/java/com/facebook/ktfmt/format/FormatterTest.kt
@@ -0,0 +1,5906 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.facebook.ktfmt.format
+
+import com.facebook.ktfmt.testutil.assertFormatted
+import com.facebook.ktfmt.testutil.assertThatFormatting
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.fail
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@Suppress("FunctionNaming")
+@RunWith(JUnit4::class)
+class FormatterTest {
+
+  @Test
+  fun `support script (kts) files`() =
+      assertFormatted(
+          """
+        |package foo
+        |
+        |import java.io.File
+        |
+        |val one: String
+        |
+        |val two: String
+        |
+        |fun f() {
+        |  println("asd")
+        |}
+        |
+        |println("Called with args:")
+        |
+        |args.forEach { println(File + "-") }
+        |""".trimMargin())
+
+  @Test
+  fun `support script (kts) files with a shebang`() =
+      assertFormatted(
+          """
+        |#!/usr/bin/env kscript
+        |package foo
+        |
+        |println("Called")
+        |""".trimMargin())
+
+  @Test
+  fun `call chains`() =
+      assertFormatted(
+          """
+      |--------------------------------------------------
+      |fun f() {
+      |  // Static method calls are attached to the class name.
+      |  ImmutableList.newBuilder()
+      |      .add(1)
+      |      .add(1)
+      |      .add(1)
+      |      .add(1)
+      |      .add(1)
+      |      .add(1)
+      |      .add(1)
+      |      .add(1)
+      |      .add(1)
+      |      .add(1)
+      |      .build()
+      |
+      |  // Multiple call expressions --> each on its own line.
+      |  ImmutableList()
+      |      .newBuilder()
+      |      .add(1)
+      |      .add(1)
+      |      .add(1)
+      |      .add(1)
+      |      .add(1)
+      |      .add(1)
+      |      .add(1)
+      |      .add(1)
+      |      .add(1)
+      |      .add(1)
+      |      .build()
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `line breaks in function arguments`() =
+      assertFormatted(
+          """
+      |--------------------------------------------------
+      |fun f() {
+      |  computeBreaks(
+      |      javaOutput.commentsHelper,
+      |      maxWidth,
+      |      Doc.State(+0, 0))
+      |  computeBreaks(
+      |      output.commentsHelper, maxWidth, State(0))
+      |  doc.computeBreaks(
+      |      javaOutput.commentsHelper,
+      |      maxWidth,
+      |      Doc.State(+0, 0))
+      |  doc.computeBreaks(
+      |      output.commentsHelper, maxWidth, State(0))
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `parameters and return type in function definitions`() =
+      assertFormatted(
+          """
+      |----------------------------------------
+      |fun format(
+      |    code: String,
+      |    maxWidth: Int =
+      |        DEFAULT_MAX_WIDTH_VERY_LONG
+      |): String {
+      |  val a = 0
+      |}
+      |
+      |fun print(
+      |    code: String,
+      |    maxWidth: Int =
+      |        DEFAULT_MAX_WIDTH_VERY_LONG
+      |) {
+      |  val a = 0
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `kitchen sink of tests`() {
+    // Don't add more tests here
+    val code =
+        """
+        |fun
+        |f  (
+        |a : Int
+        | , b: Double , c:String) {           var result = 0
+        | val aVeryLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongVar = 43
+        |          foo.bar.zed.accept(
+        |
+        |          )
+        |
+        |          foo(
+        |
+        |          )
+        |
+        |          foo.bar.zed.accept(
+        |                DoSomething.bar()
+        |          )
+        |
+        |          bar(
+        |          ImmutableList.newBuilder().add(1).add(1).add(1).add(1).add(1).add(1).add(1).add(1).add(1).add(1).build())
+        |
+        |
+        |          ImmutableList.newBuilder().add(1).add(1).add(1).add(1).add(1).add(1).add(1).add(1).add(1).add(1).build()
+        |     }
+        |""".trimMargin()
+
+    val expected =
+        """
+        |fun f(a: Int, b: Double, c: String) {
+        |  var result = 0
+        |  val aVeryLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongVar =
+        |      43
+        |  foo.bar.zed.accept()
+        |
+        |  foo()
+        |
+        |  foo.bar.zed.accept(DoSomething.bar())
+        |
+        |  bar(
+        |      ImmutableList.newBuilder()
+        |          .add(1)
+        |          .add(1)
+        |          .add(1)
+        |          .add(1)
+        |          .add(1)
+        |          .add(1)
+        |          .add(1)
+        |          .add(1)
+        |          .add(1)
+        |          .add(1)
+        |          .build())
+        |
+        |  ImmutableList.newBuilder()
+        |      .add(1)
+        |      .add(1)
+        |      .add(1)
+        |      .add(1)
+        |      .add(1)
+        |      .add(1)
+        |      .add(1)
+        |      .add(1)
+        |      .add(1)
+        |      .add(1)
+        |      .build()
+        |}
+        |""".trimMargin()
+
+    assertThatFormatting(code).isEqualTo(expected)
+    // Don't add more tests here
+  }
+
+  @Test
+  fun `spacing around variable declarations`() =
+      assertFormatted(
+          """
+      |fun f() {
+      |  var x: Int = 4
+      |  val y = 0
+      |}
+      |""".trimMargin())
+
+  @Test fun `class without a body nor properties`() = assertFormatted("class Foo\n")
+
+  @Test fun `interface without a body nor properties`() = assertFormatted("interface Foo\n")
+
+  @Test fun `preserve empty primary constructor`() = assertFormatted("class Foo()\n")
+
+  @Test
+  fun `simple fun interface`() =
+      assertFormatted(
+          """fun interface MyRunnable {
+        |  fun runIt()
+        |}
+        |""".trimMargin())
+
+  @Test
+  fun `handle complex fun interface without body`() =
+      assertFormatted("public fun interface Function<T : List<*>> : (Int, T?) -> T?\n")
+
+  @Test
+  fun `class without a body, with explicit ctor params`() =
+      assertFormatted("class Foo(a: Int, var b: Double, val c: String)\n")
+
+  @Test
+  fun `class with a body and explicit ctor params`() =
+      assertFormatted(
+          """
+      |class Foo(a: Int, var b: Double, val c: String) {
+      |  val x = 2
+      |  fun method() {}
+      |  class Bar
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `properties and fields with modifiers`() =
+      assertFormatted(
+          """
+      |class Foo(public val p1: Int, private val p2: Int, open val p3: Int, final val p4: Int) {
+      |  private var f1 = 0
+      |  public var f2 = 0
+      |  open var f3 = 0
+      |  final var f4 = 0
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `properties with multiple modifiers`() =
+      assertFormatted(
+          """
+      |class Foo(public open inner val p1: Int) {
+      |  public open inner var f2 = 0
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `spaces around binary operations`() =
+      assertFormatted(
+          """
+      |fun foo() {
+      |  a = 5
+      |  x + 1
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `breaking long binary operations`() =
+      assertFormatted(
+          """
+      |--------------------
+      |fun foo() {
+      |  val finalWidth =
+      |      value1 +
+      |          value2 +
+      |          (value3 +
+      |              value4 +
+      |              value5) +
+      |          foo(v) +
+      |          (1 + 2) +
+      |          function(
+      |              value7,
+      |              value8) +
+      |          value9
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `prioritize according to associativity`() =
+      assertFormatted(
+          """
+      |--------------------------------------
+      |fun foo() {
+      |  return expression1 != expression2 ||
+      |      expression2 != expression1
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `once a binary expression is broken, split on every line`() =
+      assertFormatted(
+          """
+        |--------------------------------------
+        |fun foo() {
+        |  val sentence =
+        |      "The" +
+        |          "quick" +
+        |          ("brown" + "fox") +
+        |          "jumps" +
+        |          "over" +
+        |          "the" +
+        |          "lazy" +
+        |          "dog"
+        |}
+        |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `long binary expressions with ranges in the middle`() =
+      assertFormatted(
+          """
+        |--------------------------------------
+        |fun foo() {
+        |  val sentence =
+        |      "The" +
+        |          "quick" +
+        |          ("brown".."fox") +
+        |          "jumps" +
+        |          "over" +
+        |          "the".."lazy" + "dog"
+        |}
+        |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `assignment expressions with scoping functions are block-like`() =
+      assertFormatted(
+          """
+        |---------------------------
+        |fun f() {
+        |  name.sub = scope { x ->
+        |    //
+        |  }
+        |  name.sub += scope { x ->
+        |    //
+        |  }
+        |  name.sub -= scope { x ->
+        |    //
+        |  }
+        |  name.sub *= scope { x ->
+        |    //
+        |  }
+        |  name.sub /= scope { x ->
+        |    //
+        |  }
+        |  name.sub %= scope { x ->
+        |    //
+        |  }
+        |}
+        |
+        |fun h() {
+        |  long.name.sub =
+        |      scope { x ->
+        |        //
+        |      }
+        |  long.name.sub +=
+        |      scope { x ->
+        |        //
+        |      }
+        |  long.name.sub -=
+        |      scope { x ->
+        |        //
+        |      }
+        |  long.name.sub *=
+        |      scope { x ->
+        |        //
+        |      }
+        |  long.name.sub /=
+        |      scope { x ->
+        |        //
+        |      }
+        |  long.name.sub %=
+        |      scope { x ->
+        |        //
+        |      }
+        |}
+        |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `don't keep adding newlines between these two comments when they're at end of file`() =
+      assertFormatted(
+          """
+     |package foo
+     |// a
+     |
+     |/* Another comment */
+     |""".trimMargin())
+
+  @Test
+  fun `properties with accessors`() =
+      assertFormatted(
+          """
+      |class Foo {
+      |  var x: Int
+      |    get() = field
+      |  var y: Boolean
+      |    get() = x.equals(123)
+      |    set(value) {
+      |      field = value
+      |    }
+      |  var z: Boolean
+      |    get() {
+      |      x.equals(123)
+      |    }
+      |  var zz = false
+      |    private set
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `properties with accessors and semicolons on same line`() {
+    val code =
+        """
+      |class Foo {
+      |  var x = false; private set
+      |  internal val a by lazy { 5 }; internal get
+      |  var foo: Int; get() = 6; set(x) {};
+      |}
+      |""".trimMargin()
+
+    val expected =
+        """
+      |class Foo {
+      |  var x = false
+      |    private set
+      |  internal val a by lazy { 5 }
+      |    internal get
+      |  var foo: Int
+      |    get() = 6
+      |    set(x) {}
+      |}
+      |""".trimMargin()
+
+    assertThatFormatting(code).isEqualTo(expected)
+  }
+
+  @Test
+  fun `a property with a too long name being broken on multiple lines`() =
+      assertFormatted(
+          """
+      |--------------------
+      |class Foo {
+      |  val thisIsALongName:
+      |      String =
+      |      "Hello there this is long"
+      |    get() = field
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `multi-character unary and binary operators such as ==`() =
+      assertFormatted(
+          """
+      |fun f() {
+      |  3 == 4
+      |  true && false
+      |  a++
+      |  a === b
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `package names stay in one line`() {
+    val code =
+        """
+      | package  com  .example. subexample
+      |
+      |fun f() = 1
+      |""".trimMargin()
+    val expected =
+        """
+      |package com.example.subexample
+      |
+      |fun f() = 1
+      |""".trimMargin()
+
+    assertThatFormatting(code).isEqualTo(expected)
+  }
+
+  @Test
+  fun `handle package name and imports with escapes and spaces`() =
+      assertFormatted(
+          """
+      |package com.`fun times`.`with package names`
+      |
+      |import `nothing stops`.`us`.`from doing this`
+      |
+      |fun f() = `from doing this`()
+      |""".trimMargin())
+
+  @Test
+  fun `safe dot operator expression`() =
+      assertFormatted("""
+      |fun f() {
+      |  node?.name
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `safe dot operator expression with normal`() =
+      assertFormatted(
+          """
+      |fun f() {
+      |  node?.name.hello
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `safe dot operator expression chain in expression function`() =
+      assertFormatted(
+          """
+      |--------------------------------------------------
+      |fun f(number: Int) =
+      |    Something.doStuff(number)?.size
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `avoid breaking suspected package names`() =
+      assertFormatted(
+          """
+      |-----------------------
+      |fun f() {
+      |  com.facebook.Foo
+      |      .format()
+      |  org.facebook.Foo
+      |      .format()
+      |  java.lang.stuff.Foo
+      |      .format()
+      |  javax.lang.stuff.Foo
+      |      .format()
+      |  kotlin.lang.Foo
+      |      .format()
+      |  foo.facebook.Foo
+      |      .format()
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `an assortment of tests for emitQualifiedExpression`() =
+      assertFormatted(
+          """
+      |-------------------------------------
+      |fun f() {
+      |  // Regression test: https://github.com/facebookincubator/ktfmt/issues/56
+      |  kjsdfglkjdfgkjdfkgjhkerjghkdfj
+      |      ?.methodName1()
+      |
+      |  // a series of field accesses followed by a single call expression
+      |  // is kept together.
+      |  abcdefghijkl.abcdefghijkl
+      |      ?.methodName2()
+      |
+      |  // Similar to above.
+      |  abcdefghijkl.abcdefghijkl
+      |      ?.methodName3
+      |      ?.abcdefghijkl()
+      |
+      |  // Multiple call expressions cause each part of the expression
+      |  // to be placed on its own line.
+      |  abcdefghijkl
+      |      ?.abcdefghijkl
+      |      ?.methodName4()
+      |      ?.abcdefghijkl()
+      |
+      |  // Don't break first call expression if it fits.
+      |  foIt(something.something.happens())
+      |      .thenReturn(result)
+      |
+      |  // Break after `longerThanFour(` because it's longer than 4 chars
+      |  longerThanFour(
+      |          something.something
+      |              .happens())
+      |      .thenReturn(result)
+      |
+      |  // Similarly to above, when part of qualified expression.
+      |  foo.longerThanFour(
+      |          something.something
+      |              .happens())
+      |      .thenReturn(result)
+      |
+      |  // Keep 'super' attached to the method name
+      |  super.abcdefghijkl
+      |      .methodName4()
+      |      .abcdefghijkl()
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `an assortment of tests for emitQualifiedExpression with lambdas`() =
+      assertFormatted(
+          """
+      |----------------------------------------------------------------------------
+      |fun f() {
+      |  val items =
+      |      items.toMutableList.apply {
+      |        //
+      |        foo
+      |      }
+      |
+      |  val items =
+      |      items.toMutableList().apply {
+      |        //
+      |        foo
+      |      }
+      |
+      |  // All dereferences are on one line (because they fit), even though
+      |  // the apply() at the end requires a line break.
+      |  val items =
+      |      items.toMutableList.sdfkjsdf.sdfjksdflk.sdlfkjsldfj.apply {
+      |        //
+      |        foo
+      |      }
+      |
+      |  // All method calls are on one line (because they fit), even though
+      |  // the apply() at the end requires a line break.
+      |  val items =
+      |      items.toMutableList().sdfkjsdf().sdfjksdflk().sdlfkjsldfj().apply {
+      |        //
+      |        foo
+      |      }
+      |
+      |  // All method calls with lambdas could fit, but we avoid a block like syntax
+      |  // and break to one call per line
+      |  val items =
+      |      items
+      |          .map { it + 1 }
+      |          .filter { it > 0 }
+      |          .apply {
+      |            //
+      |            foo
+      |          }
+      |
+      |  // the lambda is indented properly with the break before it
+      |  val items =
+      |      items.fieldName.sdfkjsdf.sdfjksdflk.sdlfkjsldfj.sdfjksdflk.sdlfkjsldfj
+      |          .sdlfkjsldfj
+      |          .apply {
+      |            //
+      |            foo
+      |          }
+      |  items.fieldName.sdfkjsdf.sdfjksdflk.sdlfkjsldfj.sdfjksdflk.sdlfkjsldfj
+      |      .apply {
+      |        //
+      |        foo
+      |      }
+      |
+      |  // When there are multiple method calls, and they don't fit on one
+      |  // line, put each on a new line.
+      |  val items =
+      |      items
+      |          .toMutableList()
+      |          .sdfkjsdf()
+      |          .sdfjksdflk()
+      |          .sdlfkjsldfj()
+      |          .sdfjksdflk()
+      |          .sdlfkjsldfj()
+      |          .apply {
+      |            //
+      |            foo
+      |          }
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `when two lambdas are in a chain, avoid block syntax`() =
+      assertFormatted(
+          """
+      |class Foo : Bar() {
+      |  fun doIt() {
+      |    fruit.onlyBananas().forEach { banana ->
+      |      val seeds = banana.find { it.type == SEED }
+      |      println(seeds)
+      |    }
+      |
+      |    fruit
+      |        .filter { isBanana(it, Bananas.types) }
+      |        .forEach { banana ->
+      |          val seeds = banana.find { it.type == SEED }
+      |          println(seeds)
+      |        }
+      |  }
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `don't one-line lambdas following parameter breaks`() =
+      assertFormatted(
+          """
+      |------------------------------------------------------------------------
+      |class Foo : Bar() {
+      |  fun doIt() {
+      |    // don't break in lambda, no parameter breaks found
+      |    fruit.forEach { eat(it) }
+      |
+      |    // break in the lambda because the closing paren gets attached
+      |    // to the last argument
+      |    fruit.forEach(
+      |        someVeryLongParameterNameThatWillCauseABreak,
+      |        evenWithoutATrailingCommaOnTheParameterListSoLetsSeeIt) {
+      |      eat(it)
+      |    }
+      |
+      |    // break in the lambda
+      |    fruit.forEach(
+      |        fromTheVine = true,
+      |    ) {
+      |      eat(it)
+      |    }
+      |
+      |    // don't break in the inner lambda, as nesting doesn't respect outer levels
+      |    fruit.forEach(
+      |        fromTheVine = true,
+      |    ) {
+      |      fruit.forEach { eat(it) }
+      |    }
+      |
+      |    // don't break in the lambda, as breaks don't propagate
+      |    fruit
+      |        .onlyBananas(
+      |            fromTheVine = true,
+      |        )
+      |        .forEach { eat(it) }
+      |
+      |    // don't break in the inner lambda, as breaks don't propagate to parameters
+      |    fruit.onlyBananas(
+      |        fromTheVine = true,
+      |        processThem = { eat(it) },
+      |    ) {
+      |      eat(it)
+      |    }
+      |
+      |    // don't break in the inner lambda, as breaks don't propagate to the body
+      |    fruit.onlyBananas(
+      |        fromTheVine = true,
+      |    ) {
+      |      val anon = { eat(it) }
+      |    }
+      |  }
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `indent parameters after a break when there's a lambda afterwards`() =
+      assertFormatted(
+          """
+      |---------------------------
+      |class C {
+      |  fun method() {
+      |    Foo.FooBar(
+      |            param1, param2)
+      |        .apply {
+      |          //
+      |          foo
+      |        }
+      |  }
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `no break between multi-line strings and their selectors`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |val STRING =
+      |    ""${'"'}
+      |    |foo
+      |    |""${'"'}.trimMargin()
+      |
+      |// This is a bug (line is longer than limit)
+      |// that we don't know how to avoid, for now.
+      |val STRING =
+      |    ""${'"'}
+      |    |foo
+      |    |----------------------------------""${'"'}.trimMargin()
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `import list`() {
+    val code =
+        """
+      | import  com .example.common.reality. FooBar
+      |  import  com .example.common.reality. FooBar2  as  foosBars
+      |   import com .example.common.reality. *
+      | import  foo.bar // Test
+      |  import  abc.def /*
+      |                  test */
+      |
+      |val x = FooBar.def { foosBars(bar) }
+      |""".trimMargin()
+    val expected =
+        """
+      |import abc.def /*
+      |               test */
+      |import com.example.common.reality.*
+      |import com.example.common.reality.FooBar
+      |import com.example.common.reality.FooBar2 as foosBars
+      |import foo.bar // Test
+      |
+      |val x = FooBar.def { foosBars(bar) }
+      |""".trimMargin()
+    assertThatFormatting(code).isEqualTo(expected)
+  }
+
+  @Test
+  fun `imports with trailing comments and expressions`() {
+    val code =
+        """
+        |import com.example.zab // test
+        |import com.example.foo ; val x = Sample(foo, zab)
+        |""".trimMargin()
+
+    val expected =
+        """
+        |import com.example.foo
+        |import com.example.zab // test
+        |
+        |val x = Sample(foo, zab)
+        |""".trimMargin()
+
+    assertThatFormatting(code).isEqualTo(expected)
+  }
+
+  @Test
+  fun `backticks are ignored in import sort order`() =
+      assertFormatted(
+          """
+      |import com.example.`if`
+      |import com.example.we
+      |import com.example.`when`
+      |import com.example.wow
+      |
+      |val x = `if` { we.`when`(wow) }
+      |""".trimMargin())
+
+  @Test
+  fun `backticks are ignored in import sort order ('as' directory)`() =
+      assertFormatted(
+          """
+      |import com.example.a as `if`
+      |import com.example.a as we
+      |import com.example.a as `when`
+      |import com.example.a as wow
+      |
+      |val x = `if` { we.`when`(wow) }
+      |""".trimMargin())
+
+  @Test
+  fun `imports are deduplicated`() {
+    val code =
+        """
+      |import com.example.b.*
+      |import com.example.b
+      |import com.example.a as `if`
+      |import com.example.a as we
+      |import com.example.a as `when`
+      |import com.example.a as wow
+      |import com.example.a as `when`
+      |
+      |val x = `if` { we.`when`(wow) } ?: b
+      |""".trimMargin()
+    val expected =
+        """
+      |import com.example.a as `if`
+      |import com.example.a as we
+      |import com.example.a as `when`
+      |import com.example.a as wow
+      |import com.example.b
+      |import com.example.b.*
+      |
+      |val x = `if` { we.`when`(wow) } ?: b
+      |""".trimMargin()
+    assertThatFormatting(code).isEqualTo(expected)
+  }
+
+  @Test
+  fun `unused imports are removed`() {
+    val code =
+        """
+      |import com.unused.Sample
+      |import com.used.FooBarBaz as Baz
+      |import com.used.bar // test
+      |import com.used.`class`
+      |import com.used.a.*
+      |import com.used.b as `if`
+      |import com.used.b as we
+      |import com.unused.a as `when`
+      |import com.unused.a as wow
+      |
+      |fun test(input: we) {
+      |  Baz(`class`)
+      |  `if` { bar }
+      |  val x = unused()
+      |}
+      |""".trimMargin()
+    val expected =
+        """
+      |import com.used.FooBarBaz as Baz
+      |import com.used.a.*
+      |import com.used.b as `if`
+      |import com.used.b as we
+      |import com.used.bar // test
+      |import com.used.`class`
+      |
+      |fun test(input: we) {
+      |  Baz(`class`)
+      |  `if` { bar }
+      |  val x = unused()
+      |}
+      |""".trimMargin()
+    assertThatFormatting(code).isEqualTo(expected)
+  }
+
+  @Test
+  fun `imports from the same package are removed`() {
+    val code =
+        """
+      |package com.example
+      |
+      |import com.example.Sample
+      |import com.example.Sample.CONSTANT
+      |import com.example.a.foo
+      |
+      |fun test() {
+      |  foo(CONSTANT, Sample())
+      |}
+      |""".trimMargin()
+    val expected =
+        """
+      |package com.example
+      |
+      |import com.example.Sample.CONSTANT
+      |import com.example.a.foo
+      |
+      |fun test() {
+      |  foo(CONSTANT, Sample())
+      |}
+      |""".trimMargin()
+    assertThatFormatting(code).isEqualTo(expected)
+  }
+
+  @Test
+  fun `keep import elements only mentioned in kdoc`() {
+    val code =
+        """
+          |package com.example.kdoc
+          |
+          |import com.example.Bar
+          |import com.example.Example
+          |import com.example.Foo
+          |import com.example.JavaDocLink
+          |import com.example.Param
+          |import com.example.R
+          |import com.example.ReturnedValue
+          |import com.example.Sample
+          |import com.example.unused
+          |import com.example.exception.AnException
+          |import com.example.kdoc.Doc
+          |
+          |/**
+          | * [Foo] is something only mentioned here, just like [R.layout.test] and [Doc].
+          | *
+          | * Old {@link JavaDocLink} that gets removed.
+          | *
+          | * @throws AnException
+          | * @exception Sample.SampleException
+          | * @param unused [Param]
+          | * @property JavaDocLink [Param]
+          | * @return [Unit] as [ReturnedValue]
+          | * @sample Example
+          | * @see Bar for more info
+          | * @throws AnException
+          | */
+          |class Dummy
+          |""".trimMargin()
+    val expected =
+        """
+          |package com.example.kdoc
+          |
+          |import com.example.Bar
+          |import com.example.Example
+          |import com.example.Foo
+          |import com.example.Param
+          |import com.example.R
+          |import com.example.ReturnedValue
+          |import com.example.Sample
+          |import com.example.exception.AnException
+          |
+          |/**
+          | * [Foo] is something only mentioned here, just like [R.layout.test] and [Doc].
+          | *
+          | * Old {@link JavaDocLink} that gets removed.
+          | *
+          | * @throws AnException
+          | * @exception Sample.SampleException
+          | * @param unused [Param]
+          | * @property JavaDocLink [Param]
+          | * @return [Unit] as [ReturnedValue]
+          | * @sample Example
+          | * @see Bar for more info
+          | * @throws AnException
+          | */
+          |class Dummy
+          |""".trimMargin()
+    assertThatFormatting(code).isEqualTo(expected)
+  }
+
+  @Test
+  fun `keep component imports`() =
+      assertFormatted(
+          """
+          |import com.example.component1
+          |import com.example.component10
+          |import com.example.component120
+          |import com.example.component2
+          |import com.example.component3
+          |import com.example.component4
+          |import com.example.component5
+          |""".trimMargin())
+
+  @Test
+  fun `keep operator imports`() =
+      assertFormatted(
+          """
+          |import com.example.and
+          |import com.example.compareTo
+          |import com.example.contains
+          |import com.example.dec
+          |import com.example.div
+          |import com.example.divAssign
+          |import com.example.equals
+          |import com.example.get
+          |import com.example.getValue
+          |import com.example.hasNext
+          |import com.example.inc
+          |import com.example.invoke
+          |import com.example.iterator
+          |import com.example.minus
+          |import com.example.minusAssign
+          |import com.example.mod
+          |import com.example.modAssign
+          |import com.example.next
+          |import com.example.not
+          |import com.example.or
+          |import com.example.plus
+          |import com.example.plusAssign
+          |import com.example.provideDelegate
+          |import com.example.rangeTo
+          |import com.example.rem
+          |import com.example.remAssign
+          |import com.example.set
+          |import com.example.setValue
+          |import com.example.times
+          |import com.example.timesAssign
+          |import com.example.unaryMinus
+          |import com.example.unaryPlus
+          |""".trimMargin())
+
+  @Test
+  fun `keep unused imports when formatting options has feature turned off`() {
+    val code =
+        """
+      |import com.unused.FooBarBaz as Baz
+      |import com.unused.Sample
+      |import com.unused.a as `when`
+      |import com.unused.a as wow
+      |import com.unused.a.*
+      |import com.unused.b as `if`
+      |import com.unused.b as we
+      |import com.unused.bar // test
+      |import com.unused.`class`
+      |""".trimMargin()
+
+    assertThatFormatting(code)
+        .withOptions(FormattingOptions(removeUnusedImports = false))
+        .isEqualTo(code)
+  }
+
+  @Test
+  fun `comments between imports are moved above import list`() {
+    val code =
+        """
+        |package com.facebook.ktfmt
+        |
+        |/* leading comment */
+        |import com.example.abc
+        |/* internal comment 1 */
+        |import com.example.bcd
+        |// internal comment 2
+        |import com.example.Sample
+        |// trailing comment
+        |
+        |val x = Sample(abc, bcd)
+        |""".trimMargin()
+    val expected =
+        """
+        |package com.facebook.ktfmt
+        |
+        |/* leading comment */
+        |/* internal comment 1 */
+        |// internal comment 2
+        |import com.example.Sample
+        |import com.example.abc
+        |import com.example.bcd
+        |
+        |// trailing comment
+        |
+        |val x = Sample(abc, bcd)
+        |""".trimMargin()
+    assertThatFormatting(code).isEqualTo(expected)
+  }
+
+  @Test
+  fun `no redundant newlines when there are no imports`() =
+      assertFormatted(
+          """
+        |package foo123
+        |
+        |/*
+        |bar
+        |*/
+        |""".trimMargin())
+
+  @Test
+  fun `basic annotations`() =
+      assertFormatted(
+          """
+      |@Fancy
+      |class Foo {
+      |  @Fancy
+      |  fun baz(@Fancy foo: Int) {
+      |    @Fancy val a = 1 + foo
+      |  }
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `function calls with multiple arguments`() =
+      assertFormatted(
+          """
+      |fun f() {
+      |  foo(1, 2, 3)
+      |
+      |  foo(
+      |      123456789012345678901234567890,
+      |      123456789012345678901234567890,
+      |      123456789012345678901234567890)
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `function calls with multiple named arguments`() =
+      assertFormatted(
+          """
+      |fun f() {
+      |  foo(1, b = 2, c = 3)
+      |
+      |  foo(
+      |      123456789012345678901234567890,
+      |      b = 23456789012345678901234567890,
+      |      c = 3456789012345678901234567890)
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `named arguments indent their value expression`() =
+      assertFormatted(
+          """
+      |fun f() =
+      |    Bar(
+      |        tokens =
+      |            mutableListOf<Token>().apply {
+      |              // Printing
+      |              print()
+      |            },
+      |        duration = duration)
+      |""".trimMargin())
+
+  @Test
+  fun `Arguments are blocks`() =
+      assertFormatted(
+          """
+      |--------------------------------------------------
+      |override fun visitProperty(property: KtProperty) {
+      |  builder.sync(property)
+      |  builder.block(ZERO) {
+      |    declareOne(
+      |        kind = DeclarationKind.FIELD,
+      |        modifiers = property.modifierList,
+      |        valOrVarKeyword =
+      |            property.valOrVarKeyword.text,
+      |        typeParameters =
+      |            property.typeParameterList,
+      |        receiver = property.receiverTypeReference,
+      |        name = property.nameIdentifier?.text,
+      |        type = property.typeReference,
+      |        typeConstraintList =
+      |            property.typeConstraintList,
+      |        delegate = property.delegate,
+      |        initializer = property.initializer)
+      |  }
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `anonymous function`() =
+      assertFormatted(
+          """
+      |fun f() {
+      |  setListener(
+      |      fun(number: Int) {
+      |        println(number)
+      |      })
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `anonymous function with receiver`() =
+      assertFormatted(
+          """
+      |fun f() {
+      |  setListener(
+      |      fun View.() {
+      |        println(this)
+      |      })
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `when() with a subject expression`() =
+      assertFormatted(
+          """
+      |fun f(x: Int) {
+      |  when (x) {
+      |    1 -> print(1)
+      |    2 -> print(2)
+      |    3 ->
+      |        // Comment
+      |        print(3)
+      |    else -> {
+      |      print("else")
+      |    }
+      |  }
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `when() expression with complex predicates`() =
+      assertFormatted(
+          """
+      |fun f(x: Int) {
+      |  when {
+      |    x == 1 || x == 2 -> print(1)
+      |    x == 3 && x != 4 -> print(2)
+      |    else -> {
+      |      print(3)
+      |    }
+      |  }
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `when() expression with several conditions`() =
+      assertFormatted(
+          """
+      |fun f(x: Int) {
+      |  when {
+      |    0,
+      |    1 -> print(1)
+      |    else -> print(0)
+      |  }
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `when() expression with is and in`() =
+      assertFormatted(
+          """
+      |fun f(x: Int) {
+      |  when (x) {
+      |    is String -> print(1)
+      |    !is String -> print(2)
+      |    in 1..3 -> print()
+      |    in a..b -> print()
+      |    in a..3 -> print()
+      |    in 1..b -> print()
+      |    !in 1..b -> print()
+      |    else -> print(3)
+      |  }
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `when() expression with enum values`() =
+      assertFormatted(
+          """
+      |fun f(x: Color) {
+      |  when (x) {
+      |    is Color.Red -> print(1)
+      |    is Color.Green -> print(2)
+      |    else -> print(3)
+      |  }
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `when() expression with generic matcher and exhaustive`() =
+      assertFormatted(
+          """
+      |fun f(x: Result) {
+      |  when (x) {
+      |    is Success<*> -> print(1)
+      |    is Failure -> print(2)
+      |  }.exhaustive
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `when() expression with multiline condition`() =
+      assertFormatted(
+          """
+      |--------------------------
+      |fun foo() {
+      |  when (expressions1 +
+      |      expression2 +
+      |      expression3) {
+      |    1 -> print(1)
+      |    2 -> print(2)
+      |  }
+      |
+      |  when (foo(
+      |      expressions1 &&
+      |          expression2 &&
+      |          expression3)) {
+      |    1 -> print(1)
+      |    2 -> print(2)
+      |  }
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `lambda assigned to variable does not break before brace`() =
+      assertFormatted(
+          """
+      |fun doIt() {
+      |  val lambda = {
+      |    doItOnce()
+      |    doItTwice()
+      |  }
+      |}
+      |
+      |fun foo() = {
+      |  doItOnce()
+      |  doItTwice()
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `when() expression storing in local variable`() =
+      assertFormatted(
+          """
+      |fun f(x: Result) {
+      |  when (val y = x.improved()) {
+      |    is Success<*> -> print(y)
+      |    is Failure -> print(2)
+      |  }
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `line breaks inside when expressions and conditions`() =
+      assertFormatted(
+          """
+      |fun f() {
+      |  return Text.create(c)
+      |      .onTouch {
+      |        when (it.motionEvent.action) {
+      |          ACTION_DOWN ->
+      |              Toast.makeText(it.view.context, "Down!", Toast.LENGTH_SHORT, blablabla).show()
+      |          ACTION_UP -> Toast.makeText(it.view.context, "Up!", Toast.LENGTH_SHORT).show()
+      |          ACTION_DOWN ->
+      |              Toast.makeText(
+      |                      it.view.context, "Down!", Toast.LENGTH_SHORT, blablabla, blablabl, blabla)
+      |                  .show()
+      |        }
+      |      }
+      |      .build()
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `function return types`() =
+      assertFormatted(
+          """
+      |fun f1(): Int = 0
+      |
+      |fun f2(): Int {}
+      |""".trimMargin())
+
+  @Test
+  fun `multi line function without a block body`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |fun longFunctionNoBlock():
+      |    Int =
+      |    1234567 + 1234567
+      |
+      |fun shortFun(): Int =
+      |    1234567 + 1234567
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `return type doesn't fit in one line`() =
+      assertFormatted(
+          """
+      |--------------------------------------------------
+      |interface X {
+      |  fun f(
+      |      arg1: Arg1Type,
+      |      arg2: Arg2Type
+      |  ): Map<String, Map<String, Double>>? {
+      |    //
+      |  }
+      |
+      |  fun functionWithGenericReturnType(
+      |      arg1: Arg1Type,
+      |      arg2: Arg2Type
+      |  ): Map<String, Map<String, Double>>? {
+      |    //
+      |  }
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `list of superclasses`() =
+      assertFormatted(
+          """
+      |class Derived2 : Super1, Super2 {}
+      |
+      |class Derived1 : Super1, Super2
+      |
+      |class Derived3(a: Int) : Super1(a)
+      |
+      |class Derived4 : Super1()
+      |
+      |class Derived5 : Super3<Int>()
+      |""".trimMargin())
+
+  @Test
+  fun `list of superclasses over multiple lines`() =
+      assertFormatted(
+          """
+      |--------------------
+      |class Derived2 :
+      |    Super1,
+      |    Super2 {}
+      |
+      |class Derived1 :
+      |    Super1, Super2
+      |
+      |class Derived3(
+      |    a: Int
+      |) : Super1(a)
+      |
+      |class Derived4 :
+      |    Super1()
+      |
+      |class Derived5 :
+      |    Super3<Int>()
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `annotations with parameters`() =
+      assertFormatted("""
+      |@AnnWithArrayValue(1, 2, 3) class C
+      |""".trimMargin())
+
+  @Test
+  fun `method modifiers`() =
+      assertFormatted("""
+      |override internal fun f() {}
+      |""".trimMargin())
+
+  @Test
+  fun `class modifiers`() =
+      assertFormatted(
+          """
+      |abstract class Foo
+      |
+      |inner class Foo
+      |
+      |final class Foo
+      |
+      |open class Foo
+      |""".trimMargin())
+
+  @Test
+  fun `kdoc comments`() {
+    val code =
+        """
+      |/**
+      | * foo
+      | */ class F {
+      |
+      | }
+      |""".trimMargin()
+    val expected = """
+      |/** foo */
+      |class F {}
+      |""".trimMargin()
+    assertThatFormatting(code).isEqualTo(expected)
+  }
+
+  @Test
+  fun `nested kdoc comments`() {
+    val code =
+        """
+      |/**
+      | * foo /* bla */
+      | */ class F {
+      |
+      | }
+      |""".trimMargin()
+    val expected = """
+      |/** foo /* bla */ */
+      |class F {}
+      |""".trimMargin()
+    assertThatFormatting(code).isEqualTo(expected)
+  }
+
+  @Test
+  fun `nested kdoc inside code block`() =
+      assertFormatted(
+          """
+      |/**
+      | * ```
+      | * edit -> { /* open edit screen */ }
+      | * ```
+      | */
+      |fun foo() {}
+      |""".trimMargin())
+
+  @Test
+  fun `formatting kdoc doesn't add p HTML tags`() =
+      assertFormatted(
+          """
+      |/**
+      | * Bla bla bla bla
+      | *
+      | * This is an inferred paragraph, and as you can see, we don't add a p tag to it, even though bla
+      | * bla.
+      | *
+      | * <p>On the other hand, we respect existing tags, and don't remove them.
+      | */
+      |""".trimMargin())
+
+  @Test
+  fun `formatting kdoc preserves lists`() =
+      assertFormatted(
+          """
+      |/**
+      | * Here are some fruit I like:
+      | * - Banana
+      | * - Apple
+      | *
+      | * This is another paragraph
+      | */
+      |""".trimMargin())
+
+  @Test
+  fun `formatting kdoc lists with line wraps breaks and merges correctly`() {
+    val code =
+        """
+      |/**
+      | * Here are some fruit I like:
+      | * - Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana
+      | * - Apple Apple Apple Apple
+      | *   Apple Apple
+      | *
+      | * This is another paragraph
+      | */
+      |""".trimMargin()
+    val expected =
+        """
+      |/**
+      | * Here are some fruit I like:
+      | * - Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana
+      | * Banana Banana Banana Banana Banana
+      | * - Apple Apple Apple Apple Apple Apple
+      | *
+      | * This is another paragraph
+      | */
+      |""".trimMargin()
+    assertThatFormatting(code).isEqualTo(expected)
+  }
+
+  @Test
+  fun `too many spaces on list continuation mean it's a code block, so mark it accordingly`() {
+    val code =
+        """
+      |/**
+      | * Here are some fruit I like:
+      | * - Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana
+      | *     Banana Banana Banana Banana Banana
+      | */
+      |""".trimMargin()
+    val expected =
+        """
+      |/**
+      | * Here are some fruit I like:
+      | * - Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana Banana
+      | * ```
+      | *     Banana Banana Banana Banana Banana
+      | * ```
+      | */
+      |""".trimMargin()
+    assertThatFormatting(code).isEqualTo(expected)
+  }
+
+  @Test
+  fun `add explicit code markers around indented code`() {
+    val code =
+        """
+      |/**
+      | * This is a code example:
+      | *
+      | *     this_is_code()
+      | *
+      | * This is not code again
+      | */
+      |""".trimMargin()
+    val expected =
+        """
+      |/**
+      | * This is a code example:
+      | * ```
+      | *     this_is_code()
+      | * ```
+      | * This is not code again
+      | */
+      |""".trimMargin()
+    assertThatFormatting(code).isEqualTo(expected)
+  }
+
+  @Test
+  fun `formatting kdoc preserves lists of asterisks`() =
+      assertFormatted(
+          """
+      |/**
+      | * Here are some fruit I like:
+      | * * Banana
+      | * * Apple
+      | *
+      | * This is another paragraph
+      | */
+      |""".trimMargin())
+
+  @Test
+  fun `formatting kdoc preserves numbered`() =
+      assertFormatted(
+          """
+      |/**
+      | * Here are some fruit I like:
+      | * 1. Banana
+      | * 2. Apple
+      | *
+      | * This is another paragraph
+      | */
+      |""".trimMargin())
+
+  @Test
+  fun `formatting kdoc with markdown errors`() =
+      assertFormatted(
+          """
+      |/** \[ */
+      |fun markdownError() = Unit
+      |""".trimMargin())
+
+  @Test
+  fun `return statement with value`() =
+      assertFormatted(
+          """
+      |fun random(): Int {
+      |  return 4
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `return statement without value`() =
+      assertFormatted(
+          """
+      |fun print(b: Boolean) {
+      |  print(b)
+      |  return
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `return expression without value`() =
+      assertFormatted(
+          """
+      |fun print(b: Boolean?) {
+      |  print(b ?: return)
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `if statement without else`() =
+      assertFormatted(
+          """
+      |fun maybePrint(b: Boolean) {
+      |  if (b) {
+      |    println(b)
+      |  }
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `if statement with else`() =
+      assertFormatted(
+          """
+      |fun maybePrint(b: Boolean) {
+      |  if (b) {
+      |    println(2)
+      |  } else {
+      |    println(1)
+      |  }
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `if expression with else`() =
+      assertFormatted(
+          """
+      |fun maybePrint(b: Boolean) {
+      |  println(if (b) 1 else 2)
+      |  println(
+      |      if (b) {
+      |        val a = 1 + 1
+      |        2 * a
+      |      } else 2)
+      |  return if (b) 1 else 2
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `if expression with break before else`() =
+      assertFormatted(
+          """
+      |------------------------------
+      |fun compute(b: Boolean) {
+      |  val c =
+      |      if (a + b < 20) a + b
+      |      else a
+      |  return if (a + b < 20) a + b
+      |  else c
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `if expression with break before expressions`() =
+      assertFormatted(
+          """
+      |--------------------------
+      |fun compute(b: Boolean) {
+      |  val c =
+      |      if (a + b < 20)
+      |          a + b
+      |      else if (a < 20) a
+      |      else
+      |          a + b + b + 1000
+      |  return if (a + b < 20)
+      |      a + b
+      |  else c
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `blocky expressions in if-else`() =
+      assertFormatted(
+          """
+      |fun numbers() {
+      |  if (true)
+      |      do {
+      |        eat("is")
+      |        matches += type()
+      |      } while (eat(","))
+      |  else
+      |      while (1 < 2) {
+      |        println("Everything is okay")
+      |      }
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `if expression with multiline condition`() =
+      assertFormatted(
+          """
+      |----------------------------
+      |fun foo() {
+      |  if (expressions1 &&
+      |      expression2 &&
+      |      expression3) {
+      |    bar()
+      |  }
+      |
+      |  if (foo(
+      |      expressions1 &&
+      |          expression2 &&
+      |          expression3)) {
+      |    bar()
+      |  }
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `assignment expression on multiple lines`() =
+      assertFormatted(
+          """
+      |--------------------------------------------------
+      |fun f() {
+      |  var myVariable = 5
+      |  myVariable =
+      |      function1(4, 60, 8) + function2(57, 39, 20)
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `A program that tickled a bug in KotlinInput`() =
+      assertFormatted("""
+      |val x = 2
+      |""".trimMargin())
+
+  @Test
+  fun `a few variations of constructors`() =
+      assertFormatted(
+          """
+      |class Foo constructor(number: Int) {}
+      |
+      |class Foo2 private constructor(number: Int) {}
+      |
+      |class Foo3 @Inject constructor(number: Int) {}
+      |
+      |class Foo4 @Inject private constructor(number: Int) {}
+      |
+      |class Foo5
+      |@Inject
+      |private constructor(
+      |    number: Int,
+      |    number2: Int,
+      |    number3: Int,
+      |    number4: Int,
+      |    number5: Int,
+      |    number6: Int
+      |) {}
+      |""".trimMargin())
+
+  @Test
+  fun `a primary constructor without a class body `() =
+      assertFormatted(
+          """
+      |-------------------------
+      |data class Foo(
+      |    val number: Int = 0
+      |)
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `a secondary constructor without a body`() =
+      assertFormatted(
+          """
+      |---------------------------
+      |data class Foo {
+      |  constructor(
+      |      val number: Int = 0
+      |  )
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `a secondary constructor with a body breaks before closing parenthesis`() =
+      assertFormatted(
+          """
+      |---------------------------
+      |data class Foo {
+      |  constructor(
+      |      val number: Int = 0
+      |  ) {}
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `a constructor with many arguments over breaking to next line`() =
+      assertFormatted(
+          """
+      |data class Foo(
+      |    val number: Int,
+      |    val name: String,
+      |    val age: Int,
+      |    val title: String,
+      |    val offspring2: List<Foo>
+      |) {}
+      |""".trimMargin())
+
+  @Test
+  fun `a constructor with keyword and many arguments over breaking to next line`() =
+      assertFormatted(
+          """
+      |data class Foo
+      |constructor(
+      |    val name: String,
+      |    val age: Int,
+      |    val title: String,
+      |    val offspring: List<Foo>,
+      |    val foo: String
+      |) {}
+      |""".trimMargin())
+
+  @Test
+  fun `a constructor with many arguments over multiple lines`() =
+      assertFormatted(
+          """
+      |--------------------------------------------------
+      |data class Foo
+      |constructor(
+      |    val number: Int,
+      |    val name: String,
+      |    val age: Int,
+      |    val title: String,
+      |    val offspring: List<Foo>
+      |) {}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `handle secondary constructors`() =
+      assertFormatted(
+          """
+      |class Foo private constructor(number: Int) {
+      |  private constructor(n: Float) : this(1)
+      |  private constructor(n: Double) : this(1) {
+      |    println("built")
+      |  }
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `a secondary constructor with many arguments over multiple lines`() =
+      assertFormatted(
+          """
+      |--------------------------------------------------
+      |data class Foo {
+      |  constructor(
+      |      val number: Int,
+      |      val name: String,
+      |      val age: Int,
+      |      val title: String,
+      |      val offspring: List<Foo>
+      |  )
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `a secondary constructor with many arguments passed to delegate`() =
+      assertFormatted(
+          """
+      |--------------------------------------------------
+      |data class Foo {
+      |  constructor(
+      |      val number: Int,
+      |      val name: String,
+      |      val age: Int,
+      |      val title: String,
+      |      val offspring: List<Foo>
+      |  ) : this(
+      |      number,
+      |      name,
+      |      age,
+      |      title,
+      |      offspring,
+      |      offspring)
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `a secondary constructor with no arguments passed to delegate`() =
+      assertFormatted(
+          """
+      |--------------------------------------------------
+      |data class Foo {
+      |  constructor() :
+      |      this(
+      |          Foo.createSpeciallyDesignedParameter(),
+      |          Foo.createSpeciallyDesignedParameter(),
+      |      )
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `secondary constructor with param list that fits in one line, with delegate`() =
+      assertFormatted(
+          """
+      |class C {
+      |  constructor(
+      |      context: Context?,
+      |      attrs: AttributeSet?,
+      |      defStyleAttr: Int,
+      |      defStyleRes: Int
+      |  ) : super(context, attrs, defStyleAttr, defStyleRes) {
+      |    init(attrs)
+      |  }
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `handle calling super constructor in secondary constructor`() =
+      assertFormatted(
+          """
+      |class Foo : Bar {
+      |  internal constructor(number: Int) : super(number) {}
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `handle super statement with with type argument`() =
+      assertFormatted(
+          """
+      |class Foo : Bar(), FooBar {
+      |  override fun doIt() {
+      |    super<FooBar>.doIt()
+      |  }
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `handle super statement with with label argument`() =
+      assertFormatted(
+          """
+      |class Foo : Bar(), FooBar {
+      |  override fun doIt() {
+      |    foo.doThat {
+      |      super<FooBar>@Foo.doIt()
+      |
+      |      // this one is actually generics on the call expression, not super
+      |      super@Foo<FooBar>.doIt()
+      |    }
+      |  }
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `primary constructor without parameters with a KDoc`() =
+      assertFormatted(
+          """
+      |class Class
+      |/** A comment */
+      |constructor() {}
+      |""".trimMargin())
+
+  @Test
+  fun `handle objects`() = assertFormatted("""
+      |object Foo(n: Int) {}
+      |""".trimMargin())
+
+  @Test
+  fun `handle object expression`() =
+      assertFormatted(
+          """
+      |fun f(): Any {
+      |  return object : Adapter() {}
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `handle object expression in parenthesis`() =
+      assertFormatted(
+          """
+      |fun f(): Any {
+      |  return (object : Adapter() {})
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `handle array indexing operator`() =
+      assertFormatted(
+          """
+      |fun f(a: Magic) {
+      |  a[3]
+      |  b[3, 4]
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `keep array indexing grouped with expression is possible`() =
+      assertFormatted(
+          """
+      |-----------------------
+      |fun f(a: Magic) {
+      |  foo.bar()
+      |      .foobar[1, 2, 3]
+      |  foo.bar()
+      |      .foobar[
+      |          1,
+      |          2,
+      |          3,
+      |          4,
+      |          5]
+      |  foo.bar()
+      |      .foobar[1, 2, 3]
+      |      .barfoo[3, 2, 1]
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `mixed chains`() =
+      assertFormatted(
+          """
+      |-----------------------
+      |fun f(a: Magic) {
+      |  foo.bar()
+      |      .foobar[1, 2, 3]
+      |  foo.bar()
+      |      .foobar[
+      |          1,
+      |          2,
+      |          3,
+      |          4,
+      |          5]
+      |  foo.bar()
+      |      .foobar[1, 2, 3]
+      |      .barfoo[3, 2, 1]
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `handle destructuring declaration`() =
+      assertFormatted(
+          """
+      |-----------------------------------------------
+      |fun f() {
+      |  val (a, b: Int) = listOf(1, 2)
+      |  val (asd, asd, asd, asd, asd, asd, asd) =
+      |      foo.bar(asdasd, asdasd)
+      |
+      |  val (accountType, accountId) =
+      |      oneTwoThreeFourFiveSixSeven(
+      |          foo, bar, zed, boo)
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+  @Test
+  fun `chains with derferences and array indexing`() =
+      assertFormatted(
+          """
+      |-----------------------
+      |fun f() {
+      |  foo.bam()
+      |      .uber!![0, 1, 2]
+      |      .boom()[1, 3, 5]
+      |      .lah
+      |      .doo { it }
+      |      .feep[1]
+      |      as Boo
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `block like syntax after dereferences and indexing with short lines`() =
+      assertFormatted(
+          """
+      |-----------------------
+      |fun f() {
+      |  foo.bam()
+      |      .uber!![0, 1, 2]
+      |      .forEach {
+      |        println(it)
+      |      }
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `block like syntax after dereferences and indexing with long lines`() =
+      assertFormatted(
+          """
+      |----------------------------------
+      |fun f() {
+      |  foo.uber!![0, 1, 2].forEach {
+      |    println(it)
+      |  }
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `try to keep type names together`() =
+      assertFormatted(
+          """
+      |-----------------------
+      |fun f() {
+      |  com.facebook.foo.Foo(
+      |      1, 2)
+      |  com.facebook.foo
+      |      .Foo(1, 2)
+      |      .andAlsoThis()
+      |  com.facebook.Foo.foo(
+      |      1, 2)
+      |  com.facebook
+      |      .foobarFoo
+      |      .foo(1, 2)
+      |  foo.invoke(
+      |      foo, bar, bar)
+      |  foo.invoke(foo, bar)
+      |      .invoke()
+      |  FooFoo.foooooooo()
+      |      .foooooooo()
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `avoid breaking brackets and keep them with array name`() =
+      assertFormatted(
+          """
+      |-------------------------------------------------------------------------
+      |fun f() {
+      |  val a =
+      |      invokeIt(context.packageName)
+      |          .getInternalMutablePackageInfo(context.packageName)
+      |          .someItems[0]
+      |          .getInternalMutablePackageInfo(context.packageName)
+      |          .someItems[0]
+      |          .doIt()
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `keep function call with type name even if array expression is next`() =
+      assertFormatted(
+          """
+      |class f {
+      |  private val somePropertyWithBackingOne
+      |    get() =
+      |        _somePropertyWithBackingOne
+      |            ?: Classname.getStuff<SomePropertyRelatedClassProvider>(requireContext())[
+      |                    somePropertiesProvider, somePropertyCallbacks]
+      |                .also { _somePropertyWithBackingOne = it }
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `array access in middle of chain and end of it behaves similarly`() =
+      assertFormatted(
+          """
+      |--------------------------------------
+      |fun f() {
+      |  if (aaaaa == null ||
+      |      aaaaa.bbbbb[0] == null ||
+      |      aaaaa.bbbbb[0].cc == null ||
+      |      aaaaa.bbbbb[0].dddd == null) {
+      |    println()
+      |  }
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `handle qmark for nullalble types`() =
+      assertFormatted(
+          """
+      |fun doItWithNullReturns(a: String, b: String): Int? {
+      |  return 5
+      |}
+      |
+      |fun doItWithNulls(a: String, b: String?) {}
+      |""".trimMargin())
+
+  @Test
+  fun `nullable function type`() =
+      assertFormatted("""
+      |var listener: ((Boolean) -> Unit)? = null
+      |""".trimMargin())
+
+  @Test
+  fun `redundant parenthesis in function types`() =
+      assertFormatted(
+          """
+      |val a: (Int) = 7
+      |
+      |var listener: ((Boolean) -> Unit) = foo
+      |""".trimMargin())
+
+  @Test
+  fun `handle string literals`() =
+      assertFormatted(
+          """
+      |fun doIt(world: String) {
+      |  println("Hello world!")
+      |  println("Hello! ${'$'}world")
+      |  println("Hello! ${'$'}{"wor" + "ld"}")
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `handle multiline string literals`() =
+      assertFormatted(
+          """
+      |fun doIt(world: String) {
+      |  println(${"\"".repeat(3)}Hello
+      |      world!${"\"".repeat(3)})
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `Trailing whitespaces are preserved in multiline strings`() {
+    val code =
+        listOf(
+                "fun doIt(world: String) {",
+                "  println(\"\"\"This line has trailing whitespace         ",
+                "      world!\"\"\")",
+                "  println(\"\"\"This line has trailing whitespace \$s     ",
+                "      world!\"\"\")",
+                "  println(\"\"\"This line has trailing whitespace \${s}   ",
+                "      world!\"\"\")",
+                "  println(\"\"\"This line has trailing whitespace \$      ",
+                "      world!\"\"\")",
+                "}",
+                "")
+            .joinToString("\n")
+    assertThatFormatting(code).allowTrailingWhitespace().isEqualTo(code)
+  }
+
+  @Test
+  fun `Consecutive line breaks in multiline strings are preserved`() =
+      assertFormatted(
+          """
+      |val x = ""${'"'}
+      |
+      |
+      |
+      |Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
+      |""${'"'}
+      |""".trimMargin())
+
+  @Test
+  fun `Trailing spaces in a comment are not preserved`() {
+    val before =
+        listOf("// trailing spaces in a comment are not preserved       ", "").joinToString("\n")
+    val after = listOf("// trailing spaces in a comment are not preserved", "").joinToString("\n")
+    assertThatFormatting(before).allowTrailingWhitespace().isEqualTo(after)
+  }
+
+  @Test
+  fun `Code with tombstones is not supported`() {
+    val code = """
+      |fun good() {
+      |  // ${'\u0003'}
+      |}
+      |""".trimMargin()
+    try {
+      Formatter.format(code)
+      fail()
+    } catch (e: ParseError) {
+      assertThat(e.errorDescription).contains("\\u0003")
+      assertThat(e.lineColumn.line).isEqualTo(1)
+      assertThat(e.lineColumn.column).isEqualTo(5)
+    }
+  }
+
+  @Test
+  fun `handle some basic generics scenarios`() =
+      assertFormatted(
+          """
+      |fun <T> doIt(a: List<T>): List<Int>? {
+      |  val b: List<Int> = convert<Int>(listOf(5, 4))
+      |  return b
+      |}
+      |
+      |class Foo<T>
+      |""".trimMargin())
+
+  @Test
+  fun `handle for loops`() =
+      assertFormatted(
+          """
+      |fun f(a: List<Int>) {
+      |  for (i in a.indices) {
+      |    println(i)
+      |  }
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `handle for loops with long dot chains`() =
+      assertFormatted(
+          """
+      |-----------------------------------
+      |fun f(a: Node<Int>) {
+      |  for (child in node.next.data()) {
+      |    println(child)
+      |  }
+      |  for (child in
+      |      node.next.next.data()) {
+      |    println(child)
+      |  }
+      |  for (child in
+      |      node.next.next.next.next
+      |          .data()) {
+      |    println(child)
+      |  }
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `when two lambdas following a call, indent the lambda properly`() =
+      assertFormatted(
+          """
+      |----------------------------
+      |fun f() {
+      |  doIt()
+      |      .apply {
+      |        number =
+      |            computeNumber1()
+      |      }
+      |      .apply {
+      |        number = 2 * number
+      |      }
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `when two lambdas following a field, indent the lambda properly`() =
+      assertFormatted(
+          """
+      |----------------------------
+      |fun f() {
+      |  field
+      |      .apply {
+      |        number =
+      |            computeNumber1()
+      |      }
+      |      .apply {
+      |        number = 2 * number
+      |      }
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `break after 'four' (even though it's 4 chars long) because there's a lambda afterwards`() =
+      assertFormatted(
+          """
+      |fun f() {
+      |  four
+      |      .let {
+      |        //
+      |        foo()
+      |      }
+      |      .methodCall()
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `keep last expression in qualified indented`() =
+      assertFormatted(
+          """
+      |----------------------------
+      |fun f() {
+      |  Stuff()
+      |      .doIt(
+      |          Foo.doIt()
+      |              .doThat())
+      |      .doIt(
+      |          Foo.doIt()
+      |              .doThat())
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `properly place lambda arguments into blocks`() =
+      assertFormatted(
+          """
+      |-----------------------
+      |fun f() {
+      |  foo {
+      |    red.orange.yellow()
+      |  }
+      |
+      |  foo.bar {
+      |    red.orange.yellow()
+      |  }
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `properly handle one statement lambda with comment`() =
+      assertFormatted(
+          """
+      |-----------------------
+      |fun f() {
+      |  foo {
+      |    // this is a comment
+      |    red.orange.yellow()
+      |  }
+      |  foo {
+      |    /* this is also a comment */
+      |    red.orange.yellow()
+      |  }
+      |  foo.bar {
+      |    // this is a comment
+      |    red.orange.yellow()
+      |  }
+      |  foo.bar() {
+      |    // this is a comment
+      |    red.orange.yellow()
+      |  }
+      |  foo.bar {
+      |    /* this is also a comment */
+      |    red.orange.yellow()
+      |  }
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `properly handle one statement lambda with comment after body statements`() =
+      assertFormatted(
+          """
+      |-----------------------
+      |fun f() {
+      |  foo {
+      |    red.orange.yellow()
+      |    // this is a comment
+      |  }
+      |  foo {
+      |    red.orange.yellow()
+      |    /* this is also a comment */
+      |  }
+      |  foo.bar {
+      |    red.orange.yellow()
+      |    // this is a comment
+      |  }
+      |  foo.bar() {
+      |    red.orange.yellow()
+      |    // this is a comment
+      |  }
+      |  foo.bar {
+      |    red.orange.yellow()
+      |    /* this is also a comment */
+      |  }
+      |  red.orange.yellow()
+      |  // this is a comment
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `try to keep expression in the same line until the first lambda`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |fun f() {
+      |  foo.bar.bar?.let {
+      |    a()
+      |  }
+      |  foo.bar.bar?.let {
+      |    action()
+      |    action2()
+      |  }
+      |  foo.bar.bar.bar.bar
+      |      ?.let { a() }
+      |  foo.bar.bar.bar.bar
+      |      ?.let {
+      |        action()
+      |        action2()
+      |      }
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `different indentation in chained calls`() =
+      assertFormatted(
+          """
+      |---------------------------
+      |fun f() {
+      |  fooDdoIt(
+      |      foo1, foo2, foo3)
+      |  foo.doIt(
+      |      foo1, foo2, foo3)
+      |  foo.doIt(
+      |          foo1, foo2, foo3)
+      |      .doThat()
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `always add a conditional break for a lambda which is not last`() =
+      assertFormatted(
+          """
+      |--------------------
+      |fun f() {
+      |  foofoo
+      |      .doIt {
+      |        doStuff()
+      |      }
+      |      .doIt {
+      |        doStuff()
+      |      }
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `keep parenthesis and braces together when there's only one lambda argument`() =
+      assertFormatted(
+          """
+      |fun f() {
+      |  doIt({})
+      |  doIt({ it + it })
+      |  doIt({
+      |    val a = it
+      |    a + a
+      |  })
+      |  doIt(functor = { it + it })
+      |  doIt(
+      |      functor = {
+      |        val a = it
+      |        a + a
+      |      })
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `Qualified type`() =
+      assertFormatted(
+          """
+      |fun f() {
+      |  var plusFour: Indent.Const
+      |  var x: Map.Entry<String, Integer>
+      |  var x: List<String>.Iterator
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `handle destructuring declaration in for loop`() =
+      assertFormatted(
+          """
+      |fun f(a: List<Pair<Int, Int>>) {
+      |  for ((x, y: Int) in a) {}
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `handle function references`() =
+      assertFormatted(
+          """
+      |--------------------------------
+      |fun f(a: List<Int>) {
+      |  a.forEach(::println)
+      |  a.map(Int::toString)
+      |  a.map(String?::isNullOrEmpty)
+      |  a.map(
+      |      SuperLongClassName?::
+      |          functionName)
+      |  val f =
+      |      SuperLongClassName::
+      |          functionName
+      |  val g =
+      |      invoke(a, b)::functionName
+      |  val h =
+      |      invoke(a, b, c)::
+      |          functionName
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `handle escaped identifier`() =
+      assertFormatted(
+          """
+      |import foo as `foo foo`
+      |import org.mockito.Mockito.`when` as `yay yay`
+      |
+      |fun `spaces in functions`() {
+      |  val `when` = NEVER
+      |  val (`do not`, `ever write`) = SERIOUSLY
+      |  val `a a`: Int
+      |  `yay yay`(`foo foo`)
+      |}
+      |
+      |class `more spaces`
+      |""".trimMargin())
+
+  @Test
+  fun `handle annotations with arguments`() =
+      assertFormatted(
+          """
+      |@Px fun f(): Int = 5
+      |
+      |@Dimenstion(unit = DP) fun g(): Int = 5
+      |
+      |@RunWith(MagicRunner::class)
+      |@Px
+      |class Test {
+      |  //
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `no newlines after annotations if entire expr fits in one line`() =
+      assertFormatted(
+          """
+      |-----------------------------------------------
+      |@Px @Px fun f(): Int = 5
+      |
+      |@Px
+      |@Px
+      |@Px
+      |@Px
+      |@Px
+      |@Px
+      |@Px
+      |@Px
+      |fun f(): Int = 5
+      |
+      |@Px
+      |@Px
+      |fun f(): Int {
+      |  return 5
+      |}
+      |
+      |@Dimenstion(unit = DP) @Px fun g(): Int = 5
+      |
+      |@Dimenstion(unit = DP)
+      |@Px
+      |fun g(): Int {
+      |  return 5
+      |}
+      |
+      |@RunWith @Px class Test
+      |
+      |@RunWith(MagicRunner::class) @Px class Test
+      |
+      |@RunWith @Px class Test {}
+      |
+      |@RunWith(MagicRunner::class) @Px class Test {}
+      |
+      |@RunWith(MagicRunner::class)
+      |@Px
+      |@Px
+      |class Test {}
+      |
+      |@RunWith(MagicRunner::class)
+      |@Px
+      |class Test {
+      |  //
+      |}
+      |
+      |fun f() {
+      |  if (@Stuff(Magic::class) isGood()) {
+      |    println("")
+      |  }
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `no newlines after annotations on properties if entire expression fits in one line`() =
+      assertFormatted(
+          """
+      |--------------------------------------------
+      |@Suppress("UnsafeCast")
+      |val ClassA.methodA
+      |  get() = foo as Bar
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `when annotations cause line breaks, and constant has no type dont break before value`() =
+      assertFormatted(
+          """
+      |----------------------------------------------------------
+      |object Foo {
+      |  @LongLongLongLongAnnotation
+      |  @LongLongLongLongLongAnnotation
+      |  private val ROW_HEIGHT = 72
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `annotations in literal function types`() =
+      assertFormatted(
+          """
+      |val callback: (@Anno List<@JvmSuppressWildcards String>) -> Unit = foo
+      |""".trimMargin())
+
+  @Test
+  fun `annotations on type parameters`() =
+      assertFormatted(
+          """
+      |class Foo<@Anno out @Anno T, @Anno in @Anno U> {
+      |  inline fun <@Anno reified @Anno X, @Anno reified @Anno Y> bar() {}
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `annotations on type constraints`() =
+      assertFormatted(
+          """
+      |class Foo<T : @Anno Kip, U> where U : @Anno Kip, U : @Anno Qux {
+      |  fun <T : @Anno Kip, U> bar() where U : @Anno Kip, U : @Anno Qux {}
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `annotations on type arguments`() =
+      assertFormatted("""
+      |fun foo(x: Foo<in @Anno Int>) {}
+      |""".trimMargin())
+
+  @Test
+  fun `annotations on destructuring declaration elements`() =
+      assertFormatted("""
+      |val x = { (@Anno x, @Anno y) -> x }
+      |""".trimMargin())
+
+  @Test
+  fun `annotations on exceptions`() =
+      assertFormatted(
+          """
+      |fun doIt() {
+      |  try {
+      |    doItAgain()
+      |  } catch (@Nullable e: Exception) {
+      |    //
+      |  } catch (@Suppress("GeneralException") e: Exception) {}
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `Unary prefix expressions`() =
+      assertFormatted(
+          """
+      |fun f() {
+      |  !a
+      |  -4
+      |  val x = -foo()
+      |  +4
+      |  ++a
+      |  --a
+      |
+      |  + +a
+      |  +-a
+      |  +!a
+      |  -+a
+      |  - -a
+      |  -!a
+      |  !+a
+      |  !a
+      |  ! !a
+      |
+      |  + ++a
+      |  +--a
+      |  -++a
+      |  - --a
+      |  !++a
+      |  !--a
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `Unary postfix expressions`() =
+      assertFormatted(
+          """
+      |fun f() {
+      |  a!!
+      |  a++
+      |  a--
+      |
+      |  a--!!
+      |  a++!!
+      |
+      |  a!! !!
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `handle wildcard generics`() =
+      assertFormatted(
+          """
+      |fun f() {
+      |  val l: List<*>
+      |  val p: Pair<*, *>
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `handle covariant and contravariant type arguments`() =
+      assertFormatted("""
+      |val p: Pair<in T, out S>
+      |""".trimMargin())
+
+  @Test
+  fun `handle covariant and contravariant type parameters`() =
+      assertFormatted("""
+      |class Foo<in T, out S>
+      |""".trimMargin())
+
+  @Test
+  fun `handle bounds for type parameters`() =
+      assertFormatted("""
+      |class Foo<in T : List<*>, out S : Any?>
+      |""".trimMargin())
+
+  @Test
+  fun `handle compound generic bounds on classes`() =
+      assertFormatted(
+          """
+      |class Foo<T>(n: Int) where T : Bar, T : FooBar {}
+      |""".trimMargin())
+
+  @Test
+  fun `handle compound generic bounds on functions`() =
+      assertFormatted(
+          """
+      |fun <T> foo(n: Int) where T : Bar, T : FooBar {}
+      |""".trimMargin())
+
+  @Test
+  fun `handle compound generic bounds on properties`() =
+      assertFormatted(
+          """
+      |val <T> List<T>.twiceSum: Int where T : Int
+      |  get() {
+      |    return 2 * sum()
+      |  }
+      |""".trimMargin())
+
+  @Test
+  fun `handle compound generic bounds on class with delegate`() =
+      assertFormatted(
+          """
+      |class Foo<T>() : Bar by bar
+      |where T : Qux
+      |""".trimMargin())
+
+  @Test
+  fun `explicit type on property getter`() =
+      assertFormatted(
+          """
+      |class Foo {
+      |  val silly: Int
+      |    get(): Int = 1
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `handle method calls with lambda arg only`() =
+      assertFormatted(
+          """
+      |fun f() {
+      |  val a = g { 1 + 1 }
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `handle method calls value args and a lambda arg`() =
+      assertFormatted(
+          """
+      |fun f() {
+      |  val a = g(1, 2) { 1 + 1 }
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `handle top level constants`() =
+      assertFormatted(
+          """
+      |-----------------------------
+      |val a = 5
+      |
+      |const val b = "a"
+      |
+      |val a = 5
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `handle lambda arg with named arguments`() =
+      assertFormatted(
+          """
+      |fun f() {
+      |  val b = { x: Int, y: Int -> x + y }
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `avoid newline before lambda argument if it is named`() =
+      assertFormatted(
+          """
+      |private fun f(items: List<Int>) {
+      |  doSomethingCool(
+      |      items,
+      |      lambdaArgument = {
+      |        step1()
+      |        step2()
+      |      }) { it.doIt() }
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `handle labeled this pointer`() =
+      assertFormatted(
+          """
+      |class Foo {
+      |  fun f() {
+      |    g { println(this@Foo) }
+      |  }
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `handle extension and operator functions`() =
+      assertFormatted("""
+      |operator fun Point.component1() = x
+      |""".trimMargin())
+
+  @Test
+  fun `handle extension methods with very long names`() =
+      assertFormatted(
+          """
+      |------------------------------------------
+      |fun LongReceiverNameThatRequiresBreaking
+      |    .doIt() {}
+      |
+      |fun LongButNotTooLong.doIt(
+      |    n: Int,
+      |    f: Float
+      |) {}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `handle extension properties`() =
+      assertFormatted(
+          """
+      |val Int.isPrime: Boolean
+      |  get() = runMillerRabinPrimality(this)
+      |""".trimMargin())
+
+  @Test
+  fun `generic extension property`() =
+      assertFormatted("""
+      |val <T> List<T>.twiceSize = 2 * size()
+      |""".trimMargin())
+
+  @Test
+  fun `handle file annotations`() =
+      assertFormatted(
+          """
+      |@file:JvmName("DifferentName")
+      |
+      |package com.somecompany.example
+      |
+      |import com.somecompany.example2
+      |
+      |class Foo {
+      |  val a = example2("and 1")
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `handle init block`() =
+      assertFormatted(
+          """
+      |class Foo {
+      |  init {
+      |    println("Init!")
+      |  }
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `handle interface delegation`() =
+      assertFormatted(
+          """
+      |class MyList(impl: List<Int>) : Collection<Int> by impl
+      |""".trimMargin())
+
+  @Test
+  fun `handle property delegation`() =
+      assertFormatted("""
+      |val a by lazy { 1 + 1 }
+      |""".trimMargin())
+
+  @Test
+  fun `handle property delegation with type and breaks`() =
+      assertFormatted(
+          """
+      |---------------------------------
+      |val importantValue: Int by lazy {
+      |  1 + 1
+      |}
+      |
+      |val importantValue: Int by lazy {
+      |  val b = 1 + 1
+      |  b + b
+      |}
+      |
+      |val importantValueLonger:
+      |    Int by lazy { 1 + 1 }
+      |
+      |val importantValue: Int by
+      |    doIt(1 + 1)
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `handle multi-annotations with use-site targets`() =
+      assertFormatted(
+          """
+    |class Something {
+    |  @field:[Inject Named("WEB_VIEW")]
+    |  internal lateinit var httpClient: OkHttpClient
+    |
+    |  @field:[Inject Named("WEB_VIEW")]
+    |  var httpClient: OkHttpClient
+    |
+    |  @Px
+    |  @field:[Inject Named("WEB_VIEW")]
+    |  var httpClient: OkHttpClient
+    |}
+    |
+    """.trimMargin())
+
+  @Test
+  fun `handle parameters with annoations with parameters`() =
+      assertFormatted(
+          """
+    |class Something {
+    |  fun doIt(@Magic(withHat = true) foo: Foo) {
+    |    println(foo)
+    |  }
+    |}
+    |
+    """.trimMargin())
+
+  @Test
+  fun `handle lambda types`() =
+      assertFormatted(
+          """
+      |val listener1: (Boolean) -> Unit = { b -> !b }
+      |
+      |val listener2: () -> Unit = {}
+      |
+      |val listener3: (Int, Double) -> Int = { a, b -> a }
+      |
+      |val listener4: Int.(Int, Boolean) -> Unit
+      |""".trimMargin())
+
+  @Test
+  fun `handle unicode in string literals`() =
+      assertFormatted("""
+      |val a = "\uD83D\uDC4D"
+      |""".trimMargin())
+
+  @Test
+  fun `handle casting`() =
+      assertFormatted(
+          """
+      |fun castIt(o: Object) {
+      |  println(o is Double)
+      |  println(o !is Double)
+      |  doIt(o as Int)
+      |  doIt(o as? Int)
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `handle casting with breaks`() =
+      assertFormatted(
+          """
+      |-----------------------
+      |fun castIt(
+      |    something: Any
+      |) {
+      |  doIt(
+      |      something
+      |          as List<*>)
+      |  doIt(
+      |      something
+      |          is List<*>)
+      |  println(
+      |      something
+      |          is
+      |          List<String>)
+      |  doIt(
+      |      something
+      |          as
+      |          List<String>)
+      |  println(
+      |      something
+      |          is
+      |          PairList<
+      |              String,
+      |              Int>)
+      |  doIt(
+      |      something
+      |          as
+      |          PairList<
+      |              String,
+      |              Int>)
+      |  println(
+      |      a is Int &&
+      |          b is String)
+      |  l.b?.s?.sOrNull() is
+      |      SomethingLongEnough
+      |}
+      |
+      |val a =
+      |    l.sOrNull() is
+      |        SomethingLongEnough
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `handle collection literals in annotations`() =
+      assertFormatted(
+          """
+      |@Foo(a = [1, 2])
+      |fun doIt(o: Object) {
+      |  //
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `handle try, catch and finally`() =
+      assertFormatted(
+          """
+      |fun foo() {
+      |  try {
+      |    bar()
+      |  } catch (e: Exception) {
+      |    throw e
+      |  } finally {
+      |    println("finally")
+      |  }
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `handle infix methods`() =
+      assertFormatted(
+          """
+      |fun numbers() {
+      |  (0 until 100).size
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `handle while loops`() =
+      assertFormatted(
+          """
+      |fun numbers() {
+      |  while (1 < 2) {
+      |    println("Everything is okay")
+      |  }
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `handle do while loops`() =
+      assertFormatted(
+          """
+      |fun numbers() {
+      |  do {
+      |    println("Everything is okay")
+      |  } while (1 < 2)
+      |
+      |  do while (1 < 2)
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `handle break and continue`() =
+      assertFormatted(
+          """
+      |fun numbers() {
+      |  while (1 < 2) {
+      |    if (true) {
+      |      break
+      |    }
+      |    if (false) {
+      |      continue
+      |    }
+      |  }
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `handle all kinds of labels and jumps`() =
+      assertFormatted(
+          """
+      |fun f(a: List<Int>) {
+      |  a.map {
+      |    myloop@ for (i in a) {
+      |      if (true) {
+      |        break@myloop
+      |      } else if (false) {
+      |        continue@myloop
+      |      } else {
+      |        a.map `inner map`@{
+      |          return@`inner map`
+      |        }
+      |      }
+      |    }
+      |    return@map 2 * it
+      |  }
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `don't crash on top level statements with semicolons`() {
+    val code =
+        """
+      |val x = { 0 };
+      |
+      |foo({ 0 });
+      |
+      |foo { 0 };
+      |
+      |val fill = 0;
+      |""".trimMargin()
+    val expected =
+        """
+      |val x = { 0 }
+      |
+      |foo({ 0 })
+      |
+      |foo { 0 }
+      |
+      |val fill = 0
+      |""".trimMargin()
+    assertThatFormatting(code).isEqualTo(expected)
+  }
+
+  @Test
+  fun `preserve semicolons in enums`() {
+    val code =
+        """
+      |enum class SemiColonIsNotRequired {
+      |  TRUE, FALSE;
+      |}
+      |
+      |enum class SemiColonIsRequired {
+      |  ONE, TWO;
+      |
+      |  fun isOne(): Boolean = this == ONE
+      |}
+      |""".trimMargin()
+    val expected =
+        """
+      |enum class SemiColonIsNotRequired {
+      |  TRUE,
+      |  FALSE
+      |}
+      |
+      |enum class SemiColonIsRequired {
+      |  ONE,
+      |  TWO;
+      |
+      |  fun isOne(): Boolean = this == ONE
+      |}
+      |""".trimMargin()
+    assertThatFormatting(code).isEqualTo(expected)
+  }
+
+  @Test
+  fun `preserve semicolons in comments and strings`() {
+    val code =
+        """
+      |fun f() {
+      |  val x = ";"
+      |  val x = ""${'"'}  don't touch ; in raw strings ""${'"'}
+      |}
+      |
+      |// Don't touch ; inside comments.
+      |
+      |/** Don't touch ; inside comments. */
+      |""".trimMargin()
+    val expected =
+        """
+      |fun f() {
+      |  val x = ";"
+      |  val x = ""${'"'}  don't touch ; in raw strings ""${'"'}
+      |}
+      |
+      |// Don't touch ; inside comments.
+      |
+      |/** Don't touch ; inside comments. */
+      |""".trimMargin()
+    assertThatFormatting(code).isEqualTo(expected)
+  }
+
+  @Test
+  fun `preserve semicolons in empty if-s and while-s`() {
+    val code =
+        """
+      |fun f() {
+      |  while (true);
+      |  while (true) /** a */ ;
+      |
+      |  if (true);
+      |  if (true) /** a */ ;
+      |
+      |  if (true)
+      |    else
+      |  ;
+      |}
+      |""".trimMargin()
+    val expected =
+        """
+      |fun f() {
+      |  while (true) ;
+      |  while (true)
+      |  /** a */
+      |  ;
+      |
+      |  if (true) ;
+      |  if (true)
+      |  /** a */
+      |  ;
+      |
+      |  if (true)  else ;
+      |}
+      |""".trimMargin()
+    assertThatFormatting(code).isEqualTo(expected)
+  }
+
+  @Test
+  fun `preserve semicolons between calls and dead lambdas`() {
+    val code =
+        """
+      |fun f() {
+      |  foo(0); { dead -> lambda }
+      |
+      |  foo(0) ; { dead -> lambda }
+      |
+      |  foo(0) /** a */ ; /** b */ { dead -> lambda }
+      |
+      |  foo(0) { trailing -> lambda }; { dead -> lambda }
+      |
+      |  foo { trailing -> lambda }; { dead -> lambda }
+      |
+      |  val x = foo(); { dead -> lambda }
+      |
+      |  val x = bar() && foo(); { dead -> lambda }
+      |
+      |  // `z` has a property and a method both named `bar`
+      |  val x = z.bar; { dead -> lambda }
+      |
+      |  // `this` has a property and a method both named `bar`
+      |  val x = bar; { dead -> lambda }
+      |
+      |  // Literally any callable expression is dangerous
+      |  val x = (if (cond) x::foo else x::bar); { dead -> lambda }
+      |}
+      |""".trimMargin()
+    val expected =
+        """
+      |fun f() {
+      |  foo(0);
+      |  { dead -> lambda }
+      |
+      |  foo(0);
+      |  { dead -> lambda }
+      |
+      |  foo(0)
+      |  /** a */
+      |  ;
+      |  /** b */
+      |  { dead -> lambda }
+      |
+      |  foo(0) { trailing -> lambda };
+      |  { dead -> lambda }
+      |
+      |  foo { trailing -> lambda };
+      |  { dead -> lambda }
+      |
+      |  val x = foo();
+      |  { dead -> lambda }
+      |
+      |  val x = bar() && foo();
+      |  { dead -> lambda }
+      |
+      |  // `z` has a property and a method both named `bar`
+      |  val x = z.bar;
+      |  { dead -> lambda }
+      |
+      |  // `this` has a property and a method both named `bar`
+      |  val x = bar;
+      |  { dead -> lambda }
+      |
+      |  // Literally any callable expression is dangerous
+      |  val x = (if (cond) x::foo else x::bar);
+      |  { dead -> lambda }
+      |}
+      |""".trimMargin()
+    assertThatFormatting(code).isEqualTo(expected)
+  }
+
+  @Test
+  fun `drop redundant semicolons`() {
+    val code =
+        """
+      |package org.examples;
+      |import org.examples.wow.MuchWow;
+      |import org.examples.wow.ManyAmaze
+      |
+      |typealias Int2 = Int;
+      |
+      |fun f() {
+      |  val a = 3;
+      |  val x = 5 ; val y = ManyAmaze();
+      |  myThingMap.forEach { val (key, value) = it; println("mapped ${"$"}MuchWow") }
+      |  when {
+      |    true -> "1"; false -> "0"
+      |  }
+      |  someLongVariableName.let {
+      |    someReallyLongFunctionNameThatMakesThisNotFitInOneLineWithTheAboveVariable();
+      |  }
+      |} ;
+      |
+      |""".trimMargin()
+    val expected =
+        """
+      |package org.examples
+      |
+      |import org.examples.wow.ManyAmaze
+      |import org.examples.wow.MuchWow
+      |
+      |typealias Int2 = Int
+      |
+      |fun f() {
+      |  val a = 3
+      |  val x = 5
+      |  val y = ManyAmaze()
+      |  myThingMap.forEach {
+      |    val (key, value) = it
+      |    println("mapped ${"$"}MuchWow")
+      |  }
+      |  when {
+      |    true -> "1"
+      |    false -> "0"
+      |  }
+      |  someLongVariableName.let {
+      |    someReallyLongFunctionNameThatMakesThisNotFitInOneLineWithTheAboveVariable()
+      |  }
+      |}
+      |""".trimMargin()
+    assertThatFormatting(code).isEqualTo(expected)
+  }
+
+  @Test
+  fun `pretty-print after dropping redundant semicolons`() {
+    val code = """
+      |fun f() {
+      |  val veryLongName = 5;
+      |}
+      |""".trimMargin()
+    val expected =
+        """
+      |fun f() {
+      |  val veryLongName = 5
+      |}
+      |""".trimMargin()
+    assertThatFormatting(code).withOptions(FormattingOptions(maxWidth = 22)).isEqualTo(expected)
+  }
+
+  @Test
+  fun `handle no parenthesis in lambda calls`() =
+      assertFormatted(
+          """
+      |fun f() {
+      |  a { println("a") }
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `handle multi statement lambdas`() =
+      assertFormatted(
+          """
+      |fun f() {
+      |  a {
+      |    println("a")
+      |    println("b")
+      |  }
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `handle multi line one statement lambda`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |fun f() {
+      |  a {
+      |    println(foo.bar.boom)
+      |  }
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `statements are wrapped in blocks`() =
+      assertFormatted(
+          """
+      |fun f() {
+      |  builder.block {
+      |    getArgumentName().accept
+      |    return
+      |  }
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `properly break fully qualified nested user types`() =
+      assertFormatted(
+          """
+      |-------------------------------------------------------
+      |val complicated:
+      |    com.example.interesting.SomeType<
+      |        com.example.interesting.SomeType<Int, Nothing>,
+      |        com.example.interesting.SomeType<
+      |            com.example.interesting.SomeType<
+      |                Int, Nothing>,
+      |            Nothing>> =
+      |    DUMMY
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `handle multi-line lambdas within lambdas and calling chains`() =
+      assertFormatted(
+          """
+      |fun f() {
+      |  builder.block(ZERO) {
+      |    builder.token("when")
+      |    expression1.let { subjectExp ->
+      |      builder.token(")")
+      |      return
+      |    }
+      |  }
+      |  builder.block(ZERO) {
+      |    expression2.subjectExpression.let { subjectExp ->
+      |      builder.token(")")
+      |      return
+      |    }
+      |  }
+      |  builder.block(ZERO) {
+      |    expression2.subjectExpression
+      |        .let { subjectExp ->
+      |          builder.token(")")
+      |          return
+      |        }
+      |        .sum
+      |  }
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `handle multi line lambdas with explicit args`() =
+      assertFormatted(
+          """
+      |--------------------
+      |fun f() {
+      |  a { (x, y) ->
+      |    x + y
+      |  }
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `handle lambda with destructuring and type`() =
+      assertFormatted(
+          """
+      |fun f() {
+      |  g { (a, b): List<Int> -> a }
+      |  g { (a, b): List<Int>, (c, d): List<Int> -> a }
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `handle parenthesis in lambda calls for now`() =
+      assertFormatted(
+          """
+      |fun f() {
+      |  a() { println("a") }
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `handle chaining of calls with lambdas`() =
+      assertFormatted(
+          """
+      |fun f() {
+      |  bobby
+      |      .map { x -> x * x }
+      |      .map { x -> x * x }
+      |      ?.map { x ->
+      |        val y = x * x
+      |        y
+      |      }
+      |      .sum
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `handle break of lambda args per line with indentation`() =
+      assertFormatted(
+          """
+      |-----------
+      |fun f() {
+      |  a() {
+      |      arg1,
+      |      arg2,
+      |      x ->
+      |    doIt()
+      |    doIt()
+      |  }
+      |  a() {
+      |      arg1,
+      |      arg2,
+      |      arg3
+      |    ->
+      |    doIt()
+      |    doIt()
+      |  }
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `handle trailing comma in lambda`() =
+      assertFormatted(
+          """
+      |-----------
+      |fun f() {
+      |  a() {
+      |      arg1,
+      |      arg2,
+      |      x,
+      |    ->
+      |    doIt()
+      |    doIt()
+      |  }
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `break before Elvis operator`() =
+      assertFormatted(
+          """
+        |--------------------------------------------------
+        |fun f() {
+        |  someObject
+        |      .someMethodReturningCollection()
+        |      .map { it.someProperty }
+        |      .find { it.contains(someSearchValue) }
+        |      ?: someDefaultValue
+        |}
+        |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `handle comments in the middle of calling chain`() =
+      assertFormatted(
+          """
+        |---------------------------
+        |fun f() {
+        |  someObject
+        |      .letsDoIt()
+        |      // this is a comment
+        |      .doItOnce()
+        |      // this is a comment
+        |      .doItTwice()
+        |}
+        |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `handle reified types`() =
+      assertFormatted(
+          """
+      |inline fun <reified T> foo(t: T) {
+      |  println(t)
+      |}
+      |
+      |inline fun <reified in T> foo2(t: T) {
+      |  println(t)
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `handle suspended types`() =
+      assertFormatted(
+          """
+      |private val reader: suspend (Key) -> Output?
+      |
+      |private val delete: (suspend (Key) -> Unit)? = null
+      |
+      |inline fun <R> foo(noinline block: suspend () -> R): suspend () -> R
+      |
+      |inline fun <R> bar(noinline block: (suspend () -> R)?): (suspend () -> R)?
+      |""".trimMargin())
+
+  @Test
+  fun `handle simple enum classes`() =
+      assertFormatted(
+          """
+      |enum class BetterBoolean {
+      |  TRUE,
+      |  FALSE,
+      |  FILE_NOT_FOUND,
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `handle enum class with functions`() =
+      assertFormatted(
+          """
+      |enum class BetterBoolean {
+      |  TRUE,
+      |  FALSE,
+      |  FILE_NOT_FOUND;
+      |  fun isGood(): Boolean {
+      |    return true
+      |  }
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `handle enum with annotations`() =
+      assertFormatted(
+          """
+      |enum class BetterBoolean {
+      |  @True TRUE,
+      |  @False @WhatIsTruth FALSE,
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `handle enum constructor calls`() =
+      assertFormatted(
+          """
+      |enum class BetterBoolean(val name: String, val value: Boolean = true) {
+      |  TRUE("true"),
+      |  FALSE("false", false),
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `handle enum entries with body`() =
+      assertFormatted(
+          """
+      |enum class Animal(canWalk: Boolean = true) {
+      |  DOG {
+      |    fun speak() = "woof"
+      |  },
+      |  FISH(false) {},
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `handle empty enum`() =
+      assertFormatted("""
+      |enum class YTho {
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `expect enum class`() =
+      assertFormatted("""
+      |expect enum class ExpectedEnum
+      |""".trimMargin())
+
+  @Test
+  fun `enum without trailing comma`() =
+      assertFormatted(
+          """
+      |enum class Highlander {
+      |  ONE
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `enum comma and semicolon`() {
+    assertThatFormatting(
+            """
+        |enum class Highlander {
+        |  ONE,;
+        |}
+        |""".trimMargin())
+        .isEqualTo(
+            """
+        |enum class Highlander {
+        |  ONE,
+        |}
+        |""".trimMargin())
+  }
+
+  @Test
+  fun `semicolon is placed on next line when there's a trailing comma in an enum declaration`() =
+      assertFormatted(
+          """
+        |enum class Highlander {
+        |  ONE,
+        |  TWO,
+        |  ;
+        |
+        |  fun f() {}
+        |}
+        |""".trimMargin())
+
+  @Test
+  fun `handle varargs and spread operator`() =
+      assertFormatted(
+          """
+      |fun foo(vararg args: String) {
+      |  foo2(*args)
+      |  foo3(options = *args)
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `handle typealias`() =
+      assertFormatted(
+          """
+      |----------------------------------------------
+      |private typealias TextChangedListener =
+      |    (string: String) -> Unit
+      |
+      |typealias PairPair<X, Y> = Pair<Pair<X, Y>, X>
+      |
+      |class Foo
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `handle the 'dynamic' type`() =
+      assertFormatted(
+          """
+      |fun x(): dynamic = "x"
+      |
+      |val dyn: dynamic = 1
+      |""".trimMargin())
+
+  @Test
+  fun `handle class expression with generics`() =
+      assertFormatted(
+          """
+      |fun f() {
+      |  println(Array<String>::class.java)
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `ParseError contains correct line and column numbers`() {
+    val code =
+        """
+      |// Foo
+      |fun good() {
+      |  //
+      |}
+      |
+      |fn (
+      |""".trimMargin()
+    try {
+      Formatter.format(code)
+      fail()
+    } catch (e: ParseError) {
+      assertThat(e.lineColumn.line).isEqualTo(6)
+      assertThat(e.lineColumn.column).isEqualTo(0)
+      assertThat(e.errorDescription).contains("Expecting an expression")
+    }
+  }
+
+  @Test
+  fun `fail() reports line+column number`() {
+    val code =
+        """
+      |// Foo
+      |fun good() {
+      |  return@ 5
+      |}
+      |""".trimMargin()
+    try {
+      Formatter.format(code)
+      fail()
+    } catch (e: ParseError) {
+      assertThat(e.lineColumn.line).isEqualTo(2)
+      assertThat(e.lineColumn.column).isEqualTo(8)
+    }
+  }
+
+  @Test
+  fun `annotations on class, fun, parameters and literals`() =
+      assertFormatted(
+          """
+      |@Fancy
+      |class Foo {
+      |  @Fancy
+      |  fun baz(@Fancy foo: Int): Int {
+      |    return (@Fancy 1)
+      |  }
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `annotations on function types`() =
+      assertFormatted(
+          """
+      |fun foo(bar: @StringRes Int) {}
+      |
+      |fun foo(error: @Composable ((x) -> Unit)) {}
+      |
+      |fun foo(error: (@Composable (x) -> Unit)) {}
+      |
+      |fun foo(
+      |    error:
+      |        @field:[Inject Named("WEB_VIEW")]
+      |        ((x) -> Unit)
+      |) {}
+      |
+      |fun foo(
+      |    error:
+      |        (@field:[Inject Named("WEB_VIEW")]
+      |        (x) -> Unit)
+      |) {}
+      |""".trimMargin())
+
+  @Test
+  fun `handle annotations with use-site targets`() =
+      assertFormatted(
+          """
+      |class FooTest {
+      |  @get:Rule val exceptionRule: ExpectedException = ExpectedException.none()
+      |
+      |  @set:Magic(name = "Jane") var field: String
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `handle annotations mixed with keywords since we cannot reorder them for now`() =
+      assertFormatted(
+          """
+      |public @Magic final class Foo
+      |
+      |public @Magic(1) final class Foo
+      |
+      |@Magic(1) public final class Foo
+      |""".trimMargin())
+
+  @Test
+  fun `handle annotations more`() =
+      assertFormatted(
+          """
+      |-------------------------------------------------
+      |@Anno1
+      |@Anno2(param = Param1::class)
+      |@Anno3
+      |@Anno4(param = Param2::class)
+      |class MyClass {}
+      |
+      |fun f() {
+      |  @Suppress("MagicNumber") add(10)
+      |
+      |  @Annotation // test a comment after annotations
+      |  return 5
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `annotated expressions`() =
+      assertFormatted(
+          """
+      |------------------------------------------------
+      |fun f() {
+      |  @Suppress("MagicNumber") add(10) && add(20)
+      |
+      |  @Suppress("MagicNumber")
+      |  add(10) && add(20)
+      |
+      |  @Anno1 @Anno2(param = Param1::class)
+      |  add(10) && add(20)
+      |
+      |  @Anno1
+      |  @Anno2(param = Param1::class)
+      |  @Anno3
+      |  @Anno4(param = Param2::class)
+      |  add(10) && add(20)
+      |
+      |  @Anno1
+      |  @Anno2(param = Param1::class)
+      |  @Anno3
+      |  @Anno4(param = Param2::class)
+      |  add(10) && add(20)
+      |
+      |  @Suppress("MagicNumber") add(10) &&
+      |      add(20) &&
+      |      add(30)
+      |
+      |  add(@Suppress("MagicNumber") 10) &&
+      |      add(20) &&
+      |      add(30)
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `annotated function declarations`() =
+      assertFormatted(
+          """
+      |@Anno
+      |fun f() {
+      |  add(10)
+      |}
+      |
+      |@Anno(param = 1)
+      |fun f() {
+      |  add(10)
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `annotated class declarations`() =
+      assertFormatted(
+          """
+      |@Anno class F
+      |
+      |@Anno(param = 1) class F
+      |
+      |@Anno(P)
+      |// Foo
+      |@Anno("param")
+      |class F
+      |""".trimMargin())
+
+  @Test
+  fun `handle type arguments in annotations`() =
+      assertFormatted(
+          """
+      |@TypeParceler<UUID, UUIDParceler>() class MyClass {}
+      |""".trimMargin())
+
+  @Test
+  fun `handle one line KDoc`() =
+      assertFormatted(
+          """
+      |/** Hi, I am a one line kdoc */
+      |class MyClass {}
+      |""".trimMargin())
+
+  @Test
+  fun `handle KDoc with Link`() =
+      assertFormatted(
+          """
+      |/** This links to [AnotherClass] */
+      |class MyClass {}
+      |""".trimMargin())
+
+  @Test
+  fun `handle KDoc with paragraphs`() =
+      assertFormatted(
+          """
+      |/**
+      | * Hi, I am a two paragraphs kdoc
+      | *
+      | * There's a space line to preserve between them
+      | */
+      |class MyClass {}
+      |""".trimMargin())
+
+  @Test
+  fun `handle KDoc with blocks`() =
+      assertFormatted(
+          """
+      |/**
+      | * Hi, I am a two paragraphs kdoc
+      | *
+      | * @param param1 this is param1
+      | * @param[param2] this is param2
+      | */
+      |class MyClass {}
+      |""".trimMargin())
+
+  @Test
+  fun `handle KDoc with code examples`() =
+      assertFormatted(
+          """
+      |/**
+      | * This is how you write a simple hello world in Kotlin:
+      | *
+      | * ```
+      | * fun main(args: Array<String>) {
+      | *   println("Hello World!")
+      | * }
+      | * ```
+      | *
+      | * Amazing ah?
+      | * ```
+      | * fun `code can be with a blank line above it` () {}
+      | * ```
+      | * Or after it!
+      | */
+      |class MyClass {}
+      |""".trimMargin())
+
+  @Test
+  fun `handle KDoc with tagged code examples`() =
+      assertFormatted(
+          """
+      |/**
+      | * ```kotlin
+      | * fun main(args: Array<String>) {
+      | *   println("Hello World!")
+      | * }
+      | * ```
+      | */
+      |class MyClass {}
+      |""".trimMargin())
+
+  @Test
+  fun `handle stray code markers in lines and produce stable output`() {
+    val code =
+        """
+      |/**
+      | * Look! code: ``` aaa
+      | * fun f() = Unit
+      | * foo
+      | * ```
+      | */
+      |class MyClass {}
+      |""".trimMargin()
+    assertFormatted(Formatter.format(code))
+  }
+
+  @Test
+  fun `code block with triple backtick`() {
+    val code =
+        """
+      |/**
+      | * Look! code:
+      | * ```
+      | * aaa ``` wow
+      | * ```
+      | */
+      |class MyClass {}
+      |""".trimMargin()
+    val expected =
+        """
+      |/**
+      | * Look! code:
+      | * ```
+      | * aaa ``` wow
+      | * ```
+      | */
+      |class MyClass {}
+      |""".trimMargin()
+    assertThatFormatting(code).isEqualTo(expected)
+  }
+
+  @Test
+  fun `when code closer in mid of line, produce stable output`() {
+    val code =
+        """
+      |/**
+      | * Look! code: ``` aaa
+      | * fun f() = Unit
+      | * foo ``` wow
+      | */
+      |class MyClass {}
+      |""".trimMargin()
+    assertFormatted(Formatter.format(code))
+  }
+
+  @Test
+  fun `handle KDoc with link reference`() =
+      assertFormatted(
+          """
+      |/** Doc line with a reference to [AnotherClass] in the middle of a sentence */
+      |class MyClass {}
+      |""".trimMargin())
+
+  @Test
+  fun `handle KDoc with links one after another`() =
+      assertFormatted(
+          """
+      |/** Here are some links [AnotherClass] [AnotherClass2] */
+      |class MyClass {}
+      |""".trimMargin())
+
+  @Test
+  fun `don't add spaces after links in Kdoc`() =
+      assertFormatted(
+          """
+      |/** Here are some links [AnotherClass][AnotherClass2]hello */
+      |class MyClass {}
+      |""".trimMargin())
+
+  @Test
+  fun `don't remove spaces after links in Kdoc`() =
+      assertFormatted(
+          """
+      |/** Please see [onNext] (which has more details) */
+      |class MyClass {}
+      |""".trimMargin())
+
+  @Test
+  fun `link anchor in KDoc are preserved`() =
+      assertFormatted(
+          """
+      |/** [link anchor](the URL for the link anchor goes here) */
+      |class MyClass {}
+      |""".trimMargin())
+
+  @Test
+  fun `don't add spaces between links in KDoc (because they're actually references)`() =
+      assertFormatted(
+          """
+      |/** Here are some links [AnotherClass][AnotherClass2] */
+      |class MyClass {}
+      |
+      |/** The final produced value may have [size][ByteString.size] < [bufferSize]. */
+      |class MyClass {}
+      |""".trimMargin())
+
+  @Test
+  fun `collapse spaces after links in KDoc`() {
+    val code =
+        """
+      |/** Here are some links [Class1], [Class2]   [Class3]. hello */
+      |class MyClass {}
+      |""".trimMargin()
+    val expected =
+        """
+      |/** Here are some links [Class1], [Class2] [Class3]. hello */
+      |class MyClass {}
+      |""".trimMargin()
+    assertThatFormatting(code).isEqualTo(expected)
+  }
+
+  @Test
+  fun `collapse newlines after links in KDoc`() {
+    val code =
+        """
+      |/**
+      | * Here are some links [Class1]
+      | * [Class2]
+      | */
+      |class MyClass {}
+      |""".trimMargin()
+    val expected =
+        """
+      |/** Here are some links [Class1] [Class2] */
+      |class MyClass {}
+      |""".trimMargin()
+    assertThatFormatting(code).isEqualTo(expected)
+  }
+
+  @Test
+  fun `do not crash because of malformed KDocs and produce stable output`() {
+    val code = """
+      |/** Surprise ``` */
+      |class MyClass {}
+      |""".trimMargin()
+    assertFormatted(Formatter.format(code))
+  }
+
+  @Test
+  fun `Respect spacing of text after link`() =
+      assertFormatted(
+          """
+      |/** Enjoy this link [linkstuff]. */
+      |class MyClass {}
+      |
+      |/** There are many [FooObject]s. */
+      |class MyClass {}
+      |""".trimMargin())
+
+  @Test
+  fun `handle KDoc with multiple separated param tags, breaking and merging lines and missing asterisk`() {
+    val code =
+        """
+      |/**
+      | * Trims leading whitespace characters followed by [marginPrefix] from every line of a source string and removes
+      | * the first and the last lines if they are blank (notice difference blank vs empty).
+      |
+      | * Doesn't affect a line if it doesn't contain [marginPrefix] except the first and the last blank lines.
+      | *
+      | * Doesn't preserve the original line endings.
+      | *
+      | * @param marginPrefix non-blank string, which is used as a margin delimiter. Default is `|` (pipe character).
+      | *
+      | * @sample samples.text.Strings.trimMargin
+      | * @see trimIndent
+      | * @see kotlin.text.isWhitespace
+      | */
+      |class ThisWasCopiedFromTheTrimMarginMethod {}
+      |""".trimMargin()
+    val expected =
+        """
+      |/**
+      | * Trims leading whitespace characters followed by [marginPrefix] from every line of a source string
+      | * and removes the first and the last lines if they are blank (notice difference blank vs empty).
+      | *
+      | * Doesn't affect a line if it doesn't contain [marginPrefix] except the first and the last blank
+      | * lines.
+      | *
+      | * Doesn't preserve the original line endings.
+      | *
+      | * @param marginPrefix non-blank string, which is used as a margin delimiter. Default is `|` (pipe
+      | * character).
+      | *
+      | * @sample samples.text.Strings.trimMargin
+      | * @see trimIndent
+      | * @see kotlin.text.isWhitespace
+      | */
+      |class ThisWasCopiedFromTheTrimMarginMethod {}
+      |""".trimMargin()
+    assertThatFormatting(code).isEqualTo(expected)
+  }
+
+  @Test
+  fun `KDoc is reflowed`() {
+    val code =
+        """
+      |/** Lorem ipsum dolor sit amet, consectetur */
+      |class MyClass {}
+      |""".trimMargin()
+    val expected =
+        """
+      |/**
+      | * Lorem ipsum dolor sit amet,
+      | * consectetur
+      | */
+      |class MyClass {}
+      |""".trimMargin()
+    assertThatFormatting(code).withOptions(FormattingOptions(maxWidth = 33)).isEqualTo(expected)
+  }
+
+  @Test
+  fun `sanity - block and continuation indents are 4`() {
+    val code =
+        """
+        |fun f() {
+        |    for (child in
+        |        node.next.next.next.next
+        |            .data()) {
+        |        println(child)
+        |    }
+        |}
+        |""".trimMargin()
+    assertThatFormatting(code)
+        .withOptions(FormattingOptions(maxWidth = 35, blockIndent = 4, continuationIndent = 4))
+        .isEqualTo(code)
+  }
+
+  @Test
+  fun `comment after a block is stable and does not add space lines`() =
+      assertFormatted(
+          """
+      |fun doIt() {}
+      |
+      |/* this is the first comment */
+      |""".trimMargin())
+
+  @Test
+  fun `preserve LF, CRLF and CR line endings`() {
+    val lines = listOf("fun main() {", "  println(\"test\")", "}")
+    for (ending in listOf("\n", "\r\n", "\r")) {
+      val code = lines.joinToString(ending) + ending
+      assertThatFormatting(code).isEqualTo(code)
+    }
+  }
+
+  @Test
+  fun `handle trailing commas (constructors)`() =
+      assertFormatted(
+          """
+      |--------------------
+      |class Foo(
+      |    a: Int,
+      |)
+      |
+      |class Foo(
+      |    a: Int,
+      |    b: Int,
+      |)
+      |
+      |class Foo(
+      |    a: Int,
+      |    b: Int
+      |)
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `handle trailing commas (explicit constructors)`() =
+      assertFormatted(
+          """
+      |------------------------
+      |class Foo
+      |constructor(
+      |    a: Int,
+      |)
+      |
+      |class Foo
+      |constructor(
+      |    a: Int,
+      |    b: Int,
+      |)
+      |
+      |class Foo
+      |constructor(
+      |    a: Int,
+      |    b: Int
+      |)
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `handle trailing commas (secondary constructors)`() =
+      assertFormatted(
+          """
+      |------------------------
+      |class Foo {
+      |  constructor(
+      |      a: Int,
+      |  )
+      |}
+      |
+      |class Foo {
+      |  constructor(
+      |      a: Int,
+      |      b: Int,
+      |  )
+      |}
+      |
+      |class Foo {
+      |  constructor(
+      |      a: Int,
+      |      b: Int
+      |  )
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `handle trailing commas (function definitions)`() =
+      assertFormatted(
+          """
+      |------------------------
+      |fun <
+      |    T,
+      |> foo() {}
+      |
+      |fun <
+      |    T,
+      |    S,
+      |> foo() {}
+      |
+      |fun foo(
+      |    a: Int,
+      |) {}
+      |
+      |fun foo(
+      |    a: Int,
+      |    b: Int
+      |) {}
+      |
+      |fun foo(
+      |    a: Int,
+      |    b: Int,
+      |) {}
+      |
+      |fun foo(
+      |    a: Int,
+      |    b: Int,
+      |    c: Int,
+      |) {}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `handle trailing commas (function calls)`() =
+      assertFormatted(
+          """
+      |------------------------
+      |fun main() {
+      |  foo(
+      |      3,
+      |  )
+      |
+      |  foo<Int>(
+      |      3,
+      |  )
+      |
+      |  foo<
+      |      Int,
+      |  >(
+      |      3,
+      |  )
+      |
+      |  foo<Int>(
+      |      "asdf", "asdf")
+      |
+      |  foo<
+      |      Int,
+      |  >(
+      |      "asd",
+      |      "asd",
+      |  )
+      |
+      |  foo<
+      |      Int,
+      |      Boolean,
+      |  >(
+      |      3,
+      |  )
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `handle trailing commas (proprties)`() =
+      assertFormatted(
+          """
+      |--------------------------
+      |val foo: String
+      |  set(
+      |      value,
+      |  ) {}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `handle trailing commas (higher-order functions)`() =
+      assertFormatted(
+          """
+      |--------------------------
+      |fun foo(
+      |    x:
+      |        (
+      |            Int,
+      |        ) -> Unit
+      |) {}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `handle trailing commas (after lambda arg)`() =
+      assertFormatted(
+          """
+      |--------------------------
+      |fun foo() {
+      |  foo({ it },)
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `handle trailing commas (other)`() =
+      assertFormatted(
+          """
+      |--------------------------
+      |fun main() {
+      |  val (
+      |      x: Int,
+      |  ) = foo()
+      |  val (
+      |      x: Int,
+      |      y: Int,
+      |  ) = foo()
+      |
+      |  val (
+      |      x: Int,
+      |  ) = foo(
+      |      blablablablFoobar,
+      |      alskdjasld)
+      |
+      |  val (
+      |      x: Int,
+      |      y: Int,
+      |  ) = foo(
+      |      blablablablFoobar,
+      |      asldkalsd)
+      |
+      |  a[
+      |      0,
+      |  ] = 43
+      |  a[
+      |      0,
+      |      1,
+      |  ] = 43
+      |
+      |  [
+      |      0,
+      |  ]
+      |  [
+      |      0,
+      |      1,
+      |  ]
+      |
+      |  when (foo) {
+      |    'x', -> 43
+      |    'x',
+      |    'y', -> 43
+      |    'x',
+      |    'y',
+      |    'z',
+      |    'w',
+      |    'a',
+      |    'b', -> 43
+      |  }
+      |
+      |  try {
+      |    //
+      |  } catch (e: Error,) {
+      |    //
+      |  }
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `assignment of a scoping function`() =
+      assertFormatted(
+          """
+      |----------------------------
+      |val foo = coroutineScope {
+      |  foo()
+      |  //
+      |}
+      |
+      |fun foo() = coroutineScope {
+      |  foo()
+      |  //
+      |}
+      |
+      |fun foo() = use { x ->
+      |  foo()
+      |  //
+      |}
+      |
+      |fun foo() =
+      |    coroutineScope { x ->
+      |      foo()
+      |      //
+      |    }
+      |
+      |fun foo() =
+      |    Runnable @Px {
+      |      foo()
+      |      //
+      |    }
+      |
+      |fun longName() =
+      |    coroutineScope {
+      |      foo()
+      |      //
+      |    }
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `top level properties with other types preserve newline spacing`() {
+    assertFormatted(
+        """
+      |---------------------------------
+      |fun something() {
+      |  println("hi")
+      |}
+      |
+      |const val SOME_CONST = 1
+      |val SOME_STR = "hi"
+      |// Single comment
+      |val SOME_INT = 1
+      |
+      |// Intentional space above single comment
+      |val SOME_INT2 = 1
+      |
+      |val FOO = 2
+      |const val BAR = 3
+      |
+      |fun baz() = 1
+      |
+      |val d = 1
+      |
+      |class Bar {}
+      |
+      |val e = 1
+      |/** Doc block */
+      |val f = 1
+      |
+      |/** Intent. space above doc */
+      |val g = 1
+      |
+      |data class Qux(val foo: String)
+      |""".trimMargin(),
+        deduceMaxWidth = true)
+
+    assertThatFormatting(
+            """
+      |import com.example.foo
+      |import com.example.bar
+      |const val SOME_CONST = foo.a
+      |val SOME_STR = bar.a
+      |""".trimMargin())
+        .isEqualTo(
+            """
+      |import com.example.bar
+      |import com.example.foo
+      |
+      |const val SOME_CONST = foo.a
+      |val SOME_STR = bar.a
+      |""".trimMargin())
+  }
+
+  @Test
+  fun `first line is never empty`() =
+      assertThatFormatting("""
+      |
+      |fun f() {}
+      |""".trimMargin())
+          .isEqualTo("""
+      |fun f() {}
+      |""".trimMargin())
+
+  @Test
+  fun `at most one newline between any adjacent top-level elements`() =
+      assertThatFormatting(
+              """
+      |import com.Bar
+      |
+      |
+      |import com.Foo
+      |
+      |
+      |fun f() {}
+      |
+      |
+      |fun f() {}
+      |
+      |
+      |class C {}
+      |
+      |
+      |class C {}
+      |
+      |
+      |val x = Foo()
+      |
+      |
+      |val x = Bar()
+      |""".trimMargin())
+          .isEqualTo(
+              """
+      |import com.Bar
+      |import com.Foo
+      |
+      |fun f() {}
+      |
+      |fun f() {}
+      |
+      |class C {}
+      |
+      |class C {}
+      |
+      |val x = Foo()
+      |
+      |val x = Bar()
+      |""".trimMargin())
+
+  @Test
+  fun `at least one newline between any adjacent top-level elements, unless it's a property`() =
+      assertThatFormatting(
+              """
+      |import com.Bar
+      |import com.Foo
+      |fun f() {}
+      |fun f() {}
+      |class C {}
+      |class C {}
+      |val x = Foo()
+      |val x = Bar()
+      |""".trimMargin())
+          .isEqualTo(
+              """
+      |import com.Bar
+      |import com.Foo
+      |
+      |fun f() {}
+      |
+      |fun f() {}
+      |
+      |class C {}
+      |
+      |class C {}
+      |
+      |val x = Foo()
+      |val x = Bar()
+      |""".trimMargin())
+
+  @Test
+  fun `handle array of annotations with field prefix`() {
+    val code: String =
+        """
+    |class MyClass {
+    |  @field:[JvmStatic Volatile]
+    |  var myVar: String? = null
+    |}
+    |
+    """.trimMargin()
+    assertThatFormatting(code).isEqualTo(code)
+  }
+
+  @Test
+  fun `handle array of annotations without field prefix`() {
+    val code: String =
+        """
+    |class MyClass {
+    |  @[JvmStatic Volatile]
+    |  var myVar: String? = null
+    |}
+    |
+    """.trimMargin()
+    assertThatFormatting(code).isEqualTo(code)
+  }
+
+  // Regression test against https://github.com/facebookincubator/ktfmt/issues/243
+  @Test
+  fun `regression test against Issue 243`() {
+    val code =
+        """
+      |class Foo {
+      |  companion object {
+      |    var instance: Foo? = null
+      |
+      |    fun getInstance() {
+      |      return instance ?: synchronized(Foo::class) {
+      |        Foo().also { instance = it }
+      |      }
+      |    }
+      |  }
+      |}
+      |""".trimMargin()
+
+    // Don't throw.
+    Formatter.format(code)
+  }
+
+  @Test
+  fun `lambda with required arrow`() =
+      assertFormatted(
+          """
+      |val a = { x: Int -> }
+      |val b = { x: Int -> 0 }
+      |val c = { x: Int ->
+      |  val y = 0
+      |  y
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `lambda with optional arrow`() =
+      assertFormatted(
+          """
+      |val a = { -> }
+      |val b = { -> 0 }
+      |val c = { ->
+      |  val y = 0
+      |  y
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `lambda missing optional arrow`() =
+      assertFormatted(
+          """
+      |val a = {}
+      |val b = { 0 }
+      |val c = {
+      |  val y = 0
+      |  y
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `chaining - many dereferences`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |rainbow.red.orange.yellow
+      |    .green
+      |    .blue
+      |    .indigo
+      |    .violet
+      |    .cyan
+      |    .magenta
+      |    .key
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining - many dereferences, fit on one line`() =
+      assertFormatted(
+          """
+      |---------------------------------------------------------------------------
+      |rainbow.red.orange.yellow.green.blue.indigo.violet.cyan.magenta.key
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining - many dereferences, one invocation at end`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |rainbow.red.orange.yellow
+      |    .green
+      |    .blue
+      |    .indigo
+      |    .violet
+      |    .cyan
+      |    .magenta
+      |    .key
+      |    .build()
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining - many dereferences, one invocation at end, fit on one line`() =
+      assertFormatted(
+          """
+      |---------------------------------------------------------------------------
+      |rainbow.red.orange.yellow.green.blue.indigo.violet.cyan.magenta.key.build()
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining - many dereferences, two invocations at end`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |rainbow.red.orange.yellow
+      |    .green
+      |    .blue
+      |    .indigo
+      |    .violet
+      |    .cyan
+      |    .magenta
+      |    .key
+      |    .build()
+      |    .shine()
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining - many dereferences, one invocation in the middle`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |rainbow.red.orange.yellow
+      |    .green
+      |    .blue
+      |    .shine()
+      |    .indigo
+      |    .violet
+      |    .cyan
+      |    .magenta
+      |    .key
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining - many dereferences, two invocations in the middle`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |rainbow.red.orange.yellow
+      |    .green
+      |    .blue
+      |    .indigo
+      |    .shine()
+      |    .bright()
+      |    .violet
+      |    .cyan
+      |    .magenta
+      |    .key
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining - many dereferences, one lambda at end`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |rainbow.red.orange.yellow
+      |    .green
+      |    .blue
+      |    .indigo
+      |    .violet
+      |    .cyan
+      |    .magenta
+      |    .key
+      |    .build { it.appear }
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining - many dereferences, one short lambda at end`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |rainbow.red.orange.yellow
+      |    .green
+      |    .blue
+      |    .indigo
+      |    .violet
+      |    .cyan
+      |    .magenta
+      |    .key
+      |    .z { it }
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining - many dereferences, one multiline lambda at end`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |rainbow.red.orange.yellow
+      |    .green
+      |    .blue
+      |    .indigo
+      |    .violet
+      |    .cyan
+      |    .magenta
+      |    .key
+      |    .z {
+      |      it
+      |      it
+      |    }
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining - many dereferences, one short lambda in the middle`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |rainbow.red.orange.yellow
+      |    .green
+      |    .blue
+      |    .z { it }
+      |    .indigo
+      |    .violet
+      |    .cyan
+      |    .magenta
+      |    .key
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining - many dereferences, one multiline lambda in the middle`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |rainbow.red.orange.yellow
+      |    .green
+      |    .blue
+      |    .z {
+      |      it
+      |      it
+      |    }
+      |    .indigo
+      |    .violet
+      |    .cyan
+      |    .magenta
+      |    .key
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining - many dereferences, one multiline lambda in the middle, remainder could fit on one line`() =
+      assertFormatted(
+          """
+      |-----------------------------------------------------------------------------------------
+      |rainbow.red.orange.yellow.green.blue
+      |    .z {
+      |      it
+      |      it
+      |    }
+      |    .indigo
+      |    .violet
+      |    .cyan
+      |    .magenta
+      |    .key
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining - many dereferences, one multiline lambda and two invocations in the middle, remainder could fit on one line`() =
+      assertFormatted(
+          """
+      |-----------------------------------------------------------------------------------------
+      |rainbow.red.orange.yellow.green.blue
+      |    .z {
+      |      it
+      |      it
+      |    }
+      |    .shine()
+      |    .bright()
+      |    .indigo
+      |    .violet
+      |    .cyan
+      |    .magenta
+      |    .key
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining - many dereferences, one lambda and invocation at end`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |rainbow.red.orange.yellow
+      |    .green
+      |    .blue
+      |    .indigo
+      |    .violet
+      |    .cyan
+      |    .magenta
+      |    .key
+      |    .z { it }
+      |    .shine()
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining - many dereferences, one multiline lambda and invocation at end`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |rainbow.red.orange.yellow
+      |    .green
+      |    .blue
+      |    .indigo
+      |    .violet
+      |    .cyan
+      |    .magenta
+      |    .key
+      |    .z {
+      |      it
+      |      it
+      |    }
+      |    .shine()
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining - many dereferences, one invocation and lambda at end`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |rainbow.red.orange.yellow
+      |    .green
+      |    .blue
+      |    .indigo
+      |    .violet
+      |    .cyan
+      |    .magenta
+      |    .key
+      |    .shine()
+      |    .z { it }
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining - many dereferences, one short lambda and invocation in the middle`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |rainbow.red.orange.yellow
+      |    .green
+      |    .blue
+      |    .indigo
+      |    .z { it }
+      |    .shine()
+      |    .violet
+      |    .cyan
+      |    .magenta
+      |    .key
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining - many dereferences, invocation and one short lambda in the middle`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |rainbow.red.orange.yellow
+      |    .green
+      |    .blue
+      |    .indigo
+      |    .shine()
+      |    .z { it }
+      |    .violet
+      |    .cyan
+      |    .magenta
+      |    .key
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining - many dereferences, starting with this`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |this.red.orange.yellow
+      |    .green
+      |    .blue
+      |    .indigo
+      |    .violet
+      |    .cyan
+      |    .magenta
+      |    .key
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining - many dereferences, starting with this, one invocation at end`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |this.red.orange.yellow
+      |    .green
+      |    .blue
+      |    .indigo
+      |    .violet
+      |    .cyan
+      |    .magenta
+      |    .key
+      |    .build()
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining - many dereferences, starting with super`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |super.red.orange.yellow
+      |    .green
+      |    .blue
+      |    .indigo
+      |    .violet
+      |    .cyan
+      |    .magenta
+      |    .key
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining - many dereferences, starting with super, one invocation at end`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |super.red.orange.yellow
+      |    .green
+      |    .blue
+      |    .indigo
+      |    .violet
+      |    .cyan
+      |    .magenta
+      |    .key
+      |    .build()
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining - many dereferences, starting with short variable`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |z123.red.orange.yellow
+      |    .green
+      |    .blue
+      |    .indigo
+      |    .violet
+      |    .cyan
+      |    .magenta
+      |    .key
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining - many dereferences, starting with short variable, one invocation at end`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |z123.red.orange.yellow
+      |    .green
+      |    .blue
+      |    .indigo
+      |    .violet
+      |    .cyan
+      |    .magenta
+      |    .key
+      |    .build()
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining - many dereferences, starting with short variable and lambda, invocation at end`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |z12.z { it }
+      |    .red
+      |    .orange
+      |    .yellow
+      |    .green
+      |    .blue
+      |    .indigo
+      |    .violet
+      |    .cyan
+      |    .magenta
+      |    .key
+      |    .shine()
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining - many dereferences, starting with this and lambda, invocation at end`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |this.z { it }
+      |    .red
+      |    .orange
+      |    .yellow
+      |    .green
+      |    .blue
+      |    .indigo
+      |    .violet
+      |    .cyan
+      |    .magenta
+      |    .key
+      |    .shine()
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining - many invocations`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |rainbow.a().b().c()
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining - many invocations, with multiline lambda at end`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |rainbow.a().b().c().zz {
+      |  it
+      |  it
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining - many dereferences, starting type name`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |com.sky.Rainbow.red
+      |    .orange
+      |    .yellow
+      |    .green
+      |    .blue
+      |    .indigo
+      |    .violet
+      |    .cyan
+      |    .magenta
+      |    .key
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining - many invocations, starting with short variable, lambda at end`() =
+      assertFormatted(
+          """
+      |-------------
+      |z12.shine()
+      |    .bright()
+      |    .z { it }
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining - start with invocation, lambda at end`() =
+      assertFormatted(
+          """
+      |---------------------
+      |getRainbow(
+      |        aa, bb, cc)
+      |    .z { it }
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining - many invocations, start with lambda`() =
+      assertFormatted(
+          """
+      |---------------------
+      |z { it }
+      |    .shine()
+      |    .bright()
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining - start with type name, end with invocation`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |com.sky.Rainbow
+      |    .colorFactory
+      |    .build()
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining (indentation) - multiline lambda`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |rainbow.z {
+      |  it
+      |  it
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining (indentation) - multiline lambda with trailing dereferences`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |rainbow
+      |    .z {
+      |      it
+      |      it
+      |    }
+      |    .red
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining (indentation) - multiline lambda with long name`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |rainbow
+      |    .someLongLambdaName {
+      |      it
+      |      it
+      |    }
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining (indentation) - multiline lambda with long name and trailing dereferences`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |rainbow
+      |    .someLongLambdaName {
+      |      it
+      |      it
+      |    }
+      |    .red
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining (indentation) - multiline lambda with prefix`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |rainbow.red.z {
+      |  it
+      |  it
+      |}
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining (indentation) - multiline lambda with prefix, forced to next line`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |rainbow.red.orange.yellow
+      |    .longLambdaName {
+      |      it
+      |      it
+      |    }
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining (indentation) - multiline lambda with prefix, forced to next line with another expression`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |rainbow.red.orange.yellow
+      |    .key
+      |    .longLambdaName {
+      |      it
+      |      it
+      |    }
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining (indentation) - multiline arguments`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |rainbow.shine(
+      |    infrared,
+      |    ultraviolet,
+      |)
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining (indentation) - multiline arguments with trailing dereferences`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |rainbow
+      |    .shine(
+      |        infrared,
+      |        ultraviolet,
+      |    )
+      |    .red
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining (indentation) - multiline arguments, forced to next line`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |rainbow.red.orange.yellow
+      |    .shine(
+      |        infrared,
+      |        ultraviolet,
+      |    )
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining (indentation) - multiline arguments, forced to next line with another expression`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |rainbow.red.orange.yellow
+      |    .key
+      |    .shine(
+      |        infrared,
+      |        ultraviolet,
+      |    )
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining (indentation) - multiline arguments, forced to next line with another expression, with trailing dereferences`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |rainbow.red.orange.yellow
+      |    .key
+      |    .shine(
+      |        infrared,
+      |        ultraviolet,
+      |    )
+      |    .red
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining (indentation) - multiline arguments, with trailing invocation`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |rainbow
+      |    .shine(
+      |        infrared,
+      |        ultraviolet,
+      |    )
+      |    .bright()
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining (indentation) - multiline arguments, with trailing lambda`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |rainbow
+      |    .shine(
+      |        infrared,
+      |        ultraviolet,
+      |    )
+      |    .z { it }
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining (indentation) - multiline arguments, prefixed with super, with trailing invocation`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |super.shine(
+      |        infrared,
+      |        ultraviolet,
+      |    )
+      |    .bright()
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining (indentation) - multiline arguments, starting with short variable, with trailing invocation`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |z12.shine(
+      |        infrared,
+      |        ultraviolet,
+      |    )
+      |    .bright()
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining (indentation) - start with multiline arguments`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |getRainbow(
+      |    infrared,
+      |    ultraviolet,
+      |)
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `chaining (indentation) - start with multiline arguments, with trailing invocation`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |getRainbow(
+      |        infrared,
+      |        ultraviolet,
+      |    )
+      |    .z { it }
+      |""".trimMargin(),
+          deduceMaxWidth = true)
+
+  @Test
+  fun `annotations for expressions`() =
+      assertFormatted(
+          """
+      |fun f() {
+      |  var b
+      |  @Suppress("UNCHECKED_CAST") b = f(1) as Int
+      |  @Suppress("UNCHECKED_CAST")
+      |  b = f(1) as Int
+      |
+      |  @Suppress("UNCHECKED_CAST") b = f(1) to 5
+      |  @Suppress("UNCHECKED_CAST")
+      |  b = f(1) to 5
+      |
+      |  @Suppress("UNCHECKED_CAST") f(1) as Int + 5
+      |  @Suppress("UNCHECKED_CAST")
+      |  f(1) as Int + 5
+      |
+      |  @Anno1 /* comment */ @Anno2 f(1) as Int
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `annotations for expressions 2`() {
+    val code =
+        """
+      |fun f() {
+      |  @Suppress("UNCHECKED_CAST") f(1 + f(1) as Int)
+      |  @Suppress("UNCHECKED_CAST")
+      |  f(1 + f(1) as Int)
+      |}
+      |""".trimMargin()
+
+    val expected =
+        """
+      |fun f() {
+      |  @Suppress("UNCHECKED_CAST") f(1 + f(1) as Int)
+      |  @Suppress("UNCHECKED_CAST") f(1 + f(1) as Int)
+      |}
+      |""".trimMargin()
+
+    assertThatFormatting(code).isEqualTo(expected)
+  }
+}
diff --git a/core/src/test/java/com/facebook/ktfmt/format/GoogleStyleFormatterKtTest.kt b/core/src/test/java/com/facebook/ktfmt/format/GoogleStyleFormatterKtTest.kt
new file mode 100644
index 0000000..61b6b47
--- /dev/null
+++ b/core/src/test/java/com/facebook/ktfmt/format/GoogleStyleFormatterKtTest.kt
@@ -0,0 +1,1024 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.facebook.ktfmt.format
+
+import com.facebook.ktfmt.testutil.assertFormatted
+import com.facebook.ktfmt.testutil.assertThatFormatting
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@Suppress("FunctionNaming")
+@RunWith(JUnit4::class)
+class GoogleStyleFormatterKtTest {
+
+  @Test
+  fun `kitchen sink of tests`() {
+    // Don't add more tests here
+    val code =
+        """
+        |fun
+        |f  (
+        |a : Int
+        | , b: Double , c:String) {           var result = 0
+        | val aVeryLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongVar = 43
+        |          foo.bar.zed.accept(
+        |
+        |          )
+        |
+        |          foo(
+        |
+        |          )
+        |
+        |          foo.bar.zed.accept(
+        |                DoSomething.bar()
+        |          )
+        |
+        |          bar(
+        |          ImmutableList.newBuilder().add(1).add(1).add(1).add(1).add(1).add(1).add(1).add(1).add(1).add(1).build())
+        |
+        |
+        |          ImmutableList.newBuilder().add(1).add(1).add(1).add(1).add(1).add(1).add(1).add(1).add(1).add(1).build()
+        |     }
+        |""".trimMargin()
+
+    val expected =
+        """
+        |fun f(a: Int, b: Double, c: String) {
+        |  var result = 0
+        |  val aVeryLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongVar =
+        |    43
+        |  foo.bar.zed.accept()
+        |
+        |  foo()
+        |
+        |  foo.bar.zed.accept(DoSomething.bar())
+        |
+        |  bar(
+        |    ImmutableList.newBuilder()
+        |      .add(1)
+        |      .add(1)
+        |      .add(1)
+        |      .add(1)
+        |      .add(1)
+        |      .add(1)
+        |      .add(1)
+        |      .add(1)
+        |      .add(1)
+        |      .add(1)
+        |      .build()
+        |  )
+        |
+        |  ImmutableList.newBuilder()
+        |    .add(1)
+        |    .add(1)
+        |    .add(1)
+        |    .add(1)
+        |    .add(1)
+        |    .add(1)
+        |    .add(1)
+        |    .add(1)
+        |    .add(1)
+        |    .add(1)
+        |    .build()
+        |}
+        |""".trimMargin()
+
+    assertThatFormatting(code).withOptions(Formatter.GOOGLE_FORMAT).isEqualTo(expected)
+    // Don't add more tests here
+  }
+
+  @Test
+  fun `class params are placed each in their own line`() =
+      assertFormatted(
+          """
+      |-----------------------------------------
+      |class Foo(
+      |  a: Int,
+      |  var b: Double,
+      |  val c: String
+      |) {
+      |  //
+      |}
+      |
+      |class Foo(
+      |  a: Int,
+      |  var b: Double,
+      |  val c: String
+      |)
+      |
+      |class Foo(
+      |  a: Int,
+      |  var b: Int,
+      |  val c: Int
+      |) {
+      |  //
+      |}
+      |
+      |class Bi(
+      |  a: Int,
+      |  var b: Int,
+      |  val c: Int
+      |) {
+      |  //
+      |}
+      |
+      |class C(a: Int, var b: Int, val c: Int) {
+      |  //
+      |}
+      |""".trimMargin(),
+          formattingOptions = Formatter.GOOGLE_FORMAT,
+          deduceMaxWidth = true)
+
+  @Test
+  fun `function params are placed each in their own line`() =
+      assertFormatted(
+          """
+      |-----------------------------------------
+      |fun foo12(
+      |  a: Int,
+      |  var b: Double,
+      |  val c: String
+      |) {
+      |  //
+      |}
+      |
+      |fun foo12(
+      |  a: Int,
+      |  var b: Double,
+      |  val c: String
+      |)
+      |
+      |fun foo12(
+      |  a: Int,
+      |  var b: Double,
+      |  val c: String
+      |) = 5
+      |
+      |fun foo12(
+      |  a: Int,
+      |  var b: Int,
+      |  val c: Int
+      |) {
+      |  //
+      |}
+      |
+      |fun bi12(
+      |  a: Int,
+      |  var b: Int,
+      |  val c: Int
+      |) {
+      |  //
+      |}
+      |
+      |fun c12(a: Int, var b: Int, val c: Int) {
+      |  //
+      |}
+      |""".trimMargin(),
+          formattingOptions = Formatter.GOOGLE_FORMAT,
+          deduceMaxWidth = true)
+
+  @Test
+  fun `return type doesn't fit in one line`() =
+      assertFormatted(
+          """
+      |--------------------------------------------------
+      |interface X {
+      |  fun f(
+      |    arg1: Arg1Type,
+      |    arg2: Arg2Type
+      |  ): Map<String, Map<String, Double>>? {
+      |    //
+      |  }
+      |
+      |  fun functionWithGenericReturnType(
+      |    arg1: Arg1Type,
+      |    arg2: Arg2Type
+      |  ): Map<String, Map<String, Double>>? {
+      |    //
+      |  }
+      |}
+      |""".trimMargin(),
+          formattingOptions = Formatter.GOOGLE_FORMAT,
+          deduceMaxWidth = true)
+
+  @Test
+  fun `indent parameters after a break when there's a lambda afterwards`() =
+      assertFormatted(
+          """
+      |---------------------------
+      |class C {
+      |  fun method() {
+      |    Foo.FooBar(
+      |        param1,
+      |        param2
+      |      )
+      |      .apply {
+      |        //
+      |        foo
+      |      }
+      |  }
+      |}
+      |""".trimMargin(),
+          formattingOptions = Formatter.GOOGLE_FORMAT,
+          deduceMaxWidth = true)
+
+  @Test
+  fun `don't one-line lambdas following parameter breaks`() =
+      assertFormatted(
+          """
+      |------------------------------------------------------------------------
+      |class Foo : Bar() {
+      |  fun doIt() {
+      |    // don't break in lambda, no parameter breaks found
+      |    fruit.forEach { eat(it) }
+      |
+      |    // don't break in lambda, because we only detect parameter breaks
+      |    // with trailing commas
+      |    fruit.forEach(
+      |      someVeryLongParameterNameThatWillCauseABreak,
+      |      evenWithoutATrailingCommaOnTheParameterListSoLetsSeeIt
+      |    ) { eat(it) }
+      |
+      |    // break in the lambda
+      |    fruit.forEach(
+      |      fromTheVine = true,
+      |    ) {
+      |      eat(it)
+      |    }
+      |
+      |    // don't break in the inner lambda, as nesting doesn't respect outer levels
+      |    fruit.forEach(
+      |      fromTheVine = true,
+      |    ) {
+      |      fruit.forEach { eat(it) }
+      |    }
+      |
+      |    // don't break in the lambda, as breaks don't propagate
+      |    fruit
+      |      .onlyBananas(
+      |        fromTheVine = true,
+      |      )
+      |      .forEach { eat(it) }
+      |
+      |    // don't break in the inner lambda, as breaks don't propagate to parameters
+      |    fruit.onlyBananas(
+      |      fromTheVine = true,
+      |      processThem = { eat(it) },
+      |    ) {
+      |      eat(it)
+      |    }
+      |
+      |    // don't break in the inner lambda, as breaks don't propagate to the body
+      |    fruit.onlyBananas(
+      |      fromTheVine = true,
+      |    ) {
+      |      val anon = { eat(it) }
+      |    }
+      |  }
+      |}
+      |""".trimMargin(),
+          formattingOptions = Formatter.GOOGLE_FORMAT,
+          deduceMaxWidth = true)
+
+  @Test
+  fun `function calls with multiple arguments`() =
+      assertFormatted(
+          """
+      |fun f() {
+      |  foo(1, 2, 3)
+      |
+      |  foo(
+      |    123456789012345678901234567890,
+      |    123456789012345678901234567890,
+      |    123456789012345678901234567890
+      |  )
+      |}
+      |""".trimMargin(),
+          formattingOptions = Formatter.GOOGLE_FORMAT)
+
+  @Test
+  fun `line breaks inside when expressions and conditions`() =
+      assertFormatted(
+          """
+      |fun f() {
+      |  return Text.create(c)
+      |    .onTouch {
+      |      when (it.motionEvent.action) {
+      |        ACTION_DOWN ->
+      |          Toast.makeText(it.view.context, "Down!", Toast.LENGTH_SHORT, blablabla).show()
+      |        ACTION_UP -> Toast.makeText(it.view.context, "Up!", Toast.LENGTH_SHORT).show()
+      |        ACTION_DOWN ->
+      |          Toast.makeText(
+      |              it.view.context,
+      |              "Down!",
+      |              Toast.LENGTH_SHORT,
+      |              blablabla,
+      |              blablabl,
+      |              blablabl,
+      |              blablabl,
+      |              blabla
+      |            )
+      |            .show()
+      |      }
+      |    }
+      |    .build()
+      |}
+      |""".trimMargin(),
+          formattingOptions = Formatter.GOOGLE_FORMAT,
+      )
+
+  @Test
+  fun `anonymous function`() =
+      assertFormatted(
+          """
+      |fun f() {
+      |  setListener(
+      |    fun(number: Int) {
+      |      println(number)
+      |    }
+      |  )
+      |}
+      |""".trimMargin(),
+          formattingOptions = Formatter.GOOGLE_FORMAT,
+      )
+
+  @Test
+  fun `avoid newline before lambda argument if it is named`() =
+      assertFormatted(
+          """
+      |private fun f(items: List<Int>) {
+      |  doSomethingCool(
+      |    items,
+      |    lambdaArgument = {
+      |      step1()
+      |      step2()
+      |    }
+      |  ) { it.doIt() }
+      |}
+      |""".trimMargin(),
+          formattingOptions = Formatter.GOOGLE_FORMAT,
+      )
+
+  @Test
+  fun `anonymous function with receiver`() =
+      assertFormatted(
+          """
+      |fun f() {
+      |  setListener(
+      |    fun View.() {
+      |      println(this)
+      |    }
+      |  )
+      |}
+      |""".trimMargin(),
+          formattingOptions = Formatter.GOOGLE_FORMAT,
+      )
+
+  @Test
+  fun `function calls with multiple named arguments`() =
+      assertFormatted(
+          """
+      |fun f() {
+      |  foo(1, b = 2, c = 3)
+      |
+      |  foo(
+      |    123456789012345678901234567890,
+      |    b = 23456789012345678901234567890,
+      |    c = 3456789012345678901234567890
+      |  )
+      |}
+      |""".trimMargin(),
+          formattingOptions = Formatter.GOOGLE_FORMAT)
+
+  @Test
+  fun `Arguments are blocks`() =
+      assertFormatted(
+          """
+      |--------------------------------------------------
+      |override fun visitProperty(property: KtProperty) {
+      |  builder.sync(property)
+      |  builder.block(ZERO) {
+      |    declareOne(
+      |      kind = DeclarationKind.FIELD,
+      |      modifiers = property.modifierList,
+      |      valOrVarKeyword =
+      |        property.valOrVarKeyword.text,
+      |      typeParametersBlaBla =
+      |        property.typeParameterList,
+      |      receiver = property.receiverTypeReference,
+      |      name = property.nameIdentifier?.text,
+      |      type = property.typeReference,
+      |      typeConstraintList =
+      |        property.typeConstraintList,
+      |      delegate = property.delegate,
+      |      initializer = property.initializer
+      |    )
+      |  }
+      |}
+      |""".trimMargin(),
+          formattingOptions = Formatter.GOOGLE_FORMAT,
+          deduceMaxWidth = true)
+
+  @Test
+  fun `keep last expression in qualified indented`() =
+      assertFormatted(
+          """
+      |-----------------------
+      |fun f() {
+      |  Stuff()
+      |    .doIt(
+      |      Foo.doIt()
+      |        .doThat()
+      |    )
+      |    .doIt(
+      |      Foo.doIt()
+      |        .doThat()
+      |    )
+      |}
+      |""".trimMargin(),
+          formattingOptions = Formatter.GOOGLE_FORMAT,
+          deduceMaxWidth = true)
+
+  @Test
+  fun `if expression with else`() =
+      assertFormatted(
+          """
+      |fun maybePrint(b: Boolean) {
+      |  println(if (b) 1 else 2)
+      |  println(
+      |    if (b) {
+      |      val a = 1 + 1
+      |      2 * a
+      |    } else 2
+      |  )
+      |  return if (b) 1 else 2
+      |}
+      |""".trimMargin(),
+          formattingOptions = Formatter.GOOGLE_FORMAT)
+
+  @Test
+  fun `named arguments indent their value expression`() =
+      assertFormatted(
+          """
+      |fun f() =
+      |  Bar(
+      |    tokens =
+      |      mutableListOf<Token>().apply {
+      |        // Printing
+      |        print()
+      |      },
+      |    duration = duration
+      |  )
+      |""".trimMargin(),
+          formattingOptions = Formatter.GOOGLE_FORMAT)
+
+  @Test
+  fun `breaking long binary operations`() =
+      assertFormatted(
+          """
+      |--------------------
+      |fun foo() {
+      |  val finalWidth =
+      |    value1 +
+      |      value2 +
+      |      (value3 +
+      |        value4 +
+      |        value5) +
+      |      foo(v) +
+      |      (1 + 2) +
+      |      function(
+      |        value7,
+      |        value8
+      |      ) +
+      |      value9
+      |}
+      |""".trimMargin(),
+          formattingOptions = Formatter.GOOGLE_FORMAT,
+          deduceMaxWidth = true)
+
+  @Test
+  fun `handle casting with breaks`() =
+      assertFormatted(
+          """
+      |-------------------
+      |fun castIt(
+      |  something: Any
+      |) {
+      |  doIt(
+      |    something
+      |      as List<*>
+      |  )
+      |  doIt(
+      |    something
+      |      is List<*>
+      |  )
+      |  println(
+      |    something
+      |      is
+      |      List<String>
+      |  )
+      |  doIt(
+      |    something
+      |      as
+      |      List<String>
+      |  )
+      |  println(
+      |    something
+      |      is
+      |      PairList<
+      |        String, Int
+      |      >
+      |  )
+      |  doIt(
+      |    something
+      |      as
+      |      PairList<
+      |        String, Int
+      |      >
+      |  )
+      |  println(
+      |    a is Int &&
+      |      b is String
+      |  )
+      |}
+      |""".trimMargin(),
+          formattingOptions = Formatter.GOOGLE_FORMAT,
+          deduceMaxWidth = true)
+
+  @Test
+  fun `line breaks in function arguments`() =
+      assertFormatted(
+          """
+      |--------------------------------------------------
+      |fun f() {
+      |  computeBreaks(
+      |    javaOutput.commentsHelper,
+      |    maxWidth,
+      |    Doc.State(+0, 0)
+      |  )
+      |  computeBreaks(
+      |    output.commentsHelper,
+      |    maxWidth,
+      |    State(0)
+      |  )
+      |  doc.computeBreaks(
+      |    javaOutput.commentsHelper,
+      |    maxWidth,
+      |    Doc.State(+0, 0)
+      |  )
+      |  doc.computeBreaks(
+      |    output.commentsHelper,
+      |    maxWidth,
+      |    State(0)
+      |  )
+      |}
+      |""".trimMargin(),
+          formattingOptions = Formatter.GOOGLE_FORMAT,
+          deduceMaxWidth = true)
+
+  // TODO: there's a bug here - the last case shouldn't break after 'foo'.
+  @Test
+  fun `different indentation in chained calls`() =
+      assertFormatted(
+          """
+      |----------------------
+      |fun f() {
+      |  fooDdoIt(
+      |    foo1,
+      |    foo2,
+      |    foo3
+      |  )
+      |  foo.doIt(
+      |    foo1,
+      |    foo2,
+      |    foo3
+      |  )
+      |  foo
+      |    .doIt(
+      |      foo1,
+      |      foo2,
+      |      foo3
+      |    )
+      |    .doThat()
+      |}
+      |""".trimMargin(),
+          formattingOptions = Formatter.GOOGLE_FORMAT,
+          deduceMaxWidth = true)
+
+  @Test
+  fun `a secondary constructor with many arguments passed to delegate`() =
+      assertFormatted(
+          """
+      |--------------------------------------------------
+      |data class Foo {
+      |  constructor(
+      |    val number: Int,
+      |    val name: String,
+      |    val age: Int,
+      |    val title: String,
+      |    val offspring: List<Foo>
+      |  ) : this(
+      |    number,
+      |    name,
+      |    age,
+      |    title,
+      |    offspring,
+      |    offspring
+      |  )
+      |}
+      |""".trimMargin(),
+          formattingOptions = Formatter.GOOGLE_FORMAT,
+          deduceMaxWidth = true)
+
+  @Test
+  fun `a secondary constructor with no arguments passed to delegate`() =
+      assertFormatted(
+          """
+      |--------------------------------------------------
+      |data class Foo {
+      |  constructor() :
+      |    this(
+      |      Foo.createSpeciallyDesignedParameter(),
+      |      Foo.createSpeciallyDesignedParameter(),
+      |    )
+      |}
+      |""".trimMargin(),
+          formattingOptions = Formatter.GOOGLE_FORMAT,
+          deduceMaxWidth = true)
+
+  @Test
+  fun `handle trailing commas (function calls)`() =
+      assertFormatted(
+          """
+      |------------------------
+      |fun main() {
+      |  foo(
+      |    3,
+      |  )
+      |
+      |  foo<Int>(
+      |    3,
+      |  )
+      |
+      |  foo<
+      |    Int,
+      |  >(
+      |    3,
+      |  )
+      |
+      |  foo<Int>(
+      |    "asdf",
+      |    "asdf"
+      |  )
+      |
+      |  foo<
+      |    Int,
+      |  >(
+      |    "asd",
+      |    "asd",
+      |  )
+      |
+      |  foo<
+      |    Int,
+      |    Boolean,
+      |  >(
+      |    3,
+      |  )
+      |}
+      |""".trimMargin(),
+          formattingOptions = Formatter.GOOGLE_FORMAT,
+          deduceMaxWidth = true)
+
+  @Test
+  fun `an assortment of tests for emitQualifiedExpression`() =
+      assertFormatted(
+          """
+      |--------------------------------------
+      |fun f() {
+      |  // Regression test: https://github.com/facebookincubator/ktfmt/issues/56
+      |  kjsdfglkjdfgkjdfkgjhkerjghkdfj
+      |    ?.methodName1()
+      |
+      |  // a series of field accesses followed by a single call expression
+      |  // is kept together.
+      |  abcdefghijkl.abcdefghijkl
+      |    ?.methodName2()
+      |
+      |  // Similar to above.
+      |  abcdefghijkl.abcdefghijkl
+      |    ?.methodName3
+      |    ?.abcdefghijkl()
+      |
+      |  // Multiple call expressions cause each part of the expression
+      |  // to be placed on its own line.
+      |  abcdefghijkl
+      |    ?.abcdefghijkl
+      |    ?.methodName4()
+      |    ?.abcdefghijkl()
+      |
+      |  // Don't break first call expression if it fits.
+      |  foIt(something.something.happens())
+      |    .thenReturn(result)
+      |
+      |  // Break after `longerThanFour(` because it's longer than 4 chars
+      |  longerThanFour(
+      |      something.somethingBlaBla
+      |        .happens()
+      |    )
+      |    .thenReturn(result)
+      |
+      |  // Similarly to above, when part of qualified expression.
+      |  foo
+      |    .longerThanFour(
+      |      something.somethingBlaBla
+      |        .happens()
+      |    )
+      |    .thenReturn(result)
+      |
+      |  // Keep 'super' attached to the method name
+      |  super.abcdefghijkl
+      |    .methodName4()
+      |    .abcdefghijkl()
+      |}
+      |""".trimMargin(),
+          formattingOptions = Formatter.GOOGLE_FORMAT,
+          deduceMaxWidth = true)
+
+  @Test
+  fun `keep parenthesis and braces together when there's only one lambda argument`() =
+      assertFormatted(
+          """
+      |fun f() {
+      |  doIt({})
+      |  doIt({ it + it })
+      |  doIt({
+      |    val a = it
+      |    a + a
+      |  })
+      |  doIt(functor = { it + it })
+      |  doIt(
+      |    functor = {
+      |      val a = it
+      |      a + a
+      |    }
+      |  )
+      |}
+      |""".trimMargin(),
+          formattingOptions = Formatter.GOOGLE_FORMAT)
+
+  @Test
+  fun `chained calls that don't fit in one line`() =
+      assertFormatted(
+          """
+      |---------------------------
+      |fun f() {
+      |  foo(
+      |      println("a"),
+      |      println("b")
+      |    )
+      |    .bar(
+      |      println("b"),
+      |      println("b")
+      |    )
+      |}
+      |""".trimMargin(),
+          formattingOptions = Formatter.GOOGLE_FORMAT,
+          deduceMaxWidth = true)
+
+  @Test
+  fun `lambda assigned to variable does not break before brace`() =
+      assertFormatted(
+          """
+      |fun doIt() {
+      |  val lambda = {
+      |    doItOnce()
+      |    doItTwice()
+      |  }
+      |}
+      |
+      |fun foo() = {
+      |  doItOnce()
+      |  doItTwice()
+      |}
+      |""".trimMargin())
+
+  @Test
+  fun `if expression with multiline condition`() =
+      assertFormatted(
+          """
+      |----------------------------
+      |fun foo() {
+      |  if (
+      |    expressions1 &&
+      |      expression2 &&
+      |      expression3
+      |  ) {
+      |    bar()
+      |  }
+      |
+      |  if (
+      |    foo(
+      |      expressions1 &&
+      |        expression2 &&
+      |        expression3
+      |    )
+      |  ) {
+      |    bar()
+      |  }
+      |}
+      |""".trimMargin(),
+          formattingOptions = Formatter.GOOGLE_FORMAT,
+          deduceMaxWidth = true)
+
+  @Test
+  fun `if expression with condition that exactly fits to line`() =
+      assertFormatted(
+          """
+      |-------------------------
+      |fun foo() {
+      |  if (
+      |    e1 && e2 && e3 = e4
+      |  ) {
+      |    bar()
+      |  }
+      |}
+      |""".trimMargin(),
+          formattingOptions = Formatter.GOOGLE_FORMAT,
+          deduceMaxWidth = true)
+
+  @Test
+  fun `when() expression with multiline condition`() =
+      assertFormatted(
+          """
+      |-----------------------
+      |fun foo() {
+      |  when (
+      |    expressions1 +
+      |      expression2 +
+      |      expression3
+      |  ) {
+      |    1 -> print(1)
+      |    2 -> print(2)
+      |  }
+      |
+      |  when (
+      |    foo(
+      |      expressions1 &&
+      |        expression2 &&
+      |        expression3
+      |    )
+      |  ) {
+      |    1 -> print(1)
+      |    2 -> print(2)
+      |  }
+      |}
+      |""".trimMargin(),
+          formattingOptions = Formatter.GOOGLE_FORMAT,
+          deduceMaxWidth = true)
+
+  @Test
+  fun `when expression with condition that exactly fits to line`() =
+      assertFormatted(
+          """
+      |---------------------------
+      |fun foo() {
+      |  when (
+      |    e1 && e2 && e3 = e4
+      |  ) {
+      |    1 -> print(1)
+      |    2 -> print(2)
+      |  }
+      |}
+      |""".trimMargin(),
+          formattingOptions = Formatter.GOOGLE_FORMAT,
+          deduceMaxWidth = true)
+
+  @Test
+  fun `while expression with multiline condition`() =
+      assertFormatted(
+          """
+      |----------------------------
+      |fun foo() {
+      |  while (
+      |    expressions1 &&
+      |      expression2 &&
+      |      expression3
+      |  ) {
+      |    bar()
+      |  }
+      |
+      |  while (
+      |    foo(
+      |      expressions1 &&
+      |        expression2 &&
+      |        expression3
+      |    )
+      |  ) {
+      |    bar()
+      |  }
+      |}
+      |""".trimMargin(),
+          formattingOptions = Formatter.GOOGLE_FORMAT,
+          deduceMaxWidth = true)
+
+  @Test
+  fun `while expression with condition that exactly fits to line`() =
+      assertFormatted(
+          """
+      |----------------------------
+      |fun foo() {
+      |  while (
+      |    e1 && e2 && e3 = e4
+      |  ) {
+      |    bar()
+      |  }
+      |}
+      |""".trimMargin(),
+          formattingOptions = Formatter.GOOGLE_FORMAT,
+          deduceMaxWidth = true)
+
+  @Test
+  fun `handle destructuring declaration`() =
+      assertFormatted(
+          """
+      |-------------------------------------------
+      |fun f() {
+      |  val (a, b: Int) = listOf(1, 2)
+      |  val (asd, asd, asd, asd, asd, asd, asd) =
+      |    foo.bar(asdasd, asdasd)
+      |
+      |  val (accountType, accountId) =
+      |    oneTwoThreeFourFiveSixSeven(
+      |      foo,
+      |      bar,
+      |      zed,
+      |      boo
+      |    )
+      |}
+      |""".trimMargin(),
+          formattingOptions = Formatter.GOOGLE_FORMAT,
+          deduceMaxWidth = true)
+
+  @Test
+  fun `trailing break argument list`() =
+      assertFormatted(
+          """
+      |-------------------
+      |fun method() {
+      |  Foo.FooBar(
+      |    longParameter
+      |  )
+      |  Foo.FooBar(
+      |    param1,
+      |    param2
+      |  )
+      |}
+      |""".trimMargin(),
+          formattingOptions = Formatter.GOOGLE_FORMAT,
+          deduceMaxWidth = true)
+
+  @Test
+  fun `trailing break chains`() =
+      assertFormatted(
+          """
+      |-------------
+      |bar(
+      |  FooOpClass
+      |    .doOp(1)
+      |    .doOp(2)
+      |)
+      |""".trimMargin(),
+          formattingOptions = Formatter.GOOGLE_FORMAT,
+          deduceMaxWidth = true)
+
+  @Test
+  fun `wrapping for long function types`() =
+      assertFormatted(
+          """
+      |------------------------
+      |var listener:
+      |  (
+      |    a: String,
+      |    b: String,
+      |    c: String,
+      |    d: String
+      |  ) -> Unit
+      |""".trimMargin(),
+          formattingOptions = Formatter.GOOGLE_FORMAT,
+          deduceMaxWidth = true)
+}
diff --git a/core/src/test/java/com/facebook/ktfmt/format/KotlinInputTest.kt b/core/src/test/java/com/facebook/ktfmt/format/KotlinInputTest.kt
new file mode 100644
index 0000000..d965f05
--- /dev/null
+++ b/core/src/test/java/com/facebook/ktfmt/format/KotlinInputTest.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.facebook.ktfmt.format
+
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+class KotlinInputTest {
+  @Test
+  fun `Comments are toks not tokens`() {
+    val code = "/** foo */ class F {}"
+    val input = KotlinInput(code, Parser.parse(code))
+    assertThat(input.getTokens().map { it.tok.text })
+        .containsExactly("class", "F", "{", "}", "")
+        .inOrder()
+    assertThat(input.getTokens()[0].toksBefore.map { it.text })
+        .containsExactly("/** foo */", " ")
+        .inOrder()
+  }
+}
diff --git a/core/src/test/java/com/facebook/ktfmt/format/TokenizerTest.kt b/core/src/test/java/com/facebook/ktfmt/format/TokenizerTest.kt
new file mode 100644
index 0000000..3f8fe71
--- /dev/null
+++ b/core/src/test/java/com/facebook/ktfmt/format/TokenizerTest.kt
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.facebook.ktfmt.format
+
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class TokenizerTest {
+  @Test
+  fun `PsiWhiteSpace are split to newlines and maximal-length whitespaces`() {
+    val code =
+        listOf(
+                "val  a = ", //
+                "", //
+                "     ", //
+                "     15")
+            .joinToString("\n")
+
+    val file = Parser.parse(code)
+    val tokenizer = Tokenizer(code, file)
+    file.accept(tokenizer)
+
+    assertThat(tokenizer.toks.map { it.originalText })
+        .containsExactly("val", "  ", "a", " ", "=", " ", "\n", "\n", "     ", "\n", "     ", "15")
+        .inOrder()
+  }
+
+  @Test
+  fun `Strings are returns as a single token`() {
+    val code =
+        listOf(
+                "val a=\"\"\"",
+                "  ",
+                "   ",
+                "    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do ",
+                "    Lorem",
+                "    ",
+                "     ",
+                "      \"\"\"",
+                "val b=\"lorem ipsum\"",
+                "      ",
+                "    ")
+            .joinToString("\n")
+
+    val file = Parser.parse(code)
+    val tokenizer = Tokenizer(code, file)
+    file.accept(tokenizer)
+
+    assertThat(tokenizer.toks.map { it.originalText })
+        .containsExactly(
+            "val",
+            " ",
+            "a",
+            "=",
+            listOf(
+                    "\"\"\"",
+                    " ${WhitespaceTombstones.SPACE_TOMBSTONE}",
+                    "  ${WhitespaceTombstones.SPACE_TOMBSTONE}",
+                    "    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do${WhitespaceTombstones.SPACE_TOMBSTONE}",
+                    "    Lorem",
+                    "   ${WhitespaceTombstones.SPACE_TOMBSTONE}",
+                    "    ${WhitespaceTombstones.SPACE_TOMBSTONE}",
+                    "      \"\"\"")
+                .joinToString("\n"),
+            "\n",
+            "val",
+            " ",
+            "b",
+            "=",
+            "\"lorem ipsum\"",
+            "\n",
+            "      ",
+            "\n",
+            "    ")
+        .inOrder()
+  }
+
+  @Test
+  fun `Token index is advanced after a string token`() {
+    val code = """
+      |val b="a"
+      |val a=5
+      |""".trimMargin().trimMargin()
+
+    val file = Parser.parse(code)
+    val tokenizer = Tokenizer(code, file)
+    file.accept(tokenizer)
+
+    assertThat(tokenizer.toks.map { it.originalText })
+        .containsExactly("val", " ", "b", "=", "\"a\"", "\n", "val", " ", "a", "=", "5")
+        .inOrder()
+    assertThat(tokenizer.toks.map { it.index })
+        .containsExactly(0, -1, 1, 2, 3, -1, 4, -1, 5, 6, 7)
+        .inOrder()
+  }
+}
diff --git a/core/src/test/java/com/facebook/ktfmt/format/WhitespaceTombstonesTest.kt b/core/src/test/java/com/facebook/ktfmt/format/WhitespaceTombstonesTest.kt
new file mode 100644
index 0000000..79ce6ce
--- /dev/null
+++ b/core/src/test/java/com/facebook/ktfmt/format/WhitespaceTombstonesTest.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.facebook.ktfmt.format
+
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class WhitespaceTombstonesTest {
+
+  @Test
+  fun testReplaceTrailingWhitespaceWithTombstone() {
+    assertThat(WhitespaceTombstones.replaceTrailingWhitespaceWithTombstone("")).isEqualTo("")
+
+    assertThat(WhitespaceTombstones.replaceTrailingWhitespaceWithTombstone("  sdfl"))
+        .isEqualTo("  sdfl")
+    assertThat(WhitespaceTombstones.replaceTrailingWhitespaceWithTombstone("  sdfl "))
+        .isEqualTo("  sdfl${WhitespaceTombstones.SPACE_TOMBSTONE}")
+    assertThat(WhitespaceTombstones.replaceTrailingWhitespaceWithTombstone("  sdfl  "))
+        .isEqualTo("  sdfl ${WhitespaceTombstones.SPACE_TOMBSTONE}")
+
+    assertThat(WhitespaceTombstones.replaceTrailingWhitespaceWithTombstone("  sdfl  \n skdjfh"))
+        .isEqualTo("  sdfl ${WhitespaceTombstones.SPACE_TOMBSTONE}\n skdjfh")
+    assertThat(WhitespaceTombstones.replaceTrailingWhitespaceWithTombstone("  sdfl  \n skdjfh "))
+        .isEqualTo(
+            "  sdfl ${WhitespaceTombstones.SPACE_TOMBSTONE}\n skdjfh${WhitespaceTombstones.SPACE_TOMBSTONE}")
+    assertThat(WhitespaceTombstones.replaceTrailingWhitespaceWithTombstone("  sdfl  \n\n skdjfh "))
+        .isEqualTo(
+            "  sdfl ${WhitespaceTombstones.SPACE_TOMBSTONE}\n\n skdjfh${WhitespaceTombstones.SPACE_TOMBSTONE}")
+  }
+}
diff --git a/core/src/test/java/com/facebook/ktfmt/kdoc/EscapingTest.kt b/core/src/test/java/com/facebook/ktfmt/kdoc/EscapingTest.kt
new file mode 100644
index 0000000..688f612
--- /dev/null
+++ b/core/src/test/java/com/facebook/ktfmt/kdoc/EscapingTest.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.facebook.ktfmt.kdoc
+
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class EscapingTest {
+
+  @Test
+  fun `escaping kdoc`() {
+    assertThat(Escaping.escapeKDoc("/** foo */")).isEqualTo("/** foo */")
+    assertThat(Escaping.escapeKDoc("/*** foo */")).isEqualTo("/*** foo */")
+    assertThat(Escaping.escapeKDoc("/** * foo */")).isEqualTo("/** * foo */")
+    assertThat(Escaping.escapeKDoc("/** /* foo */")).isEqualTo("/** \u0004\u0005 foo */")
+    assertThat(Escaping.escapeKDoc("/** /* foo */ */"))
+        .isEqualTo("/** \u0004\u0005 foo \u0005\u0004 */")
+
+    assertThat(Escaping.escapeKDoc("/* /* foo */ */"))
+        .isEqualTo("/* \u0004\u0005 foo \u0005\u0004 */")
+  }
+}
diff --git a/core/src/test/java/com/facebook/ktfmt/kdoc/KDocFormatterTest.kt b/core/src/test/java/com/facebook/ktfmt/kdoc/KDocFormatterTest.kt
new file mode 100644
index 0000000..4efdf42
--- /dev/null
+++ b/core/src/test/java/com/facebook/ktfmt/kdoc/KDocFormatterTest.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.facebook.ktfmt.kdoc
+
+import com.facebook.ktfmt.kdoc.KDocFormatter.tokenizeKdocText
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class KDocFormatterTest {
+  @Test
+  fun testTokenizeKdocText() {
+    assertThat(tokenizeKdocText("     one two three  ").asIterable())
+        .containsExactly(" ", "one", " ", "two", " ", "three", " ")
+        .inOrder()
+    assertThat(tokenizeKdocText("one two three ").asIterable())
+        .containsExactly("one", " ", "two", " ", "three", " ")
+        .inOrder()
+    assertThat(tokenizeKdocText("one two three").asIterable())
+        .containsExactly("one", " ", "two", " ", "three")
+        .inOrder()
+    assertThat(tokenizeKdocText("onetwothree").asIterable())
+        .containsExactly("onetwothree")
+        .inOrder()
+    assertThat(tokenizeKdocText("").asIterable()).isEmpty()
+  }
+}
diff --git a/core/src/test/java/com/facebook/ktfmt/testutil/KtfmtTruth.kt b/core/src/test/java/com/facebook/ktfmt/testutil/KtfmtTruth.kt
new file mode 100644
index 0000000..3a2a620
--- /dev/null
+++ b/core/src/test/java/com/facebook/ktfmt/testutil/KtfmtTruth.kt
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.facebook.ktfmt.testutil
+
+import com.facebook.ktfmt.debughelpers.PrintAstVisitor
+import com.facebook.ktfmt.format.Formatter
+import com.facebook.ktfmt.format.FormattingOptions
+import com.facebook.ktfmt.format.Parser
+import com.google.common.truth.FailureMetadata
+import com.google.common.truth.Subject
+import com.google.common.truth.Truth
+import org.junit.Assert
+
+/**
+ * Verifies the given code passes through formatting, and stays the same at the end
+ *
+ * @param code a code string that continas an optional first line made of "---" in the case
+ * [deduceMaxWidth] is true. For example:
+ * ```
+ * --------------------
+ * // exactly 20 `-` above
+ * fun f()
+ * ```
+ * @param deduceMaxWidth if this is true the code string should start with a line of "-----" in the
+ * beginning to indicate the max width to format by
+ */
+fun assertFormatted(
+    code: String,
+    formattingOptions: FormattingOptions = FormattingOptions(),
+    deduceMaxWidth: Boolean = false
+) {
+  val first = code.lines().first()
+  var deducedCode = code
+  var maxWidth = FormattingOptions.DEFAULT_MAX_WIDTH
+  val isFirstLineAMaxWidthMarker = first.all { it == '-' }
+  if (deduceMaxWidth) {
+    if (!isFirstLineAMaxWidthMarker) {
+      throw RuntimeException(
+          "deduceMaxWidth is false, please remove the first dashes only line from the code (i.e. ---)")
+    }
+    deducedCode = code.substring(code.indexOf('\n') + 1)
+    maxWidth = first.length
+  } else {
+    if (isFirstLineAMaxWidthMarker) {
+      throw RuntimeException(
+          "When deduceMaxWidth is true the first line need to be all dashes only (i.e. ---)")
+    }
+  }
+  assertThatFormatting(deducedCode)
+      .withOptions(formattingOptions.copy(maxWidth = maxWidth))
+      .isEqualTo(deducedCode)
+}
+
+fun assertThatFormatting(code: String): FormattedCodeSubject {
+  fun codes(): Subject.Factory<FormattedCodeSubject, String> {
+    return Subject.Factory { metadata, subject -> FormattedCodeSubject(metadata, subject) }
+  }
+  return Truth.assertAbout(codes()).that(code)
+}
+
+class FormattedCodeSubject(metadata: FailureMetadata, private val code: String) :
+    Subject(metadata, code) {
+  private var options: FormattingOptions = FormattingOptions()
+  private var allowTrailingWhitespace = false
+
+  fun withOptions(options: FormattingOptions): FormattedCodeSubject {
+    this.options = options
+    return this
+  }
+
+  fun allowTrailingWhitespace(): FormattedCodeSubject {
+    this.allowTrailingWhitespace = true
+    return this
+  }
+
+  fun isEqualTo(expectedFormatting: String) {
+    if (!allowTrailingWhitespace && expectedFormatting.lines().any { it.endsWith(" ") }) {
+      throw RuntimeException(
+          "Expected code contains trailing whitespace, which the formatter usually doesn't output:\n" +
+              expectedFormatting
+                  .lines()
+                  .map { if (it.endsWith(" ")) "[$it]" else it }
+                  .joinToString("\n"))
+    }
+    val actualFormatting: String
+    try {
+      actualFormatting = Formatter.format(options, code)
+      if (actualFormatting != expectedFormatting) {
+        reportError(code)
+        println("# Output: ")
+        println("#".repeat(20))
+        println(actualFormatting)
+        println("# Expected: ")
+        println("#".repeat(20))
+        println(expectedFormatting)
+        println("#".repeat(20))
+        println(
+            "Need more information about the break operations?" +
+                "Run test with assertion with \"FormattingOptions(debuggingPrintOpsAfterFormatting = true)\"")
+      }
+    } catch (e: Error) {
+      reportError(code)
+      throw e
+    }
+    Assert.assertEquals(expectedFormatting, actualFormatting)
+  }
+
+  private fun reportError(code: String) {
+    val file = Parser.parse(code)
+    println("# Parse tree of input: ")
+    println("#".repeat(20))
+    file.accept(PrintAstVisitor())
+    println()
+    println("# Input: ")
+    println("#".repeat(20))
+    println(code)
+    println()
+  }
+}
diff --git a/docs/editorconfig/.editorconfig-default b/docs/editorconfig/.editorconfig-default
new file mode 100644
index 0000000..22d8db2
--- /dev/null
+++ b/docs/editorconfig/.editorconfig-default
@@ -0,0 +1,93 @@
+# This .editorconfig section approximates ktfmt's formatting rules. You can include it in an
+# existing .editorconfig file or use it standalone by copying it to <project root>/.editorconfig
+# and making sure your editor is set to read settings from .editorconfig files.
+#
+# It includes editor-specific config options for IntelliJ IDEA.
+#
+# If any option is wrong, PR are welcome
+
+[{*.kt,*.kts}]
+indent_style = space
+insert_final_newline = true
+max_line_length = 100
+indent_size = 2
+ij_continuation_indent_size = 4
+ij_java_names_count_to_use_import_on_demand = 9999
+ij_kotlin_align_in_columns_case_branch = false
+ij_kotlin_align_multiline_binary_operation = false
+ij_kotlin_align_multiline_extends_list = false
+ij_kotlin_align_multiline_method_parentheses = false
+ij_kotlin_align_multiline_parameters = true
+ij_kotlin_align_multiline_parameters_in_calls = false
+ij_kotlin_allow_trailing_comma = true
+ij_kotlin_allow_trailing_comma_on_call_site = true
+ij_kotlin_assignment_wrap = normal
+ij_kotlin_blank_lines_after_class_header = 0
+ij_kotlin_blank_lines_around_block_when_branches = 0
+ij_kotlin_blank_lines_before_declaration_with_comment_or_annotation_on_separate_line = 1
+ij_kotlin_block_comment_at_first_column = true
+ij_kotlin_call_parameters_new_line_after_left_paren = true
+ij_kotlin_call_parameters_right_paren_on_new_line = false
+ij_kotlin_call_parameters_wrap = on_every_item
+ij_kotlin_catch_on_new_line = false
+ij_kotlin_class_annotation_wrap = split_into_lines
+ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL
+ij_kotlin_continuation_indent_for_chained_calls = true
+ij_kotlin_continuation_indent_for_expression_bodies = true
+ij_kotlin_continuation_indent_in_argument_lists = true
+ij_kotlin_continuation_indent_in_elvis = false
+ij_kotlin_continuation_indent_in_if_conditions = false
+ij_kotlin_continuation_indent_in_parameter_lists = false
+ij_kotlin_continuation_indent_in_supertype_lists = false
+ij_kotlin_else_on_new_line = false
+ij_kotlin_enum_constants_wrap = off
+ij_kotlin_extends_list_wrap = normal
+ij_kotlin_field_annotation_wrap = split_into_lines
+ij_kotlin_finally_on_new_line = false
+ij_kotlin_if_rparen_on_new_line = false
+ij_kotlin_import_nested_classes = false
+ij_kotlin_insert_whitespaces_in_simple_one_line_method = true
+ij_kotlin_keep_blank_lines_before_right_brace = 2
+ij_kotlin_keep_blank_lines_in_code = 2
+ij_kotlin_keep_blank_lines_in_declarations = 2
+ij_kotlin_keep_first_column_comment = true
+ij_kotlin_keep_indents_on_empty_lines = false
+ij_kotlin_keep_line_breaks = true
+ij_kotlin_lbrace_on_next_line = false
+ij_kotlin_line_comment_add_space = false
+ij_kotlin_line_comment_at_first_column = true
+ij_kotlin_method_annotation_wrap = split_into_lines
+ij_kotlin_method_call_chain_wrap = normal
+ij_kotlin_method_parameters_new_line_after_left_paren = true
+ij_kotlin_method_parameters_right_paren_on_new_line = true
+ij_kotlin_method_parameters_wrap = on_every_item
+ij_kotlin_name_count_to_use_star_import = 9999
+ij_kotlin_name_count_to_use_star_import_for_members = 9999
+ij_kotlin_parameter_annotation_wrap = off
+ij_kotlin_space_after_comma = true
+ij_kotlin_space_after_extend_colon = true
+ij_kotlin_space_after_type_colon = true
+ij_kotlin_space_before_catch_parentheses = true
+ij_kotlin_space_before_comma = false
+ij_kotlin_space_before_extend_colon = true
+ij_kotlin_space_before_for_parentheses = true
+ij_kotlin_space_before_if_parentheses = true
+ij_kotlin_space_before_lambda_arrow = true
+ij_kotlin_space_before_type_colon = false
+ij_kotlin_space_before_when_parentheses = true
+ij_kotlin_space_before_while_parentheses = true
+ij_kotlin_spaces_around_additive_operators = true
+ij_kotlin_spaces_around_assignment_operators = true
+ij_kotlin_spaces_around_equality_operators = true
+ij_kotlin_spaces_around_function_type_arrow = true
+ij_kotlin_spaces_around_logical_operators = true
+ij_kotlin_spaces_around_multiplicative_operators = true
+ij_kotlin_spaces_around_range = false
+ij_kotlin_spaces_around_relational_operators = true
+ij_kotlin_spaces_around_unary_operator = false
+ij_kotlin_spaces_around_when_arrow = true
+ij_kotlin_variable_annotation_wrap = off
+ij_kotlin_while_on_new_line = false
+ij_kotlin_wrap_elvis_expressions = 1
+ij_kotlin_wrap_expression_body_functions = 1
+ij_kotlin_wrap_first_method_in_call_chain = false
diff --git a/docs/editorconfig/.editorconfig-dropbox b/docs/editorconfig/.editorconfig-dropbox
new file mode 100644
index 0000000..bc214f6
--- /dev/null
+++ b/docs/editorconfig/.editorconfig-dropbox
@@ -0,0 +1,94 @@
+# This .editorconfig section approximates ktfmt's formatting rules. You can include it in an
+# existing .editorconfig file or use it standalone by copying it to <project root>/.editorconfig
+# and making sure your editor is set to read settings from .editorconfig files.
+#
+# It includes editor-specific config options for IntelliJ IDEA.
+#
+# If any option is wrong, PR are welcome
+
+[{*.kt,*.kts}]
+indent_style = space
+insert_final_newline = true
+max_line_length = 100
+indent_size = 4
+ij_continuation_indent_size = 8
+ij_java_names_count_to_use_import_on_demand = 9999
+ij_kotlin_align_in_columns_case_branch = false
+ij_kotlin_align_multiline_binary_operation = false
+ij_kotlin_align_multiline_extends_list = false
+ij_kotlin_align_multiline_method_parentheses = false
+ij_kotlin_align_multiline_parameters = true
+ij_kotlin_align_multiline_parameters_in_calls = false
+ij_kotlin_allow_trailing_comma = true
+ij_kotlin_allow_trailing_comma_on_call_site = true
+ij_kotlin_assignment_wrap = normal
+ij_kotlin_blank_lines_after_class_header = 0
+ij_kotlin_blank_lines_around_block_when_branches = 0
+ij_kotlin_blank_lines_before_declaration_with_comment_or_annotation_on_separate_line = 1
+ij_kotlin_block_comment_at_first_column = true
+ij_kotlin_call_parameters_new_line_after_left_paren = true
+ij_kotlin_call_parameters_right_paren_on_new_line = false
+ij_kotlin_call_parameters_wrap = on_every_item
+ij_kotlin_catch_on_new_line = false
+ij_kotlin_class_annotation_wrap = split_into_lines
+ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL
+ij_kotlin_continuation_indent_for_chained_calls = true
+ij_kotlin_continuation_indent_for_expression_bodies = true
+ij_kotlin_continuation_indent_in_argument_lists = true
+ij_kotlin_continuation_indent_in_elvis = false
+ij_kotlin_continuation_indent_in_if_conditions = false
+ij_kotlin_continuation_indent_in_parameter_lists = false
+ij_kotlin_continuation_indent_in_supertype_lists = false
+ij_kotlin_else_on_new_line = false
+ij_kotlin_enum_constants_wrap = off
+ij_kotlin_extends_list_wrap = normal
+ij_kotlin_field_annotation_wrap = split_into_lines
+ij_kotlin_finally_on_new_line = false
+ij_kotlin_if_rparen_on_new_line = false
+ij_kotlin_import_nested_classes = false
+ij_kotlin_insert_whitespaces_in_simple_one_line_method = true
+ij_kotlin_keep_blank_lines_before_right_brace = 2
+ij_kotlin_keep_blank_lines_in_code = 2
+ij_kotlin_keep_blank_lines_in_declarations = 2
+ij_kotlin_keep_first_column_comment = true
+ij_kotlin_keep_indents_on_empty_lines = false
+ij_kotlin_keep_line_breaks = true
+ij_kotlin_lbrace_on_next_line = false
+ij_kotlin_line_comment_add_space = false
+ij_kotlin_line_comment_at_first_column = true
+ij_kotlin_method_annotation_wrap = split_into_lines
+ij_kotlin_method_call_chain_wrap = normal
+ij_kotlin_method_parameters_new_line_after_left_paren = true
+ij_kotlin_method_parameters_right_paren_on_new_line = true
+ij_kotlin_method_parameters_wrap = on_every_item
+ij_kotlin_name_count_to_use_star_import = 9999
+ij_kotlin_name_count_to_use_star_import_for_members = 9999
+ij_java_names_count_to_use_import_on_demand = 9999
+ij_kotlin_parameter_annotation_wrap = off
+ij_kotlin_space_after_comma = true
+ij_kotlin_space_after_extend_colon = true
+ij_kotlin_space_after_type_colon = true
+ij_kotlin_space_before_catch_parentheses = true
+ij_kotlin_space_before_comma = false
+ij_kotlin_space_before_extend_colon = true
+ij_kotlin_space_before_for_parentheses = true
+ij_kotlin_space_before_if_parentheses = true
+ij_kotlin_space_before_lambda_arrow = true
+ij_kotlin_space_before_type_colon = false
+ij_kotlin_space_before_when_parentheses = true
+ij_kotlin_space_before_while_parentheses = true
+ij_kotlin_spaces_around_additive_operators = true
+ij_kotlin_spaces_around_assignment_operators = true
+ij_kotlin_spaces_around_equality_operators = true
+ij_kotlin_spaces_around_function_type_arrow = true
+ij_kotlin_spaces_around_logical_operators = true
+ij_kotlin_spaces_around_multiplicative_operators = true
+ij_kotlin_spaces_around_range = false
+ij_kotlin_spaces_around_relational_operators = true
+ij_kotlin_spaces_around_unary_operator = false
+ij_kotlin_spaces_around_when_arrow = true
+ij_kotlin_variable_annotation_wrap = off
+ij_kotlin_while_on_new_line = false
+ij_kotlin_wrap_elvis_expressions = 1
+ij_kotlin_wrap_expression_body_functions = 1
+ij_kotlin_wrap_first_method_in_call_chain = false
diff --git a/docs/editorconfig/.editorconfig-google b/docs/editorconfig/.editorconfig-google
new file mode 100644
index 0000000..9e62d0d
--- /dev/null
+++ b/docs/editorconfig/.editorconfig-google
@@ -0,0 +1,93 @@
+# This .editorconfig section approximates ktfmt's formatting rules. You can include it in an
+# existing .editorconfig file or use it standalone by copying it to <project root>/.editorconfig
+# and making sure your editor is set to read settings from .editorconfig files.
+#
+# It includes editor-specific config options for IntelliJ IDEA.
+#
+# If any option is wrong, PR are welcome
+
+[{*.kt,*.kts}]
+indent_style = space
+insert_final_newline = true
+max_line_length = 100
+indent_size = 2
+ij_continuation_indent_size = 2
+ij_java_names_count_to_use_import_on_demand = 9999
+ij_kotlin_align_in_columns_case_branch = false
+ij_kotlin_align_multiline_binary_operation = false
+ij_kotlin_align_multiline_extends_list = false
+ij_kotlin_align_multiline_method_parentheses = false
+ij_kotlin_align_multiline_parameters = true
+ij_kotlin_align_multiline_parameters_in_calls = false
+ij_kotlin_allow_trailing_comma = true
+ij_kotlin_allow_trailing_comma_on_call_site = true
+ij_kotlin_assignment_wrap = normal
+ij_kotlin_blank_lines_after_class_header = 0
+ij_kotlin_blank_lines_around_block_when_branches = 0
+ij_kotlin_blank_lines_before_declaration_with_comment_or_annotation_on_separate_line = 1
+ij_kotlin_block_comment_at_first_column = true
+ij_kotlin_call_parameters_new_line_after_left_paren = true
+ij_kotlin_call_parameters_right_paren_on_new_line = false
+ij_kotlin_call_parameters_wrap = on_every_item
+ij_kotlin_catch_on_new_line = false
+ij_kotlin_class_annotation_wrap = split_into_lines
+ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL
+ij_kotlin_continuation_indent_for_chained_calls = true
+ij_kotlin_continuation_indent_for_expression_bodies = true
+ij_kotlin_continuation_indent_in_argument_lists = true
+ij_kotlin_continuation_indent_in_elvis = false
+ij_kotlin_continuation_indent_in_if_conditions = false
+ij_kotlin_continuation_indent_in_parameter_lists = false
+ij_kotlin_continuation_indent_in_supertype_lists = false
+ij_kotlin_else_on_new_line = false
+ij_kotlin_enum_constants_wrap = off
+ij_kotlin_extends_list_wrap = normal
+ij_kotlin_field_annotation_wrap = split_into_lines
+ij_kotlin_finally_on_new_line = false
+ij_kotlin_if_rparen_on_new_line = false
+ij_kotlin_import_nested_classes = false
+ij_kotlin_insert_whitespaces_in_simple_one_line_method = true
+ij_kotlin_keep_blank_lines_before_right_brace = 2
+ij_kotlin_keep_blank_lines_in_code = 2
+ij_kotlin_keep_blank_lines_in_declarations = 2
+ij_kotlin_keep_first_column_comment = true
+ij_kotlin_keep_indents_on_empty_lines = false
+ij_kotlin_keep_line_breaks = true
+ij_kotlin_lbrace_on_next_line = false
+ij_kotlin_line_comment_add_space = false
+ij_kotlin_line_comment_at_first_column = true
+ij_kotlin_method_annotation_wrap = split_into_lines
+ij_kotlin_method_call_chain_wrap = normal
+ij_kotlin_method_parameters_new_line_after_left_paren = true
+ij_kotlin_method_parameters_right_paren_on_new_line = true
+ij_kotlin_method_parameters_wrap = on_every_item
+ij_kotlin_name_count_to_use_star_import = 9999
+ij_kotlin_name_count_to_use_star_import_for_members = 9999
+ij_kotlin_parameter_annotation_wrap = off
+ij_kotlin_space_after_comma = true
+ij_kotlin_space_after_extend_colon = true
+ij_kotlin_space_after_type_colon = true
+ij_kotlin_space_before_catch_parentheses = true
+ij_kotlin_space_before_comma = false
+ij_kotlin_space_before_extend_colon = true
+ij_kotlin_space_before_for_parentheses = true
+ij_kotlin_space_before_if_parentheses = true
+ij_kotlin_space_before_lambda_arrow = true
+ij_kotlin_space_before_type_colon = false
+ij_kotlin_space_before_when_parentheses = true
+ij_kotlin_space_before_while_parentheses = true
+ij_kotlin_spaces_around_additive_operators = true
+ij_kotlin_spaces_around_assignment_operators = true
+ij_kotlin_spaces_around_equality_operators = true
+ij_kotlin_spaces_around_function_type_arrow = true
+ij_kotlin_spaces_around_logical_operators = true
+ij_kotlin_spaces_around_multiplicative_operators = true
+ij_kotlin_spaces_around_range = false
+ij_kotlin_spaces_around_relational_operators = true
+ij_kotlin_spaces_around_unary_operator = false
+ij_kotlin_spaces_around_when_arrow = true
+ij_kotlin_variable_annotation_wrap = off
+ij_kotlin_while_on_new_line = false
+ij_kotlin_wrap_elvis_expressions = 1
+ij_kotlin_wrap_expression_body_functions = 1
+ij_kotlin_wrap_first_method_in_call_chain = false
diff --git a/docs/editorconfig/.editorconfig-kotlinlang b/docs/editorconfig/.editorconfig-kotlinlang
new file mode 100644
index 0000000..2f55867
--- /dev/null
+++ b/docs/editorconfig/.editorconfig-kotlinlang
@@ -0,0 +1,93 @@
+# This .editorconfig section approximates ktfmt's formatting rules. You can include it in an
+# existing .editorconfig file or use it standalone by copying it to <project root>/.editorconfig
+# and making sure your editor is set to read settings from .editorconfig files.
+#
+# It includes editor-specific config options for IntelliJ IDEA.
+#
+# If any option is wrong, PR are welcome
+
+[{*.kt,*.kts}]
+indent_style = space
+insert_final_newline = true
+max_line_length = 100
+indent_size = 4
+ij_continuation_indent_size = 4
+ij_java_names_count_to_use_import_on_demand = 9999
+ij_kotlin_align_in_columns_case_branch = false
+ij_kotlin_align_multiline_binary_operation = false
+ij_kotlin_align_multiline_extends_list = false
+ij_kotlin_align_multiline_method_parentheses = false
+ij_kotlin_align_multiline_parameters = true
+ij_kotlin_align_multiline_parameters_in_calls = false
+ij_kotlin_allow_trailing_comma = true
+ij_kotlin_allow_trailing_comma_on_call_site = true
+ij_kotlin_assignment_wrap = normal
+ij_kotlin_blank_lines_after_class_header = 0
+ij_kotlin_blank_lines_around_block_when_branches = 0
+ij_kotlin_blank_lines_before_declaration_with_comment_or_annotation_on_separate_line = 1
+ij_kotlin_block_comment_at_first_column = true
+ij_kotlin_call_parameters_new_line_after_left_paren = true
+ij_kotlin_call_parameters_right_paren_on_new_line = false
+ij_kotlin_call_parameters_wrap = on_every_item
+ij_kotlin_catch_on_new_line = false
+ij_kotlin_class_annotation_wrap = split_into_lines
+ij_kotlin_code_style_defaults = KOTLIN_OFFICIAL
+ij_kotlin_continuation_indent_for_chained_calls = true
+ij_kotlin_continuation_indent_for_expression_bodies = true
+ij_kotlin_continuation_indent_in_argument_lists = true
+ij_kotlin_continuation_indent_in_elvis = false
+ij_kotlin_continuation_indent_in_if_conditions = false
+ij_kotlin_continuation_indent_in_parameter_lists = false
+ij_kotlin_continuation_indent_in_supertype_lists = false
+ij_kotlin_else_on_new_line = false
+ij_kotlin_enum_constants_wrap = off
+ij_kotlin_extends_list_wrap = normal
+ij_kotlin_field_annotation_wrap = split_into_lines
+ij_kotlin_finally_on_new_line = false
+ij_kotlin_if_rparen_on_new_line = false
+ij_kotlin_import_nested_classes = false
+ij_kotlin_insert_whitespaces_in_simple_one_line_method = true
+ij_kotlin_keep_blank_lines_before_right_brace = 2
+ij_kotlin_keep_blank_lines_in_code = 2
+ij_kotlin_keep_blank_lines_in_declarations = 2
+ij_kotlin_keep_first_column_comment = true
+ij_kotlin_keep_indents_on_empty_lines = false
+ij_kotlin_keep_line_breaks = true
+ij_kotlin_lbrace_on_next_line = false
+ij_kotlin_line_comment_add_space = false
+ij_kotlin_line_comment_at_first_column = true
+ij_kotlin_method_annotation_wrap = split_into_lines
+ij_kotlin_method_call_chain_wrap = normal
+ij_kotlin_method_parameters_new_line_after_left_paren = true
+ij_kotlin_method_parameters_right_paren_on_new_line = true
+ij_kotlin_method_parameters_wrap = on_every_item
+ij_kotlin_name_count_to_use_star_import = 9999
+ij_kotlin_name_count_to_use_star_import_for_members = 9999
+ij_kotlin_parameter_annotation_wrap = off
+ij_kotlin_space_after_comma = true
+ij_kotlin_space_after_extend_colon = true
+ij_kotlin_space_after_type_colon = true
+ij_kotlin_space_before_catch_parentheses = true
+ij_kotlin_space_before_comma = false
+ij_kotlin_space_before_extend_colon = true
+ij_kotlin_space_before_for_parentheses = true
+ij_kotlin_space_before_if_parentheses = true
+ij_kotlin_space_before_lambda_arrow = true
+ij_kotlin_space_before_type_colon = false
+ij_kotlin_space_before_when_parentheses = true
+ij_kotlin_space_before_while_parentheses = true
+ij_kotlin_spaces_around_additive_operators = true
+ij_kotlin_spaces_around_assignment_operators = true
+ij_kotlin_spaces_around_equality_operators = true
+ij_kotlin_spaces_around_function_type_arrow = true
+ij_kotlin_spaces_around_logical_operators = true
+ij_kotlin_spaces_around_multiplicative_operators = true
+ij_kotlin_spaces_around_range = false
+ij_kotlin_spaces_around_relational_operators = true
+ij_kotlin_spaces_around_unary_operator = false
+ij_kotlin_spaces_around_when_arrow = true
+ij_kotlin_variable_annotation_wrap = off
+ij_kotlin_while_on_new_line = false
+ij_kotlin_wrap_elvis_expressions = 1
+ij_kotlin_wrap_expression_body_functions = 1
+ij_kotlin_wrap_first_method_in_call_chain = false
diff --git a/docs/images/before.png b/docs/images/before.png
new file mode 100644
index 0000000..c545a44
--- /dev/null
+++ b/docs/images/before.png
Binary files differ
diff --git a/docs/images/intellij.png b/docs/images/intellij.png
new file mode 100644
index 0000000..c2ac597
--- /dev/null
+++ b/docs/images/intellij.png
Binary files differ
diff --git a/docs/images/ktfmt.png b/docs/images/ktfmt.png
new file mode 100644
index 0000000..79ed3f7
--- /dev/null
+++ b/docs/images/ktfmt.png
Binary files differ
diff --git a/docs/images/ktlint.png b/docs/images/ktlint.png
new file mode 100644
index 0000000..9c70b83
--- /dev/null
+++ b/docs/images/ktlint.png
Binary files differ
diff --git a/generate_includes_file.py b/generate_includes_file.py
new file mode 100755
index 0000000..be9ebff
--- /dev/null
+++ b/generate_includes_file.py
@@ -0,0 +1,95 @@
+#!/usr/bin/env python3
+
+#
+# Copyright 2022, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""Script to generate an include file that can be passed to ktfmt.py.
+
+The include file is generated from one or more folders containing one or more
+Kotlin files. The generated include file will exclude all Kotlin files that
+currently don't follow ktfmt format. This way, we can easily start formatting
+all new files in a project/directory.
+"""
+
+import argparse
+import os
+import subprocess
+import sys
+
+
+def main():
+  parser = argparse.ArgumentParser(
+      'Generate an include file that can be passed to ktfmt.py')
+  parser.add_argument(
+      '--output', '-o', required=True, help='The output include file.')
+  parser.add_argument(
+      'files',
+      nargs='*',
+      help='The files or directories that should be included.')
+  args = parser.parse_args()
+
+  # Add a line preprended with '+' for included files/folders.
+  with open(args.output, 'w+') as out:
+    includes = args.files
+    includes.sort()
+    for include in includes:
+      out.write('+' + include + '\n')
+
+    # Retrieve all Kotlin files.
+    kt_files = []
+    for file in args.files:
+      if os.path.isdir(file):
+        for root, dirs, files in os.walk(file):
+          for f in files:
+            if is_kotlin_file(f):
+              kt_files += [os.path.join(root, f)]
+
+      if is_kotlin_file(file):
+        kt_files += [file]
+
+    # Check all files with ktfmt.
+    ktfmt_args = ['--kotlinlang-style', '--set-exit-if-changed', '--dry-run'
+                 ] + kt_files
+    dir = os.path.dirname(__file__)
+    ktfmt_jar = os.path.normpath(
+        os.path.join(dir,
+                     '../../prebuilts/build-tools/common/framework/ktfmt.jar'))
+
+    ktlint_env = os.environ.copy()
+    ktlint_env['JAVA_CMD'] = 'java'
+    try:
+      process = subprocess.Popen(
+          ['java', '-jar', ktfmt_jar] + ktfmt_args,
+          stdout=subprocess.PIPE,
+          env=ktlint_env)
+
+      # Add a line prepended with '-' for all files that are not correctly.
+      # formatted.
+      stdout, _ = process.communicate()
+      incorrect_files = stdout.decode('utf-8').splitlines()
+      incorrect_files.sort()
+      for file in incorrect_files:
+        out.write('-' + file + '\n')
+    except OSError as e:
+      print('Error running ktfmt')
+      sys.exit(1)
+
+
+def is_kotlin_file(name):
+  return name.endswith('.kt') or name.endswith('.kts')
+
+
+if __name__ == '__main__':
+  main()
diff --git a/ktfmt.py b/ktfmt.py
new file mode 100755
index 0000000..6f115f7
--- /dev/null
+++ b/ktfmt.py
@@ -0,0 +1,138 @@
+#!/usr/bin/env python3
+
+#
+# Copyright 2022, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""Script to format or check Kotlin files."""
+
+import argparse
+import os
+import subprocess
+import sys
+
+
+def main():
+  parser = argparse.ArgumentParser(
+      'Format Kotlin files or check that they are correctly formatted.')
+  parser.add_argument(
+      '--check',
+      '-c',
+      action='store_true',
+      default=False,
+      help='Perform a format check instead of formatting.')
+  parser.add_argument(
+      '--includes_file',
+      '-i',
+      default='',
+      help='The file containing the Kotlin files and directories that should be included/excluded, generated using generate_includes_file.py.'
+  )
+  parser.add_argument(
+      'files',
+      nargs='*',
+      help='The files to format or check. If --include_file is specified, only the files at their intersection will be formatted/checked.'
+  )
+  args = parser.parse_args()
+
+  ktfmt_args = ['--kotlinlang-style']
+
+  check = args.check
+  if check:
+    ktfmt_args += ['--set-exit-if-changed', '--dry-run']
+
+  kt_files = []
+  for file in args.files:
+    if os.path.isdir(file):
+      for root, dirs, files in os.walk(file):
+        for f in files:
+          if is_kotlin_file(f):
+            kt_files += [os.path.join(root, f)]
+
+    if is_kotlin_file(file):
+      kt_files += [file]
+
+  # Only format/check files from the includes list.
+  includes_file = args.includes_file
+  if kt_files and includes_file:
+    f = open(includes_file, 'r')
+
+    lines = f.read().splitlines()
+    included = [line[1:] for line in lines if line.startswith('+')]
+    included_files = set()
+    included_dirs = []
+    for line in included:
+      if is_kotlin_file(line):
+        included_files.add(line)
+      else:
+        included_dirs += [line]
+
+    excluded_files = [line[1:] for line in lines if line.startswith('-')]
+
+    kt_files = [
+        kt_file for kt_file in kt_files if kt_file not in excluded_files and
+        (kt_file in included_files or is_included(kt_file, included_dirs))
+    ]
+
+  # No need to start ktfmt if there are no files to check/format.
+  if not kt_files:
+    sys.exit(0)
+
+  ktfmt_args += kt_files
+
+  dir = os.path.normpath(os.path.dirname(__file__))
+  ktfmt_jar = os.path.join(
+      dir, '../../prebuilts/build-tools/common/framework/ktfmt.jar')
+
+  ktlint_env = os.environ.copy()
+  ktlint_env['JAVA_CMD'] = 'java'
+  try:
+    process = subprocess.Popen(
+        ['java', '-jar', ktfmt_jar] + ktfmt_args,
+        stdout=subprocess.PIPE,
+        env=ktlint_env)
+    stdout, _ = process.communicate()
+    code = process.returncode
+    if check and code != 0:
+      print(
+          '**********************************************************************'
+      )
+      print(
+          'Some Kotlin files are not properly formatted. Run the following command to format them:\n\n'
+      )
+      script_path = os.path.normpath(__file__)
+      incorrect_files = stdout.decode('utf-8').splitlines()
+      print('$ ' + script_path + ' ' + ' '.join(incorrect_files) + '\n')
+      print(
+          '**********************************************************************'
+      )
+      sys.exit(code)
+    else:
+      sys.exit(0)
+  except OSError as e:
+    print('Error running ktfmt')
+    sys.exit(1)
+
+
+def is_kotlin_file(name):
+  return name.endswith('.kt') or name.endswith('.kts')
+
+
+def is_included(file, dirs):
+  for dir in dirs:
+    if file.startswith(dir):
+      return True
+
+
+if __name__ == '__main__':
+  main()
diff --git a/ktfmt_idea_plugin/DEVELOPING.md b/ktfmt_idea_plugin/DEVELOPING.md
new file mode 100644
index 0000000..de64dcb
--- /dev/null
+++ b/ktfmt_idea_plugin/DEVELOPING.md
@@ -0,0 +1,24 @@
+## Meaning of Versions
+
+Versions are defined in `build.gradle.kts`.
+
+`intellij.version` is used for -
+1. Building the plugin, so the source code needs to be compatible with it.
+2. Determining the _maximum_ supported version. This means that it needs to be updated every time a new IntelliJ _Branch_ version is released. For example, 2021.1, 2021.2, etc.
+
+`tasks.patchPluginXml.sinceBuild` is used for -
+1. Determinig the _minimum_ supported version, so the code must not reference classes not introduced yet.
+
+
+## Testing
+
+* Use Gradle to run the plugin.
+* _Don't_ use IntelliJ to run the plugin.
+
+```
+ktfmt/ktfmt_idea_plugin $ ./gradlew runIde
+```
+
+Point `intellij.alternativeIdePath` in `build.gradle.kts` to a local installation of IntelliJ / Android Studio to test the plugin with it. Don't forget to enable the plugin. Easiest way: Cmd-shift-A, type 'ktfmt', look for Preferences.
+
+Another way: change `intellij.version` and comment out `alternativeIdePath`. This will run the plugin on whatever version you put - it'll be downloaded through Gradle.
diff --git a/ktfmt_idea_plugin/README.md b/ktfmt_idea_plugin/README.md
new file mode 100644
index 0000000..2e44397
--- /dev/null
+++ b/ktfmt_idea_plugin/README.md
@@ -0,0 +1,9 @@
+## Tested with:
+
+Android Studio 4.1.1 (build 201)
+IntelliJ 2020.2 (build 202)
+IntelliJ 2020.3 (build 203)
+
+## Doesn't work with:
+
+Android Studio 4.0 (build 193) (requires large changes to the plugin code)
diff --git a/ktfmt_idea_plugin/build.gradle.kts b/ktfmt_idea_plugin/build.gradle.kts
new file mode 100644
index 0000000..63c7879
--- /dev/null
+++ b/ktfmt_idea_plugin/build.gradle.kts
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+plugins {
+  id("org.jetbrains.intellij") version "0.7.2"
+  java
+  id("com.diffplug.spotless") version "5.10.2"
+}
+
+val ktfmtVersion = rootProject.file("../version.txt").readText().trim()
+val pluginVersion = "1.1"
+
+group = "com.facebook"
+
+version = "$pluginVersion.$ktfmtVersion"
+
+repositories {
+  mavenCentral()
+  mavenLocal()
+}
+
+java {
+  sourceCompatibility = JavaVersion.VERSION_11
+  targetCompatibility = JavaVersion.VERSION_11
+}
+
+dependencies {
+  implementation("com.facebook", "ktfmt", ktfmtVersion)
+  implementation("com.google.googlejavaformat", "google-java-format", "1.8")
+}
+
+// See https://github.com/JetBrains/gradle-intellij-plugin/
+intellij {
+  // Version with which to build (and run; unless alternativeIdePath is specified)
+  version = "2020.3"
+  // To run on a different IDE, uncomment and specify a path.
+  // alternativeIdePath = "/Applications/Android Studio.app"
+}
+
+tasks {
+  patchPluginXml {
+    sinceBuild("201")
+    untilBuild("")
+  }
+  publishPlugin { token(System.getenv("JETBRAINS_MARKETPLACE_TOKEN")) }
+  runPluginVerifier { ideVersions(listOf("211.6432.7")) }
+}
+
+spotless { java { googleJavaFormat() } }
diff --git a/ktfmt_idea_plugin/gradle.properties b/ktfmt_idea_plugin/gradle.properties
new file mode 100644
index 0000000..29e08e8
--- /dev/null
+++ b/ktfmt_idea_plugin/gradle.properties
@@ -0,0 +1 @@
+kotlin.code.style=official
\ No newline at end of file
diff --git a/ktfmt_idea_plugin/gradle/wrapper/gradle-wrapper.jar b/ktfmt_idea_plugin/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..62d4c05
--- /dev/null
+++ b/ktfmt_idea_plugin/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/ktfmt_idea_plugin/gradle/wrapper/gradle-wrapper.properties b/ktfmt_idea_plugin/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..622ab64
--- /dev/null
+++ b/ktfmt_idea_plugin/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/ktfmt_idea_plugin/gradlew b/ktfmt_idea_plugin/gradlew
new file mode 100755
index 0000000..fbd7c51
--- /dev/null
+++ b/ktfmt_idea_plugin/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+    echo "$*"
+}
+
+die () {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+  NONSTOP* )
+    nonstop=true
+    ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+    
+    JAVACMD=`cygpath --unix "$JAVACMD"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=`expr $i + 1`
+    done
+    case $i in
+        0) set -- ;;
+        1) set -- "$args0" ;;
+        2) set -- "$args0" "$args1" ;;
+        3) set -- "$args0" "$args1" "$args2" ;;
+        4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Escape application args
+save () {
+    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+    echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/ktfmt_idea_plugin/gradlew.bat b/ktfmt_idea_plugin/gradlew.bat
new file mode 100644
index 0000000..a9f778a
--- /dev/null
+++ b/ktfmt_idea_plugin/gradlew.bat
@@ -0,0 +1,104 @@
+@rem

+@rem Copyright 2015 the original author or authors.

+@rem

+@rem Licensed under the Apache License, Version 2.0 (the "License");

+@rem you may not use this file except in compliance with the License.

+@rem You may obtain a copy of the License at

+@rem

+@rem      https://www.apache.org/licenses/LICENSE-2.0

+@rem

+@rem Unless required by applicable law or agreed to in writing, software

+@rem distributed under the License is distributed on an "AS IS" BASIS,

+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+@rem See the License for the specific language governing permissions and

+@rem limitations under the License.

+@rem

+

+@if "%DEBUG%" == "" @echo off

+@rem ##########################################################################

+@rem

+@rem  Gradle startup script for Windows

+@rem

+@rem ##########################################################################

+

+@rem Set local scope for the variables with windows NT shell

+if "%OS%"=="Windows_NT" setlocal

+

+set DIRNAME=%~dp0

+if "%DIRNAME%" == "" set DIRNAME=.

+set APP_BASE_NAME=%~n0

+set APP_HOME=%DIRNAME%

+

+@rem Resolve any "." and ".." in APP_HOME to make it shorter.

+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi

+

+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.

+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"

+

+@rem Find java.exe

+if defined JAVA_HOME goto findJavaFromJavaHome

+

+set JAVA_EXE=java.exe

+%JAVA_EXE% -version >NUL 2>&1

+if "%ERRORLEVEL%" == "0" goto init

+

+echo.

+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

+echo.

+echo Please set the JAVA_HOME variable in your environment to match the

+echo location of your Java installation.

+

+goto fail

+

+:findJavaFromJavaHome

+set JAVA_HOME=%JAVA_HOME:"=%

+set JAVA_EXE=%JAVA_HOME%/bin/java.exe

+

+if exist "%JAVA_EXE%" goto init

+

+echo.

+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%

+echo.

+echo Please set the JAVA_HOME variable in your environment to match the

+echo location of your Java installation.

+

+goto fail

+

+:init

+@rem Get command-line arguments, handling Windows variants

+

+if not "%OS%" == "Windows_NT" goto win9xME_args

+

+:win9xME_args

+@rem Slurp the command line arguments.

+set CMD_LINE_ARGS=

+set _SKIP=2

+

+:win9xME_args_slurp

+if "x%~1" == "x" goto execute

+

+set CMD_LINE_ARGS=%*

+

+:execute

+@rem Setup the command line

+

+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar

+

+

+@rem Execute Gradle

+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%

+

+:end

+@rem End local scope for the variables with windows NT shell

+if "%ERRORLEVEL%"=="0" goto mainEnd

+

+:fail

+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of

+rem the _cmd.exe /c_ return code!

+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1

+exit /b 1

+

+:mainEnd

+if "%OS%"=="Windows_NT" endlocal

+

+:omega

diff --git a/ktfmt_idea_plugin/settings.gradle.kts b/ktfmt_idea_plugin/settings.gradle.kts
new file mode 100644
index 0000000..8deb7fc
--- /dev/null
+++ b/ktfmt_idea_plugin/settings.gradle.kts
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+rootProject.name = "ktfmt_idea_plugin"
diff --git a/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/CodeStyleManagerDecorator.java b/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/CodeStyleManagerDecorator.java
new file mode 100644
index 0000000..61de704
--- /dev/null
+++ b/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/CodeStyleManagerDecorator.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.facebook.ktfmt.intellij;
+
+import com.intellij.formatting.FormattingMode;
+import com.intellij.lang.ASTNode;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.codeStyle.ChangedRangesInfo;
+import com.intellij.psi.codeStyle.CodeStyleManager;
+import com.intellij.psi.codeStyle.DocCommentSettings;
+import com.intellij.psi.codeStyle.FormattingModeAwareIndentAdjuster;
+import com.intellij.psi.codeStyle.Indent;
+import com.intellij.util.IncorrectOperationException;
+import com.intellij.util.ThrowableRunnable;
+import java.util.Collection;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Decorates the {@link CodeStyleManager} abstract class by delegating to a concrete implementation
+ * instance (likely IJ's default instance).
+ */
+@SuppressWarnings("deprecation")
+class CodeStyleManagerDecorator extends CodeStyleManager
+    implements FormattingModeAwareIndentAdjuster {
+
+  private final CodeStyleManager delegate;
+
+  CodeStyleManagerDecorator(CodeStyleManager delegate) {
+    this.delegate = delegate;
+  }
+
+  CodeStyleManager getDelegate() {
+    return delegate;
+  }
+
+  @Override
+  public Project getProject() {
+    return delegate.getProject();
+  }
+
+  @Override
+  public PsiElement reformat(PsiElement element) throws IncorrectOperationException {
+    return delegate.reformat(element);
+  }
+
+  @Override
+  public PsiElement reformat(PsiElement element, boolean canChangeWhiteSpacesOnly)
+      throws IncorrectOperationException {
+    return delegate.reformat(element, canChangeWhiteSpacesOnly);
+  }
+
+  @Override
+  public PsiElement reformatRange(PsiElement element, int startOffset, int endOffset)
+      throws IncorrectOperationException {
+    return delegate.reformatRange(element, startOffset, endOffset);
+  }
+
+  @Override
+  public PsiElement reformatRange(
+      PsiElement element, int startOffset, int endOffset, boolean canChangeWhiteSpacesOnly)
+      throws IncorrectOperationException {
+    return delegate.reformatRange(element, startOffset, endOffset, canChangeWhiteSpacesOnly);
+  }
+
+  @Override
+  public void reformatText(PsiFile file, int startOffset, int endOffset)
+      throws IncorrectOperationException {
+    delegate.reformatText(file, startOffset, endOffset);
+  }
+
+  @Override
+  public void reformatText(PsiFile file, Collection<TextRange> ranges)
+      throws IncorrectOperationException {
+    delegate.reformatText(file, ranges);
+  }
+
+  @Override
+  public void reformatTextWithContext(PsiFile psiFile, ChangedRangesInfo changedRangesInfo)
+      throws IncorrectOperationException {
+    delegate.reformatTextWithContext(psiFile, changedRangesInfo);
+  }
+
+  @Override
+  public void reformatTextWithContext(PsiFile file, Collection<TextRange> ranges)
+      throws IncorrectOperationException {
+    delegate.reformatTextWithContext(file, ranges);
+  }
+
+  @Override
+  public void adjustLineIndent(PsiFile file, TextRange rangeToAdjust)
+      throws IncorrectOperationException {
+    delegate.adjustLineIndent(file, rangeToAdjust);
+  }
+
+  @Override
+  public int adjustLineIndent(PsiFile file, int offset) throws IncorrectOperationException {
+    return delegate.adjustLineIndent(file, offset);
+  }
+
+  @Override
+  public int adjustLineIndent(Document document, int offset) {
+    return delegate.adjustLineIndent(document, offset);
+  }
+
+  public void scheduleIndentAdjustment(Document document, int offset) {
+    delegate.scheduleIndentAdjustment(document, offset);
+  }
+
+  @Override
+  public boolean isLineToBeIndented(PsiFile file, int offset) {
+    return delegate.isLineToBeIndented(file, offset);
+  }
+
+  @Override
+  @Nullable
+  public String getLineIndent(PsiFile file, int offset) {
+    return delegate.getLineIndent(file, offset);
+  }
+
+  @Override
+  @Nullable
+  public String getLineIndent(PsiFile file, int offset, FormattingMode mode) {
+    return delegate.getLineIndent(file, offset, mode);
+  }
+
+  @Override
+  @Nullable
+  public String getLineIndent(Document document, int offset) {
+    return delegate.getLineIndent(document, offset);
+  }
+
+  @Override
+  public Indent getIndent(String text, FileType fileType) {
+    return delegate.getIndent(text, fileType);
+  }
+
+  @Override
+  public String fillIndent(Indent indent, FileType fileType) {
+    return delegate.fillIndent(indent, fileType);
+  }
+
+  @Override
+  public Indent zeroIndent() {
+    return delegate.zeroIndent();
+  }
+
+  @Override
+  public void reformatNewlyAddedElement(ASTNode block, ASTNode addedElement)
+      throws IncorrectOperationException {
+    delegate.reformatNewlyAddedElement(block, addedElement);
+  }
+
+  @Override
+  public boolean isSequentialProcessingAllowed() {
+    return delegate.isSequentialProcessingAllowed();
+  }
+
+  @Override
+  public void performActionWithFormatterDisabled(Runnable r) {
+    delegate.performActionWithFormatterDisabled(r);
+  }
+
+  @Override
+  public <T extends Throwable> void performActionWithFormatterDisabled(ThrowableRunnable<T> r)
+      throws T {
+    delegate.performActionWithFormatterDisabled(r);
+  }
+
+  @Override
+  public <T> T performActionWithFormatterDisabled(Computable<T> r) {
+    return delegate.performActionWithFormatterDisabled(r);
+  }
+
+  @Override
+  public int getSpacing(PsiFile file, int offset) {
+    return delegate.getSpacing(file, offset);
+  }
+
+  @Override
+  public int getMinLineFeeds(PsiFile file, int offset) {
+    return delegate.getMinLineFeeds(file, offset);
+  }
+
+  @Override
+  public void runWithDocCommentFormattingDisabled(PsiFile file, Runnable runnable) {
+    delegate.runWithDocCommentFormattingDisabled(file, runnable);
+  }
+
+  @Override
+  public DocCommentSettings getDocCommentSettings(PsiFile file) {
+    return delegate.getDocCommentSettings(file);
+  }
+
+  // From FormattingModeAwareIndentAdjuster
+
+  /** Uses same fallback as {@link CodeStyleManager#getCurrentFormattingMode}. */
+  @Override
+  public FormattingMode getCurrentFormattingMode() {
+    if (delegate instanceof FormattingModeAwareIndentAdjuster) {
+      return ((FormattingModeAwareIndentAdjuster) delegate).getCurrentFormattingMode();
+    }
+    return FormattingMode.REFORMAT;
+  }
+
+  @Override
+  public int adjustLineIndent(final Document document, final int offset, FormattingMode mode)
+      throws IncorrectOperationException {
+    if (delegate instanceof FormattingModeAwareIndentAdjuster) {
+      return ((FormattingModeAwareIndentAdjuster) delegate)
+          .adjustLineIndent(document, offset, mode);
+    }
+    return offset;
+  }
+
+  @Override
+  public void scheduleReformatWhenSettingsComputed(PsiFile file) {
+    delegate.scheduleReformatWhenSettingsComputed(file);
+  }
+}
diff --git a/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/FormatterUtil.java b/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/FormatterUtil.java
new file mode 100644
index 0000000..c67f114
--- /dev/null
+++ b/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/FormatterUtil.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.facebook.ktfmt.intellij;
+
+import static com.facebook.ktfmt.format.Formatter.format;
+
+import com.facebook.ktfmt.format.ParseError;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableMap;
+import com.google.googlejavaformat.java.FormatterException;
+import com.intellij.openapi.util.TextRange;
+import java.util.Map;
+
+final class FormatterUtil {
+
+  private FormatterUtil() {}
+
+  /**
+   * Formats 'code' using ktfmt.
+   *
+   * @return formatted code
+   */
+  static Map<TextRange, String> getReplacements(UiFormatterStyle uiFormatterStyle, String code) {
+    try {
+      return ImmutableMap.of(TextRange.allOf(code), formatCode(uiFormatterStyle, code));
+    } catch (FormatterException | ParseError e) {
+      return ImmutableMap.of();
+    }
+  }
+
+  @VisibleForTesting
+  static String formatCode(UiFormatterStyle uiFormatterStyle, String code)
+      throws FormatterException {
+
+    return format(uiFormatterStyle.getFormattingOptions(), code);
+  }
+}
diff --git a/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/InitialConfigurationProjectManagerListener.java b/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/InitialConfigurationProjectManagerListener.java
new file mode 100644
index 0000000..2bc9775
--- /dev/null
+++ b/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/InitialConfigurationProjectManagerListener.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.facebook.ktfmt.intellij;
+
+import com.intellij.notification.Notification;
+import com.intellij.notification.NotificationDisplayType;
+import com.intellij.notification.NotificationGroup;
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectManagerListener;
+import org.jetbrains.annotations.NotNull;
+
+final class InitialConfigurationProjectManagerListener implements ProjectManagerListener {
+
+  private static final String NOTIFICATION_TITLE = "Enable ktfmt";
+  private static final NotificationGroup NOTIFICATION_GROUP =
+      new NotificationGroup(NOTIFICATION_TITLE, NotificationDisplayType.STICKY_BALLOON, true);
+
+  @Override
+  public void projectOpened(@NotNull Project project) {
+
+    KtfmtSettings settings = KtfmtSettings.getInstance(project);
+
+    if (settings.isUninitialized()) {
+      settings.setEnabled(false);
+      displayNewUserNotification(project, settings);
+    }
+  }
+
+  private void displayNewUserNotification(Project project, KtfmtSettings settings) {
+    Notification notification =
+        new Notification(
+            NOTIFICATION_GROUP.getDisplayId(),
+            NOTIFICATION_TITLE,
+            "The ktfmt plugin is disabled by default. "
+                + "<a href=\"enable\">Enable for this project</a>.",
+            NotificationType.INFORMATION,
+            (n, e) -> {
+              settings.setEnabled(true);
+              n.expire();
+            });
+    notification.notify(project);
+  }
+}
diff --git a/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/KtfmtCodeStyleManager.java b/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/KtfmtCodeStyleManager.java
new file mode 100644
index 0000000..d2184ea
--- /dev/null
+++ b/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/KtfmtCodeStyleManager.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2015 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.facebook.ktfmt.intellij;
+
+import static java.util.Comparator.comparing;
+
+import com.google.common.collect.ImmutableList;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.command.WriteCommandAction;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.codeStyle.CodeStyleManager;
+import com.intellij.psi.impl.CheckUtil;
+import com.intellij.util.IncorrectOperationException;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.TreeMap;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.kotlin.idea.KotlinFileType;
+
+/**
+ * A {@link CodeStyleManager} implementation which formats .kt files with ktfmt. Formatting of all
+ * other types of files is delegated to IJ's default implementation.
+ */
+class KtfmtCodeStyleManager extends CodeStyleManagerDecorator {
+
+  public KtfmtCodeStyleManager(@NotNull CodeStyleManager original) {
+    super(original);
+  }
+
+  @Override
+  public void reformatText(PsiFile file, int startOffset, int endOffset)
+      throws IncorrectOperationException {
+    if (overrideFormatterForFile(file)) {
+      formatInternal(file, ImmutableList.of(new TextRange(startOffset, endOffset)));
+    } else {
+      super.reformatText(file, startOffset, endOffset);
+    }
+  }
+
+  @Override
+  public void reformatText(PsiFile file, Collection<TextRange> ranges)
+      throws IncorrectOperationException {
+    if (overrideFormatterForFile(file)) {
+      formatInternal(file, ranges);
+    } else {
+      super.reformatText(file, ranges);
+    }
+  }
+
+  @Override
+  public void reformatTextWithContext(PsiFile file, Collection<TextRange> ranges) {
+    if (overrideFormatterForFile(file)) {
+      formatInternal(file, ranges);
+    } else {
+      super.reformatTextWithContext(file, ranges);
+    }
+  }
+
+  @Override
+  public PsiElement reformatRange(
+      PsiElement element, int startOffset, int endOffset, boolean canChangeWhiteSpacesOnly) {
+    // Only handle elements that are PsiFile for now -- otherwise we need to search
+    // for some
+    // element within the file at new locations given the original startOffset and
+    // endOffsets
+    // to serve as the return value.
+    PsiFile file = element instanceof PsiFile ? (PsiFile) element : null;
+    if (file != null && canChangeWhiteSpacesOnly && overrideFormatterForFile(file)) {
+      formatInternal(file, ImmutableList.of(new TextRange(startOffset, endOffset)));
+      return file;
+    } else {
+      return super.reformatRange(element, startOffset, endOffset, canChangeWhiteSpacesOnly);
+    }
+  }
+
+  /** Return whether or not this formatter can handle formatting the given file. */
+  private boolean overrideFormatterForFile(PsiFile file) {
+    return KotlinFileType.INSTANCE.getName().equals(file.getFileType().getName())
+        && KtfmtSettings.getInstance(getProject()).isEnabled();
+  }
+
+  private void formatInternal(PsiFile file, Collection<TextRange> ranges) {
+    ApplicationManager.getApplication().assertWriteAccessAllowed();
+    PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject());
+    documentManager.commitAllDocuments();
+    CheckUtil.checkWritable(file);
+
+    Document document = documentManager.getDocument(file);
+
+    if (document == null) {
+      return;
+    }
+    // If there are postponed PSI changes (e.g., during a refactoring), just abort.
+    // If we apply them now, then the incoming text ranges may no longer be valid.
+    if (documentManager.isDocumentBlockedByPsi(document)) {
+      return;
+    }
+
+    format(document, ranges);
+  }
+
+  /**
+   * Format the ranges of the given document.
+   *
+   * <p>Overriding methods will need to modify the document with the result of the external
+   * formatter (usually using {@link #performReplacements(Document, Map)}.
+   */
+  private void format(Document document, Collection<TextRange> ranges) {
+    UiFormatterStyle uiFormatterStyle =
+        KtfmtSettings.getInstance(getProject()).getUiFormatterStyle();
+
+    performReplacements(
+        document, FormatterUtil.getReplacements(uiFormatterStyle, document.getText()));
+  }
+
+  private void performReplacements(
+      final Document document, final Map<TextRange, String> replacements) {
+
+    if (replacements.isEmpty()) {
+      return;
+    }
+
+    TreeMap<TextRange, String> sorted = new TreeMap<>(comparing(TextRange::getStartOffset));
+    sorted.putAll(replacements);
+    WriteCommandAction.runWriteCommandAction(
+        getProject(),
+        () -> {
+          for (Entry<TextRange, String> entry : sorted.descendingMap().entrySet()) {
+            document.replaceString(
+                entry.getKey().getStartOffset(), entry.getKey().getEndOffset(), entry.getValue());
+          }
+          PsiDocumentManager.getInstance(getProject()).commitDocument(document);
+        });
+  }
+}
diff --git a/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/KtfmtConfigurable.form b/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/KtfmtConfigurable.form
new file mode 100644
index 0000000..e515398
--- /dev/null
+++ b/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/KtfmtConfigurable.form
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.facebook.ktfmt.intellij.KtfmtConfigurable">
+  <grid id="27dc6" binding="panel" layout-manager="GridLayoutManager" row-count="3" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
+    <margin top="0" left="0" bottom="0" right="0"/>
+    <constraints>
+      <xy x="20" y="20" width="500" height="400"/>
+    </constraints>
+    <properties/>
+    <border type="none"/>
+    <children>
+      <component id="4a87f" class="javax.swing.JCheckBox" binding="enable" default-binding="true">
+        <constraints>
+          <grid row="0" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties>
+          <text value="Enable ktfmt"/>
+        </properties>
+      </component>
+      <vspacer id="19e83">
+        <constraints>
+          <grid row="2" column="0" row-span="1" col-span="2" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
+        </constraints>
+      </vspacer>
+      <component id="c93e1" class="javax.swing.JLabel">
+        <constraints>
+          <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
+        </constraints>
+        <properties>
+          <text value="Code style"/>
+        </properties>
+      </component>
+      <component id="31761" class="javax.swing.JComboBox" binding="styleComboBox" custom-create="true">
+        <constraints>
+          <grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="2" anchor="8" fill="1" indent="1" use-parent-layout="false"/>
+        </constraints>
+        <properties/>
+      </component>
+    </children>
+  </grid>
+</form>
diff --git a/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/KtfmtConfigurable.java b/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/KtfmtConfigurable.java
new file mode 100644
index 0000000..ac82917
--- /dev/null
+++ b/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/KtfmtConfigurable.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.facebook.ktfmt.intellij;
+
+import com.facebook.ktfmt.intellij.KtfmtSettings.EnabledState;
+import com.intellij.openapi.options.BaseConfigurable;
+import com.intellij.openapi.options.ConfigurationException;
+import com.intellij.openapi.options.SearchableConfigurable;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.ComboBox;
+import com.intellij.uiDesigner.core.GridConstraints;
+import com.intellij.uiDesigner.core.GridLayoutManager;
+import com.intellij.uiDesigner.core.Spacer;
+import java.awt.Insets;
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JComponent;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import org.jetbrains.annotations.Nls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class KtfmtConfigurable extends BaseConfigurable implements SearchableConfigurable {
+
+  private final Project project;
+  private JPanel panel;
+  private JCheckBox enable;
+  private JComboBox styleComboBox;
+
+  public KtfmtConfigurable(Project project) {
+    this.project = project;
+  }
+
+  @NotNull
+  @Override
+  public String getId() {
+    return "ktfmt.settings";
+  }
+
+  @Nullable
+  @Override
+  public Runnable enableSearch(String option) {
+    return null;
+  }
+
+  @Nls
+  @Override
+  public String getDisplayName() {
+    return "ktfmt Settings";
+  }
+
+  @Nullable
+  @Override
+  public String getHelpTopic() {
+    return null;
+  }
+
+  @Nullable
+  @Override
+  public JComponent createComponent() {
+    return panel;
+  }
+
+  @Override
+  public void apply() throws ConfigurationException {
+    KtfmtSettings settings = KtfmtSettings.getInstance(project);
+    settings.setEnabled(enable.isSelected() ? EnabledState.ENABLED : getDisabledState());
+    settings.setUiFormatterStyle(((UiFormatterStyle) styleComboBox.getSelectedItem()));
+  }
+
+  private EnabledState getDisabledState() {
+    // The default settings (inherited by new projects) are either 'enabled' or
+    // 'show notification'. There's no way to default new projects to disabled. If
+    // someone wants
+    // that, we can add another checkbox, I suppose.
+    return project.isDefault() ? EnabledState.UNKNOWN : EnabledState.DISABLED;
+  }
+
+  @Override
+  public void reset() {
+    KtfmtSettings settings = KtfmtSettings.getInstance(project);
+    enable.setSelected(settings.isEnabled());
+    styleComboBox.setSelectedItem(settings.getUiFormatterStyle());
+  }
+
+  @Override
+  public boolean isModified() {
+    KtfmtSettings settings = KtfmtSettings.getInstance(project);
+    return enable.isSelected() != settings.isEnabled()
+        || !styleComboBox.getSelectedItem().equals(settings.getUiFormatterStyle());
+  }
+
+  @Override
+  public void disposeUIResources() {}
+
+  private void createUIComponents() {
+    styleComboBox = new ComboBox<>(UiFormatterStyle.values());
+  }
+
+  {
+    // GUI initializer generated by IntelliJ IDEA GUI Designer
+    // >>> IMPORTANT!! <<<
+    // DO NOT EDIT OR ADD ANY CODE HERE!
+    $$$setupUI$$$();
+  }
+
+  /**
+   * Method generated by IntelliJ IDEA GUI Designer >>> IMPORTANT!! <<< DO NOT edit this method OR
+   * call it in your code!
+   *
+   * @noinspection ALL
+   */
+  private void $$$setupUI$$$() {
+    createUIComponents();
+    panel = new JPanel();
+    panel.setLayout(new GridLayoutManager(3, 2, new Insets(0, 0, 0, 0), -1, -1));
+    enable = new JCheckBox();
+    enable.setText("Enable ktfmt");
+    panel.add(
+        enable,
+        new GridConstraints(
+            0,
+            0,
+            1,
+            2,
+            GridConstraints.ANCHOR_WEST,
+            GridConstraints.FILL_NONE,
+            GridConstraints.SIZEPOLICY_CAN_SHRINK | GridConstraints.SIZEPOLICY_CAN_GROW,
+            GridConstraints.SIZEPOLICY_FIXED,
+            null,
+            null,
+            null,
+            0,
+            false));
+    final Spacer spacer1 = new Spacer();
+    panel.add(
+        spacer1,
+        new GridConstraints(
+            2,
+            0,
+            1,
+            2,
+            GridConstraints.ANCHOR_CENTER,
+            GridConstraints.FILL_VERTICAL,
+            1,
+            GridConstraints.SIZEPOLICY_WANT_GROW,
+            null,
+            null,
+            null,
+            0,
+            false));
+    final JLabel label1 = new JLabel();
+    label1.setText("Code style");
+    panel.add(
+        label1,
+        new GridConstraints(
+            1,
+            0,
+            1,
+            1,
+            GridConstraints.ANCHOR_WEST,
+            GridConstraints.FILL_NONE,
+            GridConstraints.SIZEPOLICY_FIXED,
+            GridConstraints.SIZEPOLICY_FIXED,
+            null,
+            null,
+            null,
+            0,
+            false));
+    panel.add(
+        styleComboBox,
+        new GridConstraints(
+            1,
+            1,
+            1,
+            1,
+            GridConstraints.ANCHOR_WEST,
+            GridConstraints.FILL_HORIZONTAL,
+            GridConstraints.SIZEPOLICY_CAN_GROW,
+            GridConstraints.SIZEPOLICY_FIXED,
+            null,
+            null,
+            null,
+            1,
+            false));
+  }
+
+  /** @noinspection ALL */
+  public JComponent $$$getRootComponent$$$() {
+    return panel;
+  }
+}
diff --git a/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/KtfmtInstaller.java b/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/KtfmtInstaller.java
new file mode 100644
index 0000000..ab414e8
--- /dev/null
+++ b/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/KtfmtInstaller.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.facebook.ktfmt.intellij;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import com.intellij.ide.plugins.IdeaPluginDescriptor;
+import com.intellij.ide.plugins.PluginManagerCore;
+import com.intellij.openapi.extensions.PluginId;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectManagerListener;
+import com.intellij.psi.codeStyle.CodeStyleManager;
+import com.intellij.serviceContainer.ComponentManagerImpl;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * A component that replaces the default IntelliJ {@link CodeStyleManager} with one that formats via
+ * ktfmt.
+ */
+final class KtfmtInstaller implements ProjectManagerListener {
+
+  @Override
+  public void projectOpened(@NotNull Project project) {
+    installFormatter(project);
+  }
+
+  private static void installFormatter(Project project) {
+    CodeStyleManager currentManager = CodeStyleManager.getInstance(project);
+
+    if (currentManager instanceof KtfmtCodeStyleManager) {
+      currentManager = ((KtfmtCodeStyleManager) currentManager).getDelegate();
+    }
+
+    setManager(project, new KtfmtCodeStyleManager(currentManager));
+  }
+
+  private static void setManager(Project project, CodeStyleManager newManager) {
+    ComponentManagerImpl platformComponentManager = (ComponentManagerImpl) project;
+    IdeaPluginDescriptor plugin =
+        PluginManagerCore.getPlugin(PluginId.getId("com.facebook.ktfmt_idea_plugin"));
+    checkState(plugin != null, "Couldn't locate our own PluginDescriptor.");
+    platformComponentManager.registerServiceInstance(CodeStyleManager.class, newManager, plugin);
+  }
+}
diff --git a/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/KtfmtSettings.java b/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/KtfmtSettings.java
new file mode 100644
index 0000000..076e633
--- /dev/null
+++ b/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/KtfmtSettings.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.facebook.ktfmt.intellij;
+
+import com.intellij.openapi.components.PersistentStateComponent;
+import com.intellij.openapi.components.ServiceManager;
+import com.intellij.openapi.components.State;
+import com.intellij.openapi.components.Storage;
+import com.intellij.openapi.project.Project;
+import org.jetbrains.annotations.Nullable;
+
+@State(
+    name = "KtfmtSettings",
+    storages = {@Storage("ktfmt.xml")})
+class KtfmtSettings implements PersistentStateComponent<KtfmtSettings.State> {
+
+  private State state = new State();
+
+  static KtfmtSettings getInstance(Project project) {
+    return ServiceManager.getService(project, KtfmtSettings.class);
+  }
+
+  @Nullable
+  @Override
+  public State getState() {
+    return state;
+  }
+
+  @Override
+  public void loadState(State state) {
+    this.state = state;
+  }
+
+  boolean isEnabled() {
+    return state.enabled.equals(EnabledState.ENABLED);
+  }
+
+  void setEnabled(boolean enabled) {
+    setEnabled(enabled ? EnabledState.ENABLED : EnabledState.DISABLED);
+  }
+
+  void setEnabled(EnabledState enabled) {
+    state.enabled = enabled;
+  }
+
+  boolean isUninitialized() {
+    return state.enabled.equals(EnabledState.UNKNOWN);
+  }
+
+  UiFormatterStyle getUiFormatterStyle() {
+    return state.uiFormatterStyle;
+  }
+
+  void setUiFormatterStyle(UiFormatterStyle uiFormatterStyle) {
+    state.uiFormatterStyle = uiFormatterStyle;
+  }
+
+  enum EnabledState {
+    UNKNOWN,
+    ENABLED,
+    DISABLED;
+  }
+
+  static class State {
+
+    private EnabledState enabled = EnabledState.UNKNOWN;
+    public UiFormatterStyle uiFormatterStyle = UiFormatterStyle.DEFAULT;
+
+    // enabled used to be a boolean so we use bean property methods for backwards
+    // compatibility
+    public void setEnabled(@Nullable String enabledStr) {
+      if (enabledStr == null) {
+        enabled = EnabledState.UNKNOWN;
+      } else if (Boolean.valueOf(enabledStr)) {
+        enabled = EnabledState.ENABLED;
+      } else {
+        enabled = EnabledState.DISABLED;
+      }
+    }
+
+    public String getEnabled() {
+      switch (enabled) {
+        case ENABLED:
+          return "true";
+        case DISABLED:
+          return "false";
+        default:
+          return null;
+      }
+    }
+  }
+}
diff --git a/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/UiFormatterStyle.java b/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/UiFormatterStyle.java
new file mode 100644
index 0000000..8147397
--- /dev/null
+++ b/ktfmt_idea_plugin/src/main/java/com/facebook/ktfmt/intellij/UiFormatterStyle.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.facebook.ktfmt.intellij;
+
+import static com.facebook.ktfmt.format.Formatter.DROPBOX_FORMAT;
+import static com.facebook.ktfmt.format.Formatter.GOOGLE_FORMAT;
+import static com.facebook.ktfmt.format.Formatter.KOTLINLANG_FORMAT;
+
+import com.facebook.ktfmt.format.FormattingOptions;
+
+/** Configuration options for the formatting style. */
+enum UiFormatterStyle {
+  DEFAULT("Default", new FormattingOptions()),
+  DROPBOX("Dropbox", DROPBOX_FORMAT),
+  GOOGLE("Google (internal)", GOOGLE_FORMAT),
+  KOTLINLANG("Kotlinlang", KOTLINLANG_FORMAT);
+
+  private final String description;
+  private final FormattingOptions formattingOptions;
+
+  UiFormatterStyle(String description, FormattingOptions formattingOptions) {
+    this.description = description;
+    this.formattingOptions = formattingOptions;
+  }
+
+  FormattingOptions getFormattingOptions() {
+    return formattingOptions;
+  }
+
+  @Override
+  public String toString() {
+    return description;
+  }
+}
diff --git a/ktfmt_idea_plugin/src/main/resources/META-INF/plugin.xml b/ktfmt_idea_plugin/src/main/resources/META-INF/plugin.xml
new file mode 100644
index 0000000..bf371f9
--- /dev/null
+++ b/ktfmt_idea_plugin/src/main/resources/META-INF/plugin.xml
@@ -0,0 +1,27 @@
+<idea-plugin url="https://github.com/facebookincubator/ktfmt/tree/main/ktfmt_idea_plugin">
+    <id>com.facebook.ktfmt_idea_plugin</id>
+    <name>ktfmt</name>
+    <vendor url="https://github.com/facebookincubator/ktfmt">Facebook</vendor>
+
+    <description>ktfmt is a program that reformats Kotlin source code to comply with the common community standard for
+        Kotlin code conventions.
+    </description>
+
+    <depends>com.intellij.modules.platform</depends>
+
+    <applicationListeners>
+        <listener class="com.facebook.ktfmt.intellij.InitialConfigurationProjectManagerListener"
+                  topic="com.intellij.openapi.project.ProjectManagerListener"/>
+        <listener class="com.facebook.ktfmt.intellij.KtfmtInstaller"
+                  topic="com.intellij.openapi.project.ProjectManagerListener"/>
+    </applicationListeners>
+
+    <extensions defaultExtensionNs="com.intellij">
+        <projectConfigurable instance="com.facebook.ktfmt.intellij.KtfmtConfigurable"
+                             id="com.facebook.ktfmt_idea_plugin.settings"
+                             displayName="ktfmt Settings"
+                             parentId="editor"/>
+        <projectService serviceImplementation="com.facebook.ktfmt.intellij.KtfmtSettings"/>
+    </extensions>
+
+</idea-plugin>
diff --git a/ktfmt_idea_plugin/src/test/java/com/facebook/ktfmt/intellij/FormatterUtilTest.java b/ktfmt_idea_plugin/src/test/java/com/facebook/ktfmt/intellij/FormatterUtilTest.java
new file mode 100644
index 0000000..8b6b642
--- /dev/null
+++ b/ktfmt_idea_plugin/src/test/java/com/facebook/ktfmt/intellij/FormatterUtilTest.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.facebook.ktfmt.intellij;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class FormatterUtilTest {
+  @Test
+  public void getReplacements() throws Exception {
+    String code = "val   a =    5";
+    String expected = "val a = 5\n";
+    String actual = FormatterUtil.formatCode(UiFormatterStyle.DEFAULT, code);
+
+    assertEquals(expected, actual);
+  }
+}
diff --git a/online_formatter/README.md b/online_formatter/README.md
new file mode 100644
index 0000000..139e582
--- /dev/null
+++ b/online_formatter/README.md
@@ -0,0 +1,15 @@
+# AWS Lambda to format Kotlin code using ktfmt
+
+## Build
+
+```
+./gradlew build
+```
+
+## Deploy
+
+```
+./build_and_deploy.sh
+```
+
+The script creates two jars, one with the `com.facebook.ktfmt.onlineformatter` package, and the other with all of its dependencies (including ktfmt itself). This makes deploying just the Lambda faster.
diff --git a/online_formatter/build.gradle.kts b/online_formatter/build.gradle.kts
new file mode 100644
index 0000000..140c338
--- /dev/null
+++ b/online_formatter/build.gradle.kts
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
+plugins { kotlin("jvm") version "1.5.0" }
+
+repositories {
+  mavenLocal()
+  mavenCentral()
+}
+
+val ktfmtVersion = rootProject.file("../version.txt").readText().trim()
+
+dependencies {
+  implementation("com.facebook:ktfmt:$ktfmtVersion")
+  implementation(platform("software.amazon.awssdk:bom:2.10.73"))
+  implementation("software.amazon.awssdk:lambda")
+  implementation("com.amazonaws:aws-lambda-java-core:1.2.1")
+  implementation("com.amazonaws:aws-lambda-java-events:2.2.9")
+  implementation("com.google.code.gson:gson:2.8.6")
+  testImplementation(kotlin("test-junit"))
+}
+
+tasks {
+  test { useJUnit() }
+
+  withType<KotlinCompile>() { kotlinOptions.jvmTarget = "11" }
+
+  val packageFat by
+      creating(Zip::class) {
+        from(compileKotlin)
+        from(processResources)
+        into("lib") { from(configurations.runtimeClasspath) }
+        dirMode = 0b111101101 // 0755
+        fileMode = 0b111101101 // 0755
+      }
+
+  val packageLibs by
+      creating(Zip::class) {
+        into("java/lib") { from(configurations.runtimeClasspath) }
+        dirMode = 0b111101101 // 0755
+        fileMode = 0b111101101 // 0755
+      }
+
+  val packageSkinny by
+      creating(Zip::class) {
+        from(compileKotlin)
+        from(processResources)
+      }
+
+  build { dependsOn(packageSkinny) }
+}
diff --git a/online_formatter/build_and_deploy.sh b/online_formatter/build_and_deploy.sh
new file mode 100755
index 0000000..c0ec4b0
--- /dev/null
+++ b/online_formatter/build_and_deploy.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+# Copyright (c) Meta Platforms, Inc. and affiliates.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -eo pipefail
+./gradlew -q packageLibs
+mv build/distributions/lambda.zip build/lambda-lib.zip
+
+./gradlew build
+
+ARTIFACT_BUCKET=ktfmt-online-formatter-lambda
+TEMPLATE=format.yaml
+aws cloudformation package --template-file $TEMPLATE --s3-bucket $ARTIFACT_BUCKET --output-template-file out.yml
+aws cloudformation deploy --template-file out.yml --stack-name ktfmt --capabilities CAPABILITY_NAMED_IAM --region=us-east-2
diff --git a/online_formatter/format.yaml b/online_formatter/format.yaml
new file mode 100644
index 0000000..bb504fb
--- /dev/null
+++ b/online_formatter/format.yaml
@@ -0,0 +1,29 @@
+AWSTemplateFormatVersion: '2010-09-09'
+Transform: 'AWS::Serverless-2016-10-31'
+Description: An AWS Serverless Specification template describing your function.
+Resources:
+  format:
+    Type: 'AWS::Serverless::Function'
+    Properties:
+      CodeUri: build/distributions/lambda.zip
+      Handler: 'com.facebook.ktfmt.onlineformatter.Handler::handleRequest'
+      Runtime: java11
+      Description: ''
+      MemorySize: 1024
+      Timeout: 15
+      Policies:
+        - AWSLambdaBasicExecutionRole
+        - AWSLambda_ReadOnlyAccess
+        - AWSXrayWriteOnlyAccess
+        - AWSLambdaVPCAccessExecutionRole
+      Tracing: Active
+      Layers:
+        - !Ref libs
+  libs:
+    Type: AWS::Serverless::LayerVersion
+    Properties:
+      LayerName: ktfmt-lib
+      Description: Dependencies for ktfmt
+      ContentUri: build/lambda-lib.zip
+      CompatibleRuntimes:
+        - java11
diff --git a/online_formatter/gradle.properties b/online_formatter/gradle.properties
new file mode 100644
index 0000000..7fc6f1f
--- /dev/null
+++ b/online_formatter/gradle.properties
@@ -0,0 +1 @@
+kotlin.code.style=official
diff --git a/online_formatter/gradle/wrapper/gradle-wrapper.jar b/online_formatter/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..e708b1c
--- /dev/null
+++ b/online_formatter/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/online_formatter/gradle/wrapper/gradle-wrapper.properties b/online_formatter/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..be52383
--- /dev/null
+++ b/online_formatter/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/online_formatter/gradlew b/online_formatter/gradlew
new file mode 100755
index 0000000..4f906e0
--- /dev/null
+++ b/online_formatter/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+    echo "$*"
+}
+
+die () {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+  NONSTOP* )
+    nonstop=true
+    ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+    JAVACMD=`cygpath --unix "$JAVACMD"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=`expr $i + 1`
+    done
+    case $i in
+        0) set -- ;;
+        1) set -- "$args0" ;;
+        2) set -- "$args0" "$args1" ;;
+        3) set -- "$args0" "$args1" "$args2" ;;
+        4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Escape application args
+save () {
+    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+    echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/online_formatter/gradlew.bat b/online_formatter/gradlew.bat
new file mode 100644
index 0000000..ac1b06f
--- /dev/null
+++ b/online_formatter/gradlew.bat
@@ -0,0 +1,89 @@
+@rem

+@rem Copyright 2015 the original author or authors.

+@rem

+@rem Licensed under the Apache License, Version 2.0 (the "License");

+@rem you may not use this file except in compliance with the License.

+@rem You may obtain a copy of the License at

+@rem

+@rem      https://www.apache.org/licenses/LICENSE-2.0

+@rem

+@rem Unless required by applicable law or agreed to in writing, software

+@rem distributed under the License is distributed on an "AS IS" BASIS,

+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+@rem See the License for the specific language governing permissions and

+@rem limitations under the License.

+@rem

+

+@if "%DEBUG%" == "" @echo off

+@rem ##########################################################################

+@rem

+@rem  Gradle startup script for Windows

+@rem

+@rem ##########################################################################

+

+@rem Set local scope for the variables with windows NT shell

+if "%OS%"=="Windows_NT" setlocal

+

+set DIRNAME=%~dp0

+if "%DIRNAME%" == "" set DIRNAME=.

+set APP_BASE_NAME=%~n0

+set APP_HOME=%DIRNAME%

+

+@rem Resolve any "." and ".." in APP_HOME to make it shorter.

+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi

+

+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.

+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"

+

+@rem Find java.exe

+if defined JAVA_HOME goto findJavaFromJavaHome

+

+set JAVA_EXE=java.exe

+%JAVA_EXE% -version >NUL 2>&1

+if "%ERRORLEVEL%" == "0" goto execute

+

+echo.

+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

+echo.

+echo Please set the JAVA_HOME variable in your environment to match the

+echo location of your Java installation.

+

+goto fail

+

+:findJavaFromJavaHome

+set JAVA_HOME=%JAVA_HOME:"=%

+set JAVA_EXE=%JAVA_HOME%/bin/java.exe

+

+if exist "%JAVA_EXE%" goto execute

+

+echo.

+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%

+echo.

+echo Please set the JAVA_HOME variable in your environment to match the

+echo location of your Java installation.

+

+goto fail

+

+:execute

+@rem Setup the command line

+

+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar

+

+

+@rem Execute Gradle

+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*

+

+:end

+@rem End local scope for the variables with windows NT shell

+if "%ERRORLEVEL%"=="0" goto mainEnd

+

+:fail

+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of

+rem the _cmd.exe /c_ return code!

+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1

+exit /b 1

+

+:mainEnd

+if "%OS%"=="Windows_NT" endlocal

+

+:omega

diff --git a/online_formatter/settings.gradle.kts b/online_formatter/settings.gradle.kts
new file mode 100644
index 0000000..67cc482
--- /dev/null
+++ b/online_formatter/settings.gradle.kts
@@ -0,0 +1,17 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+rootProject.name = "lambda"
diff --git a/online_formatter/src/main/kotlin/main.kt b/online_formatter/src/main/kotlin/main.kt
new file mode 100644
index 0000000..22611fe
--- /dev/null
+++ b/online_formatter/src/main/kotlin/main.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.facebook.ktfmt.onlineformatter
+
+import com.amazonaws.services.lambda.runtime.Context
+import com.amazonaws.services.lambda.runtime.RequestHandler
+import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent
+import com.facebook.ktfmt.cli.ParsedArgs
+import com.facebook.ktfmt.format.Formatter
+import com.google.gson.Gson
+import java.io.ByteArrayOutputStream
+import java.io.PrintStream
+
+class Handler : RequestHandler<APIGatewayProxyRequestEvent, String> {
+  init {
+    // Warm up.
+    gson.toJson(Formatter.format(Formatter.KOTLINLANG_FORMAT, "/* foo */ fun foo() {}"))
+  }
+
+  override fun handleRequest(event: APIGatewayProxyRequestEvent, context: Context?): String {
+    return gson.toJson(
+        try {
+          val request = gson.fromJson(event.body, Request::class.java)
+          val parsingErrors = ByteArrayOutputStream()
+          val style = request.style
+          val parsedArgs =
+              ParsedArgs.parseOptions(
+                  PrintStream(parsingErrors), if (style == null) arrayOf() else arrayOf(style))
+          Response(
+              Formatter.format(
+                  parsedArgs.formattingOptions.copy(maxWidth = request.maxWidth ?: 100),
+                  request.source ?: ""),
+              parsingErrors.toString().ifEmpty { null })
+        } catch (e: Exception) {
+          Response(null, e.message)
+        })
+  }
+
+  companion object {
+    val gson = Gson()
+  }
+}
+
+class Request {
+  var source: String? = ""
+  var style: String? = ""
+  var maxWidth: Int? = 100
+}
+
+data class Response(val source: String?, val err: String?)
diff --git a/online_formatter/src/test/kotlin/HandlerTest.kt b/online_formatter/src/test/kotlin/HandlerTest.kt
new file mode 100644
index 0000000..ce7a141
--- /dev/null
+++ b/online_formatter/src/test/kotlin/HandlerTest.kt
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.facebook.ktfmt.onlineformatter
+
+import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent
+import kotlin.test.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class HandlerTest {
+
+  @Test
+  fun dontCrash_success() {
+    val event = APIGatewayProxyRequestEvent()
+    event.body = """{"source": "fun foo ( ) { }","style": "--kotlinlang-style"}"""
+    val response = Handler().handleRequest(event, null)
+    assertEquals("""{"source":"fun foo() {}\n"}""", response)
+  }
+
+  @Test
+  fun dontCrash_malformedRequestJson() {
+    val event = APIGatewayProxyRequestEvent()
+    event.body = """{"source": "fun foo ( ) { }"style": "--kotlinlang-style"}"""
+    val response = Handler().handleRequest(event, null)
+    assertEquals(
+        """{"err":"com.google.gson.stream.MalformedJsonException: """ +
+            """Unterminated object at line 1 column 30 path ${'$'}.source"}""",
+        response)
+  }
+
+  @Test
+  fun dontCrash_emptyRequestJson() {
+    val event = APIGatewayProxyRequestEvent()
+    event.body = ""
+    val response = Handler().handleRequest(event, null)
+    assertEquals("{}", response)
+  }
+
+  @Test
+  fun dontCrash_malformedSourceCode() {
+    val event = APIGatewayProxyRequestEvent()
+    event.body = """{"source": "fun foo ( { }","style": "--kotlinlang-style"}"""
+    val response = Handler().handleRequest(event, null)
+    assertEquals("""{"err":"1:10: error: Expecting \u0027)\u0027"}""", response)
+  }
+
+  @Test
+  fun dontCrash_allNullRequest() {
+    val event = APIGatewayProxyRequestEvent()
+    event.body = """{}"""
+    val response = Handler().handleRequest(event, null)
+    assertEquals("""{"source":"\n"}""", response)
+  }
+
+  @Test
+  fun dontCrash_malformedStyle() {
+    val event = APIGatewayProxyRequestEvent()
+    event.body = """{"source": "fun foo ( ) { }","style": "blabla"}"""
+    val response = Handler().handleRequest(event, null)
+    assertEquals("""{"source":"fun foo() {}\n"}""", response)
+  }
+
+  @Test
+  fun dontCrash_noStyle() {
+    val event = APIGatewayProxyRequestEvent()
+    event.body = """{"source": "fun foo ( ) { }"}"""
+    val response = Handler().handleRequest(event, null)
+    assertEquals("""{"source":"fun foo() {}\n"}""", response)
+  }
+
+  @Test
+  fun dontCrash_negativeMaxWidth() {
+    val event = APIGatewayProxyRequestEvent()
+    event.body = """{"source": "fun foo ( ) { }", "maxWidth": -100}"""
+    val response = Handler().handleRequest(event, null)
+    assertEquals("""{"source":"fun foo() {}\n"}""", response)
+  }
+
+  /** maxWidth == null is treated as if it were 100 */
+  @Test
+  fun dontCrash_nullMaxWidth() {
+    val event = APIGatewayProxyRequestEvent()
+    event.body = """{"source": "fun foo ( ) { }", "maxWidth": null}"""
+    val response = Handler().handleRequest(event, null)
+    assertEquals("""{"source":"fun foo() {}\n"}""", response)
+  }
+
+  @Test
+  fun dontCrash_nonIntegerMaxWidth() {
+    val event = APIGatewayProxyRequestEvent()
+    event.body = """{"source": "fun foo ( ) { }", "maxWidth": "bla"}"""
+    val response = Handler().handleRequest(event, null)
+    assertEquals(
+        """{"err":"java.lang.NumberFormatException: For input string: \"bla\""}""", response)
+  }
+
+  @Test
+  fun dontCrash_coerceMaxWidthToInt() {
+    val event = APIGatewayProxyRequestEvent()
+    event.body = """{"source": "fun foo ( ) { }", "maxWidth": "100"}"""
+    val response = Handler().handleRequest(event, null)
+    assertEquals("""{"source":"fun foo() {}\n"}""", response)
+  }
+}
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..b8a40db
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" 
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>com.facebook</groupId>
+  <artifactId>ktfmt-parent</artifactId>
+  <version>0.39</version>
+  <packaging>pom</packaging>
+
+  <name>Ktfmt Parent</name>
+  <description>A program that reformats Kotlin source code to comply with the common community standard for Kotlin code conventions.</description>
+  <url>https://github.com/facebookincubator/ktfmt</url>
+  <inceptionYear>2019</inceptionYear>
+  <licenses>
+    <license>
+      <name>The Apache Software License, Version 2.0</name>
+      <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
+    </license>
+  </licenses>
+  <developers>
+    <developer>
+      <name>Facebook</name>
+    </developer>
+  </developers>
+  <scm>
+    <connection>scm:git:https://github.com/facebookincubator/ktfmt.git</connection>
+    <developerConnection>scm:git:git@github.com:facebookincubator/ktfmt.git</developerConnection>
+    <url>https://github.com/facebookincubator/ktfmt.git</url>
+  </scm>
+
+  <modules>
+    <module>core</module>
+  </modules>
+
+  <distributionManagement>
+    <snapshotRepository>
+      <id>ossrh</id>
+      <url>https://oss.sonatype.org/content/repositories/snapshots</url>
+    </snapshotRepository>
+  </distributionManagement>
+
+  <profiles>
+    <profile>
+      <id>release</id>
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.sonatype.plugins</groupId>
+            <artifactId>nexus-staging-maven-plugin</artifactId>
+            <version>1.6.7</version>
+            <extensions>true</extensions>
+            <configuration>
+              <serverId>ossrh</serverId>
+              <nexusUrl>https://oss.sonatype.org/</nexusUrl>
+              <autoReleaseAfterClose>true</autoReleaseAfterClose>
+            </configuration>
+          </plugin>
+
+          <!-- Sign artifacts -->
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-gpg-plugin</artifactId>
+            <version>1.6</version>
+            <executions>
+              <execution>
+                <id>sign-artifacts</id>
+                <phase>verify</phase>
+                <goals>
+                  <goal>sign</goal>
+                </goals>
+                <configuration>
+                  <!-- Honor -Dgpg.passphrase=... on the command line. -->
+                  <gpgArguments>
+                    <arg>--pinentry-mode</arg>
+                    <arg>loopback</arg>
+                  </gpgArguments>
+                </configuration>
+              </execution>
+            </executions>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
+  </profiles>
+
+
+</project>
diff --git a/version.txt b/version.txt
new file mode 100644
index 0000000..751b1ea
--- /dev/null
+++ b/version.txt
@@ -0,0 +1 @@
+0.39
diff --git a/website/README.md b/website/README.md
new file mode 100644
index 0000000..14ba602
--- /dev/null
+++ b/website/README.md
@@ -0,0 +1,19 @@
+# A demo website for ktfmt
+
+## Run
+
+```
+npm install .
+npm run simpleserver
+```
+
+## Build for deployment
+
+```
+npm install .
+KTFMT_TMP_DIR=$(mktemp -d)
+KTFMT_WEBSITE_OUTPUT_DIR=$KTFMT_TMP_DIR gulp build-website
+
+# Clean up:
+rm -rf "$KTFMT_TMP_DIR"
+```
diff --git a/website/all.css b/website/all.css
new file mode 100644
index 0000000..9a15931
--- /dev/null
+++ b/website/all.css
@@ -0,0 +1,310 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Forked from https://microsoft.github.io/monaco-editor/ */
+
+body {
+  padding: 54px 0 40px 0;
+  overflow-y: scroll;
+}
+
+body.home {
+  padding-top: 0;
+}
+
+body,
+input,
+button,
+select,
+textarea,
+.navbar-search .search-query {
+  font: 400 14px/1.4em "Segoe UI", "Open Sans", Calibri, Candara, Arial, sans-serif;
+}
+
+.navbar .nav {
+  float: left;
+  margin-right: 0;
+}
+
+a {
+  color: #0066cc;
+  cursor: pointer;
+}
+
+h1, h2, h3, h4, h5, h6 {
+  letter-spacing: -0.01em;
+  margin: 0;
+  line-height: normal;
+}
+
+/***** Bootstrap Cosmo Overrides *****/
+h1, h2 {
+  font-family: "Segoe UI Light", "Segoe UI", "Open Sans", Calibri, Candara, Arial, sans-serif;
+  font-weight: 300;
+}
+h1 {
+  font-size: 72px;
+  letter-spacing: -0.02em;
+}
+
+.hero-unit h1 {
+  font-size: 48px;
+}
+
+h2 {
+  font-size: 26px;
+}
+
+h3 {
+  font-size: 26px;
+}
+
+h4 {
+  font-size: 16px;
+}
+
+h1 small,
+h2 small,
+h3 small,
+h4 small,
+h5 small,
+h6 small {
+  color: #999;
+}
+
+.alert-heading,
+.alert h1,
+.alert h2,
+.alert h3,
+.alert h4,
+.alert h5,
+.alert h6 {
+  color: inherit;
+}
+
+.alert-info {
+  color: #3a87ad;
+  background-color: #d9edf7;
+}
+
+.navbar-inverse .navbar-inner {
+  background-color: #68217A;
+  -webkit-filter: none;
+          filter: none;
+}
+
+.navbar-inverse.home .navbar-inner {
+  background-color: transparent;
+}
+
+.navbar-inverse .btn-navbar {
+  background: transparent;
+  margin-top: 14px;
+}
+
+.navbar-inverse .btn-navbar:hover,
+.navbar-inverse .btn-navbar:focus,
+.navbar-inverse .btn-navbar:active,
+.navbar-inverse .btn-navbar.active,
+.navbar-inverse .btn-navbar.disabled,
+.navbar-inverse .btn-navbar[disabled] {
+  background: #442359;
+}
+
+.btn-primary {
+  background-color: #0072C6;
+}
+
+.home .hero-unit {
+  margin-top: -54px;
+  position: relative;
+  z-index: 100;
+}
+
+.hero-unit {
+  font-weight: normal;
+}
+
+.hero-unit h1 {
+  margin: 0 0 6px;
+}
+
+.nav-tabs > li > a {
+  color: #999;
+}
+
+.nav-tabs > li > a:hover {
+  color: #555;
+}
+
+.nav-tabs > .active > a,
+.nav-tabs > .active > a:hover,
+.nav-tabs > .active > a:focus {
+  color: #0072C6;
+}
+
+/***** General *****/
+
+body > section > .container {
+  padding-top: 12px;
+}
+
+.masthead {
+  background-color: #0072C6;
+  color: white;
+}
+
+.masthead .hero-unit {
+  padding: 30px 0 0;
+  background: none;
+}
+
+.navbar.home {
+  position: relative;
+  z-index: 500;
+}
+
+.navbar .nav>li>a {
+  text-shadow: none;
+  padding-top: 18px;
+  font-size: 14px;
+  text-transform: uppercase;
+}
+
+.navbar-inverse .nav-collapse .nav>li>a {
+  color: white;
+  padding-left: 0;
+}
+
+.navbar-inverse .nav>li>a.nav-item:focus, .navbar-inverse .nav>li>a.nav-item:hover {
+  background-color: rgba(0,0,0,.12);
+}
+
+.navbar-inverse .nav .active>a.nav-item, .navbar-inverse .nav .active>a.nav-item:hover, .navbar-inverse .nav .active>a.nav-item:focus {
+  color:#fff;
+  background-color: rgba(0,0,0,.24);
+  -webkit-box-shadow: none;
+          box-shadow: none;
+}
+
+.navbar .logo {
+  padding: 16px 12px 0 0px;
+  height: 35px;
+  display: block;
+  float: left;
+  color: #fff;
+  font-size: 24px;
+  letter-spacing: -1px;
+}
+
+.navbar .logo a {
+  color: #fff;
+  font-size: 24px;
+  letter-spacing: -1px;
+}
+
+.navbar-fixed-top {
+  z-index: 500;
+}
+
+.flgroup:after {
+  content: "";
+  display: block;
+  visibility: hidden;
+  height: 0;
+  clear: both;
+}
+
+/* Controls */
+
+/* Media Queries */
+@media (min-width: 1200px) {
+  h1, h2 {
+    letter-spacing: -0.04em;
+  }
+
+  .hero-unit h1 {
+    font-size: 72px;
+  }
+
+  h2 {
+    font-size: 36px;
+  }
+
+}
+
+#gh-link {
+  display: none;
+  position: fixed;
+  top: 0;
+  right: 0;
+  border: 0;
+  margin:0;
+  z-index: 1000;
+}
+@media (min-width: 980px) {
+  #gh-link {
+    display: block;
+  }
+}
+
+@media (min-width: 980px) {
+  .navbar .nav {
+    float: right;
+  }
+
+  .navbar-inverse .nav-collapse .nav>li>a {
+    padding-left: 15px;
+  }
+}
+
+@media (min-width: 768px) and (max-width: 979px) {
+  h1 {
+    letter-spacing: -0.02em;
+  }
+}
+
+@media (max-width: 979px) {
+  body {
+    padding: inherit;
+  }
+
+  .navbar-fixed-top, .navbar-fixed-bottom, .navbar-static-top {
+    margin-right: inherit;
+    margin-left: inherit;
+    margin-bottom: 0;
+  }
+
+  .navbar-fixed-top .navbar-inner {
+    padding: 0 20px;
+  }
+
+  .navbar .container {
+    width: 724px;
+  }
+}
+
+@media (max-width: 767px) {
+  .navbar .container {
+    width: 100%;
+  }
+}
+
+@media (max-width: 480px) {
+  .navbar .logo a {
+    display: none;
+  }
+}
diff --git a/website/gulpfile.js b/website/gulpfile.js
new file mode 100644
index 0000000..dd08969
--- /dev/null
+++ b/website/gulpfile.js
@@ -0,0 +1,167 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Forked from https://microsoft.github.io/monaco-editor/ */
+
+const gulp = require("gulp");
+const es = require("event-stream");
+const path = require("path");
+const fs = require("fs");
+const os = require('os');
+const rimraf = require("rimraf");
+const cp = require("child_process");
+const CleanCSS = require("clean-css");
+const uncss = require("uncss");
+
+const VERSION = fs.readFileSync("../version.txt", "utf-8").trim();
+
+allow_deploy_to_github = process.env.KTFMT_WEBSITE_ALLOW_DEPLOY_TO_GITHUB == '1';
+outdir = process.env.KTFMT_WEBSITE_OUTPUT_DIR || path.join(__dirname, '../release-ktfmt-website');
+console.log('Using output dir: ' + outdir)
+
+// --- website
+const cleanWebsiteTask = function (cb) {
+  rimraf(outdir, { maxBusyTries: 1 }, cb);
+};
+const buildWebsiteTask = function () {
+  function replaceWithRelativeResource(dataPath, contents, regex, callback) {
+    return contents.replace(regex, function (_, m0) {
+      var filePath = path.join(path.dirname(dataPath), m0);
+      return callback(m0, fs.readFileSync(filePath));
+    });
+  }
+
+  var waiting = 0;
+  var done = false;
+
+  return es
+    .merge(
+      gulp
+        .src(["**/*"], {
+          dot: true,
+          ignore: [
+            "package.json",
+            "package-lock.json",
+            "node_modules/**/*",
+            "gulpfile.js",
+            '.DS_Store',
+          ],
+        })
+        .pipe(
+          es.through(
+            function (data) {
+              if (!data.contents || !/\.(html)$/.test(data.path)) {
+                return this.emit("data", data);
+              }
+
+              var contents = data.contents.toString();
+              contents = contents.replace(/{{version}}/g, VERSION);
+              contents = contents.replace(
+                /{{year}}/g,
+                new Date().getFullYear()
+              );
+
+              var allCSS = "";
+              var tmpcontents = replaceWithRelativeResource(
+                data.path,
+                contents,
+                /<link data-inline="yes-please" href="([^"]+)".*/g,
+                function (m0, fileContents) {
+                  allCSS += fileContents.toString("utf8");
+                  return "";
+                }
+              );
+              tmpcontents = tmpcontents.replace(/<script.*/g, "");
+              tmpcontents = tmpcontents.replace(/<link.*/g, "");
+
+              waiting++;
+              uncss(
+                tmpcontents,
+                { raw: allCSS },
+                function (err, output) {
+                  waiting--;
+
+                  if (!err) {
+                    output = new CleanCSS().minify(output).styles;
+                    var isFirst = true;
+                    contents = contents.replace(
+                      /<link data-inline="yes-please" href="([^"]+)".*/g,
+                      function (_, m0) {
+                        if (isFirst) {
+                          isFirst = false;
+                          return "<style>" + output + "</style>";
+                        }
+                        return "";
+                      }
+                    );
+                  }
+
+                  // Inline javascript
+                  contents = replaceWithRelativeResource(
+                    data.path,
+                    contents,
+                    /<script data-inline="yes-please" src="([^"]+)".*/g,
+                    function (m0, fileContents) {
+                      return (
+                        "<script>" + fileContents.toString("utf8") + "</script>"
+                      );
+                    }
+                  );
+
+                  data.contents = Buffer.from(
+                    contents.split(/\r\n|\r|\n/).join("\n")
+                  );
+                  this.emit("data", data);
+
+                  if (done && waiting === 0) {
+                    this.emit("end");
+                  }
+                }.bind(this)
+              );
+            },
+            function () {
+              done = true;
+              if (waiting === 0) {
+                this.emit("end");
+              }
+            }
+          )
+        )
+        .pipe(gulp.dest(outdir))
+    )
+    .pipe(
+      es.through(
+        function (data) {
+          this.emit("data", data);
+        },
+        function () {
+          // temporarily create package.json so that npm install doesn't bark
+          fs.writeFileSync(path.join(outdir, 'package.json'), '{}');
+          fs.writeFileSync(path.join(outdir, '.nojekyll'), '');
+          cp.execSync('npm install monaco-editor@0.23', {
+            cwd: outdir
+          });
+          rimraf.sync(path.join(outdir, 'node_modules/monaco-editor/dev'));
+          rimraf.sync(path.join(outdir, 'node_modules/monaco-editor/esm'));
+          fs.unlinkSync(path.join(outdir, 'package.json'));
+
+          this.emit("end");
+        }
+      )
+    );
+}
+buildWebsiteSeries = gulp.series(cleanWebsiteTask, buildWebsiteTask);
+gulp.task("build-website", buildWebsiteSeries);
diff --git a/website/img/oss_logo.png b/website/img/oss_logo.png
new file mode 100644
index 0000000..226c942
--- /dev/null
+++ b/website/img/oss_logo.png
Binary files differ
diff --git a/website/index.html b/website/index.html
new file mode 100644
index 0000000..7572e39
--- /dev/null
+++ b/website/index.html
@@ -0,0 +1,308 @@
+<!-- Forked from https://microsoft.github.io/monaco-editor/ -->
+
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+    <meta name="viewport" content="width=device-width" />
+
+    <title>ktfmt - the Kotlin code formatter</title>
+
+    <link data-inline="yes-please" href="./lib/bootstrap-cosmo.css" rel="stylesheet"/>
+    <link data-inline="yes-please" href="./lib/bootstrap-responsive.min.css" rel="stylesheet"/>
+    <link data-inline="yes-please" href="./all.css" rel="stylesheet" />
+    <link data-inline="yes-please" href="./index/index.css" rel="stylesheet" />
+
+    <link data-name="vs/editor/editor.main"
+      rel="stylesheet"
+      href="node_modules/monaco-editor/min/vs/editor/editor.main.css"
+    />
+  </head>
+
+  <body>
+    <a id="gh-link" href="https://github.com/facebookincubator/ktfmt"
+      ><img
+        loading="lazy"
+        width="149"
+        height="149"
+        src="https://github.blog/wp-content/uploads/2008/12/forkme_right_white_ffffff.png?resize=149%2C149"
+        class="attachment-full size-full"
+        alt="Fork me on GitHub"
+        data-recalc-dims="1"
+    /></a>
+    <nav class="navbar navbar-inverse navbar-fixed-top">
+      <div class="navbar-inner">
+        <div class="container">
+          <div class="logo">
+            ktfmt
+          </div>
+          <div class="logo">
+            <a href="index.html">- the Kotlin code formatter</a>
+          </div>
+          <!-- collapse button for smaller screens -->
+          <button
+            type="button"
+            class="btn btn-navbar"
+            data-toggle="collapse"
+            data-target=".nav-collapse"
+          >
+            <span class="icon-bar"></span>
+            <span class="icon-bar"></span>
+            <span class="icon-bar"></span>
+          </button>
+
+          <!-- navbar title -->
+          <div class="nav-collapse collapse">
+            <ul class="nav">
+              <li>
+                <a
+                  class="nav-item"
+                  href="https://github.com/facebookincubator/ktfmt"
+                  >GitHub</a
+                >
+              </li>
+              <li>
+                <a
+                  class="nav-item"
+                  href="https://kotlinlang.slack.com/archives/C01GZCU0QNB"
+                  >Slack</a
+                >
+              </li>
+              <li>
+                <a
+                  class="nav-item"
+                  href="https://search.maven.org/artifact/com.facebook/ktfmt/{{version}}/jar"
+                  >Maven Central</a
+                >
+              </li>
+            </ul>
+          </div>
+        </div>
+      </div>
+    </nav>
+    <section class="try">
+      <div class="container">
+        <h3>About</h3>
+        <div class="row">
+          <div class="span12">
+            <br />
+            <p>
+              <code>ktfmt</code> is a program that pretty-prints (formats)
+              Kotlin code.
+            </p>
+            <p>
+              It always produces the same result, regardless of how the code
+              looks initially, freeing developers to focus on essence.
+            </p>
+            <p>
+                It is by design non-customizable in order to promote consistency.
+              </p>
+            </div>
+        </div>
+        <hr />
+        <h3>Usage</h3>
+        <div class="row">
+          <div class="span12">
+            <br />
+            <ul class="nav nav-tabs" id="usage_tab">
+              <li class="active">
+                <a href="#usage_intellij" data-toggle="tab"
+                  >IntelliJ / Android Studio</a
+                >
+              </li>
+              <li>
+                <a href="#usage_gradle" data-toggle="tab">Gradle</a>
+              </li>
+              <li><a href="#usage_maven" data-toggle="tab">Maven</a></li>
+              <li><a href="#usage_cli" data-toggle="tab">Command line</a></li>
+            </ul>
+            <div class="tab-content">
+              <div class="tab-pane active" id="usage_intellij">
+                <p>
+                  A ktfmt plugin that augments the Reformat Code action (<code>⌥⌘L</code>) is available from JetBrains Marketplace.
+                </p>
+                <a class="btn btn-info" href="https://plugins.jetbrains.com/plugin/14912-ktfmt" target="_blank">Install</a>
+              </div>
+              <div class="tab-pane" id="usage_gradle">
+                <p>
+                  <u><b>Spotless</b></u> (<a
+                    href="https://github.com/diffplug/spotless/tree/main/plugin-gradle#ktfmt"
+                    >details</a
+                  >):
+                </p>
+
+                <pre>
+# build.gradle.kts
+plugins { id("com.diffplug.spotless") }
+    
+// version and style are optional
+spotless { kotlin { ktfmt('{{version}}').kotlinlangStyle() } }
+</pre
+                >
+
+                <p>or,</p>
+                <p>
+                  <u><b>ktfmt-gradle</b></u> (<a
+                    href="https://github.com/cortinico/ktfmt-gradle#how-to-use-"
+                    >details</a
+                  >):
+                </p>
+                <pre>
+# build.gradle.kts
+plugins { id("com.ncorti.ktfmt.gradle") }
+
+ktfmt { kotlinLangStyle() }
+</pre
+                >
+              </div>
+              <div class="tab-pane" id="usage_maven">
+                <p>
+                  <u><b>Spotless</b></u> (<a
+                    href="https://github.com/diffplug/spotless/tree/main/plugin-maven#ktfmt"
+                    >details</a
+                  >):
+                </p>
+
+                <pre>
+# pom.xml
+&lt;configuration&gt;
+  &lt;kotlin&gt;
+    &lt;ktfmt&gt;
+      &lt;version&gt;{{version}}&lt;/version&gt;
+    &lt;/ktfmt&gt;
+  &lt;/kotlin&gt;
+&lt;/configuration&gt;
+</pre
+                >
+              </div>
+              <div class="tab-pane" id="usage_cli">
+                <p>
+                  Download the jar from Maven Central and invoke it using
+                  <code>java</code>:
+                </p>
+                <pre>
+$ wget https://repo1.maven.org/maven2/com/facebook/ktfmt/{{version}}/ktfmt-{{version}}-jar-with-dependencies.jar
+$ java -jar ktfmt-{{version}}-jar-with-dependencies.jar [--kotlinlang-style] [files...]
+</pre
+                >
+              </div>
+            </div>
+          </div>
+        </div>
+        <hr />
+        <h3>Try It Online</h3>
+        <small>Running v{{version}}</small>
+        <div class="editor row">
+          <div class="span12">
+            <div class="row">
+              <form id="editorForm" name="editorForm">
+                <div class="span3">
+                  <label class="control-label">Style</label>
+                  <select class="style-picker input-medium">
+                    <option>kotlinlang</option>
+                    <option>Java-like</option>
+                  </select>
+                </div>
+                <div class="span3">
+                  <label class="control-label">Column Limit</label>
+                  <input
+                    class="input-small column-limit-picker"
+                    type="number"
+                    min="50"
+                    max="140"
+                    class="form-control"
+                    id="column_limit"
+                    value="100"
+                    required
+                  />
+                </div>
+                <div class="span4 pull-right">
+                  <button class="btn btn-primary pull-right" type="submit">
+                    Format
+                  </button>
+                  <p
+                    id="error-message"
+                    class="text-error"
+                    style="display: none"
+                  >
+                    Donec ullamcorper nulla non metus auctor fringilla.
+                  </p>
+                </div>
+              </form>
+            </div>
+            <div class="editor-frame">
+              <div class="loading editor" style="display: none">
+                <div class="progress progress-striped active">
+                  <div class="bar"></div>
+                </div>
+              </div>
+              <div id="editor"></div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </section>
+
+    <footer class="container">
+      <hr />
+      <p class="text-center">
+        [
+        <a
+          target="_blank"
+          rel="noopener noreferrer"
+          href="https://opensource.facebook.com/legal/privacy/"
+          >Privacy</a
+        >
+        ] [
+        <a
+          target="_blank"
+          rel="noopener noreferrer"
+          href="https://opensource.facebook.com/legal/terms/"
+          >Terms</a
+        >
+        ] [
+        <a
+          target="_blank"
+          rel="noopener noreferrer"
+          href="https://opensource.facebook.com/legal/data-policy/"
+          >Data Policy</a
+        >
+        ] [
+        <a
+          target="_blank"
+          rel="noopener noreferrer"
+          href="https://opensource.facebook.com/legal/cookie-policy/"
+          >Cookie Policy</a
+        >
+        ]
+      </p>
+      <p class="text-center">
+        <a href="https://opensource.facebook.com/" title="Facebook Open Source">
+          <img
+            src="img/oss_logo.png"
+            alt="Facebook Open Source"
+            style="max-height: 50px; margin-bottom: 12px"
+          />
+        </a>
+        <br />
+        <small>&copy; {{year}} Facebook, Inc. Based on <a href="https://microsoft.github.io/monaco-editor">https://microsoft.github.io/monaco-editor</a></small>
+      </p>
+    </footer>
+
+    <script
+      src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"
+      integrity="sha256-wS9gmOZBqsqWxgIVgA8Y9WcQOa7PgSIX+rPA0VL2rbQ="
+      crossorigin="anonymous"
+    ></script>
+    <script
+      src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.3.0/bootstrap.min.js"
+      integrity="sha256-u+l2mGjpmGK/mFgUncmMcFKdMijvV+J3odlDJZSNUu8="
+      crossorigin="anonymous"
+    ></script>
+
+    <script src="node_modules/monaco-editor/min/vs/loader.js"></script>
+    <script src="node_modules/monaco-editor/min/vs/editor/editor.main.nls.js"></script>
+    <script src="node_modules/monaco-editor/min/vs/editor/editor.main.js"></script>
+    <script data-inline="yes-please" src="./index/index.js"></script>
+  </body>
+</html>
diff --git a/website/index/index.css b/website/index/index.css
new file mode 100644
index 0000000..1135b17
--- /dev/null
+++ b/website/index/index.css
@@ -0,0 +1,274 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Forked from https://microsoft.github.io/monaco-editor/ */
+
+.try .drops {
+    margin: 0;
+    list-style: none;
+    float: right;
+}
+
+.try .drops li {
+    float: left;
+    width: auto;
+    height: auto;
+    text-indent: 0;
+    font-size: 26px;
+    line-height: normal;
+    margin: 0 12px;
+    cursor: pointer;
+}
+
+.try .drops li a:hover,
+.try .drops li a:focus {
+    text-decoration: none;
+}
+
+.try .drops li h4 {
+    color: #999;
+}
+
+.try .drops li.active h4,
+.try .drops li.active:hover h4 {
+    color: #0072C6;
+}
+
+.try .drops li:hover h4 {
+    color: rgba(0, 114, 198, .5);
+}
+
+.try .editor.row {
+    padding: 18px 0
+}
+
+.try .row h4 {
+    padding-bottom: 6px;
+}
+
+.try .tile {
+    position: relative;
+    height: 72px;
+    border: 1px solid #ddd;
+    text-align: right;
+    overflow: hidden;
+    cursor: pointer;
+
+    -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055);
+    -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055);
+    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055);
+
+    -webkit-box-sizing: border-box;
+    -o-box-sizing: border-box;
+    -moz-box-sizing: border-box;
+    -ms-box-sizing: border-box;
+    box-sizing: border-box;
+}
+
+.try .tile:hover {
+    -webkit-box-shadow: 0 1px 5px rgba(0, 0, 0, 0.25);
+    -moz-box-shadow: 0 1px 5px rgba(0, 0, 0, 0.25);
+    box-shadow: 0 1px 5px rgba(0, 0, 0, 0.25);
+}
+
+.try .tile h4 {
+    margin: 0;
+    position: absolute;
+    right: 12px;
+    bottom: 12px;
+    z-index: 1;
+}
+
+.try .tile .glyph {
+    position: absolute;
+    left: 12px;
+    bottom: -6px;
+    background: url('../img/cloud.png') no-repeat;
+    background-size: 80px 43px;
+    height: 43px;
+    width: 80px;
+
+    opacity: .5;
+    transition: opacity .5s ease, bottom .5s ease;
+    -webkit-transition: opacity .5s ease, bottom .5s ease;
+}
+
+.try .tile:hover .glyph {
+    opacity: 1;
+    bottom: -2px;
+}
+
+.try .editor.row h4 small {
+    color: #555;
+}
+
+.try .editor.row .control-label {
+    display: inline-block;
+    position: relative;
+    top: -4px;
+    margin-right: 4px;
+}
+
+.try .editor.row .monaco-editor .find-widget input[type="text"] {
+    margin-bottom: 0;
+    -webkit-box-shadow: none;
+    -moz-box-shadow: none;
+    box-shadow: none;
+    -webkit-transition: none;
+    -moz-transition: none;
+    -o-transition: none;
+    transition: none;
+}
+
+.try .editor.row .monaco-editor .find-widget .monaco-checkbox .label {
+    min-height: 20px;
+    min-width: 20px;
+}
+
+.try .editor.row .monaco-editor .find-widget .close {
+    float: none;
+    opacity: 1;
+}
+
+.try .editor .editor-frame {
+    position: relative;
+}
+
+.try .editor .editor-frame #editor,
+.try .editor .editor-frame #diff-editor {
+    height: 400px;
+    margin-bottom: 10px;
+    border: 1px solid #eee;
+}
+
+.try .editor .editor-frame .loading {
+    position: absolute;
+    height: 100%;
+    width: 100%;
+    background-color: rgba(255, 255, 255, .5);
+    margin: 0 auto;
+    display: none;
+    z-index: 100;
+}
+
+.try .editor .editor-frame .progress {
+    width: 50%;
+    margin: 15% auto 0;
+}
+
+.try .editor .editor-frame .progress .bar {
+    width: 100%;
+    background-color: #4bb1cf;
+}
+
+.try .editor .editor-frame #editor .alert,
+.try .editor .editor-frame #diff-editor .alert {
+    margin: 18% auto 0;
+    width: 30%;
+    text-align: center;
+    color: #b94a48;
+    background-color: #f2dede;
+    border-color: #eed3d7;
+}
+
+.try .editor #editor .monaco-editor .row,
+.try .editor #diff-editor .monaco-editor .row {
+    margin-left: 0;
+}
+
+.try .editor .vs.monaco-editor .context-view.monaco-menu-container a {
+    color: #646465;
+}
+
+.try .editor .vs-dark.monaco-editor .context-view.monaco-menu-container a {
+    color: #BBB;
+}
+
+@media (min-width: 1200px) {
+    .try .editor .row {
+        margin-left: 0px;
+    }
+}
+
+@media (max-width: 767px) {
+    .try .container {
+        margin: 0 24px;
+    }
+
+    .try .tile h4 {
+        right: 6px;
+    }
+
+    .try .editor > .span9 .row .span4 {
+        float: left;
+        width: 50%;
+    }
+
+    .try .editor h4 {
+        margin-top: 0;
+    }
+}
+
+@media (max-width: 520px) {
+    .try .editor > .span3 p,
+    .try .editor > .span3 h4 {
+        display: none;
+    }
+}
+
+@media (max-width: 320px) {
+    .try .editor > .span9 {
+        display: none;
+    }
+}
+
+/* ------- BEGIN bootstrap fixes for the editor ------- */
+
+.monaco-editor .container:before, .monaco-editor .row:before {
+    content: "";
+    display: inherit;
+}
+
+.monaco-editor .container:after, .monaco-editor .row:after {
+    clear: inherit;
+}
+
+.monaco-editor .container {
+    width: auto;
+    margin: inherit;
+    padding: inherit;
+}
+
+.monaco-editor .close {
+    float: none;
+    font-size: inherit;
+    font-weight: inherit;
+    line-height: inherit;
+    color: inherit;
+    text-shadow: inherit;
+    opacity: inherit;
+    filter: inherit;
+}
+
+.monaco-editor .row {
+    margin: inherit;
+}
+
+.monaco-editor .invisible {
+    visibility: visible;
+}
+
+/* ------- END bootstrap fixes for the editor ------- */
diff --git a/website/index/index.js b/website/index/index.js
new file mode 100644
index 0000000..55c5c8f
--- /dev/null
+++ b/website/index/index.js
@@ -0,0 +1,144 @@
+/**
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Forked from https://microsoft.github.io/monaco-editor/ */
+
+"use strict";
+
+var editor = null;
+var style = "--kotlinlang-style";
+var columnLimit = 100;
+var options = null;
+
+require.config({ paths: { vs: "node_modules/monaco-editor/min/vs" } });
+
+$(document).ready(function () {
+  require(["vs/editor/editor.main"], function () {
+    editor = monaco.editor.create(document.getElementById("editor"), {
+      value: [
+        "/**",
+        " * Formats the given Javadoc comment, which must start with /** and end with */. The output will",
+        " * start and end with the same characters.",
+        " */",
+        "class Handler : RequestHandler<APIGatewayProxyRequestEvent, String> {",
+        "    override fun handleRequest(event: APIGatewayProxyRequestEvent, context: Context?): String {",
+        "        for ((i, item) in items.withIndex()) {",
+        "            if (needDot) {",
+        "                // fillMode is bla bla",
+        "                val fillMode =",
+        "                    if (unconsumedPrefixes.isNotEmpty() && i <= unconsumedPrefixes.peekFirst()) {",
+        "                        prefixFillMode /* bla bla */",
+        "                    } else {",
+        "                        Doc.FillMode.UNIFIED",
+        "                    }",
+        '                builder.breakOp(fillMode, "", ZERO, Optional.of(nameTag))',
+        "                builder.token((item as KtQualifiedExpression).operationSign.value)",
+        "            }",
+        "            emitSelectorUpToParenthesis(item)",
+        "            if (unconsumedPrefixes.isNotEmpty() && i == unconsumedPrefixes.peekFirst()) {",
+        "                builder.close()",
+        "                unconsumedPrefixes.removeFirst()",
+        "            }",
+        "            if (i == items.size - 1 && hasTrailingLambda) {",
+        "                builder.close()",
+        "            }",
+        "            val argsIndent =",
+        "                Indent.If.make(",
+        "                    nameTag,",
+        "                    expressionBreakIndent,",
+        "                    if (trailingDereferences) expressionBreakIndent else ZERO",
+        "                )",
+        "        }",
+        "    }",
+        "}",
+      ].join("\n"),
+      language: "kotlin",
+      rulers: [columnLimit],
+      scrollBeyondLastLine: false,
+    });
+  });
+  options = editor.getOptions();
+  window.onresize = function () {
+    editor.layout();
+  };
+
+  $(".style-picker").change(function () {
+    changeStyle(this.selectedIndex);
+  });
+
+  $(".column-limit-picker").change(function () {
+    columnLimit = parseInt(this.value);
+    editor.updateOptions({'rulers': [columnLimit]});
+    reformatEditor();
+  });
+
+  $("#editorForm").submit(function (event) {
+    event.preventDefault();
+    reformatEditor();
+  });
+});
+
+function changeStyle(newStyle) {
+  style = newStyle === 0 ? "--kotlinlang-style" : undefined;
+  reformatEditor();
+}
+
+function reformatEditor() {
+    disableDemoUi();
+  $.ajax({
+    type: "POST",
+    url: "https://8uj6xa47qa.execute-api.us-east-2.amazonaws.com/ktfmt",
+    contentType: "application/json",
+    data: JSON.stringify({
+      source: editor.getValue(),
+      maxWidth: columnLimit,
+      style: style,
+    }),
+    dataType: "json",
+    error: function (xhr, errorType, errorThrown) {
+      showError(errorThrown);
+    },
+  })
+    .done(function (data) {
+      if (data.err) {
+        showError(data.err);
+      } else {
+        $("#error-message").hide();
+        editor.setValue(data.source);
+      }
+    })
+    .always(function () {
+        enableDemoUi();
+    });
+}
+
+function disableDemoUi() {
+    $('.loading.editor').show();
+    $("#editorForm :input").prop("disabled", true);
+    editor.updateOptions({ readOnly: true, language: 'text' });
+}
+
+function enableDemoUi() {
+    $("#editorForm :input").prop("disabled", false);
+    editor.updateOptions({ readOnly: false });
+    $('.loading.editor').fadeOut({ duration: 300 });
+}
+
+function showError(error) {
+  var errorMessage = $("#error-message");
+  errorMessage.text("Error: " + error);
+  errorMessage.show();
+}
diff --git a/website/lib/bootstrap-cosmo.css b/website/lib/bootstrap-cosmo.css
new file mode 100644
index 0000000..4ee78a2
--- /dev/null
+++ b/website/lib/bootstrap-cosmo.css
@@ -0,0 +1,6794 @@
+/* Forked from https://microsoft.github.io/monaco-editor/ */
+
+/*!
+ * Bootstrap v2.3.0
+ *
+ * Copyright 2012 Twitter, Inc
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Designed and built with all the love in the world @twitter by @mdo and @fat.
+ */
+
+.clearfix {
+  *zoom: 1;
+}
+
+.clearfix:before,
+.clearfix:after {
+  display: table;
+  line-height: 0;
+  content: "";
+}
+
+.clearfix:after {
+  clear: both;
+}
+
+.hide-text {
+  font: 0/0 a;
+  color: transparent;
+  text-shadow: none;
+  background-color: transparent;
+  border: 0;
+}
+
+.input-block-level {
+  display: block;
+  width: 100%;
+  min-height: 30px;
+  -webkit-box-sizing: border-box;
+     -moz-box-sizing: border-box;
+          box-sizing: border-box;
+}
+
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+nav,
+section {
+  display: block;
+}
+
+audio,
+canvas,
+video {
+  display: inline-block;
+  *display: inline;
+  *zoom: 1;
+}
+
+audio:not([controls]) {
+  display: none;
+}
+
+html {
+  font-size: 100%;
+  -webkit-text-size-adjust: 100%;
+      -ms-text-size-adjust: 100%;
+}
+
+a:focus {
+  outline: thin dotted #333;
+  outline: 5px auto -webkit-focus-ring-color;
+  outline-offset: -2px;
+}
+
+a:hover,
+a:active {
+  outline: 0;
+}
+
+sub,
+sup {
+  position: relative;
+  font-size: 75%;
+  line-height: 0;
+  vertical-align: baseline;
+}
+
+sup {
+  top: -0.5em;
+}
+
+sub {
+  bottom: -0.25em;
+}
+
+img {
+  width: auto\9;
+  height: auto;
+  max-width: 100%;
+  vertical-align: middle;
+  border: 0;
+  -ms-interpolation-mode: bicubic;
+}
+
+#map_canvas img,
+.google-maps img {
+  max-width: none;
+}
+
+button,
+input,
+select,
+textarea {
+  margin: 0;
+  font-size: 100%;
+  vertical-align: middle;
+}
+
+button,
+input {
+  *overflow: visible;
+  line-height: normal;
+}
+
+button::-moz-focus-inner,
+input::-moz-focus-inner {
+  padding: 0;
+  border: 0;
+}
+
+button,
+html input[type="button"],
+input[type="reset"],
+input[type="submit"] {
+  cursor: pointer;
+  -webkit-appearance: button;
+}
+
+label,
+select,
+button,
+input[type="button"],
+input[type="reset"],
+input[type="submit"],
+input[type="radio"],
+input[type="checkbox"] {
+  cursor: pointer;
+}
+
+input[type="search"] {
+  -webkit-box-sizing: content-box;
+     -moz-box-sizing: content-box;
+          box-sizing: content-box;
+  -webkit-appearance: textfield;
+}
+
+input[type="search"]::-webkit-search-decoration,
+input[type="search"]::-webkit-search-cancel-button {
+  -webkit-appearance: none;
+}
+
+textarea {
+  overflow: auto;
+  vertical-align: top;
+}
+
+@media print {
+  * {
+    color: #000 !important;
+    text-shadow: none !important;
+    background: transparent !important;
+    box-shadow: none !important;
+  }
+  a,
+  a:visited {
+    text-decoration: underline;
+  }
+  a[href]:after {
+    content: " (" attr(href) ")";
+  }
+  abbr[title]:after {
+    content: " (" attr(title) ")";
+  }
+  .ir a:after,
+  a[href^="javascript:"]:after,
+  a[href^="#"]:after {
+    content: "";
+  }
+  pre,
+  blockquote {
+    border: 1px solid #999;
+    page-break-inside: avoid;
+  }
+  thead {
+    display: table-header-group;
+  }
+  tr,
+  img {
+    page-break-inside: avoid;
+  }
+  img {
+    max-width: 100% !important;
+  }
+  @page  {
+    margin: 0.5cm;
+  }
+  p,
+  h2,
+  h3 {
+    orphans: 3;
+    widows: 3;
+  }
+  h2,
+  h3 {
+    page-break-after: avoid;
+  }
+}
+
+body {
+  margin: 0;
+  line-height: 20px;
+  color: #555555;
+  background-color: #ffffff;
+}
+
+a {
+  color: #007fff;
+  text-decoration: none;
+}
+
+a:hover,
+a:focus {
+  color: #0066cc;
+  text-decoration: underline;
+}
+
+.img-rounded {
+  -webkit-border-radius: 6px;
+     -moz-border-radius: 6px;
+          border-radius: 6px;
+}
+
+.img-polaroid {
+  padding: 4px;
+  background-color: #fff;
+  border: 1px solid #ccc;
+  border: 1px solid rgba(0, 0, 0, 0.2);
+  -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+     -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+          box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+}
+
+.img-circle {
+  -webkit-border-radius: 500px;
+     -moz-border-radius: 500px;
+          border-radius: 500px;
+}
+
+.row {
+  margin-left: -20px;
+  *zoom: 1;
+}
+
+.row:before,
+.row:after {
+  display: table;
+  line-height: 0;
+  content: "";
+}
+
+.row:after {
+  clear: both;
+}
+
+[class*="span"] {
+  float: left;
+  min-height: 1px;
+  margin-left: 20px;
+}
+
+.container,
+.navbar-static-top .container,
+.navbar-fixed-top .container,
+.navbar-fixed-bottom .container {
+  width: 940px;
+}
+
+.span12 {
+  width: 940px;
+}
+
+.span11 {
+  width: 860px;
+}
+
+.span10 {
+  width: 780px;
+}
+
+.span9 {
+  width: 700px;
+}
+
+.span8 {
+  width: 620px;
+}
+
+.span7 {
+  width: 540px;
+}
+
+.span6 {
+  width: 460px;
+}
+
+.span5 {
+  width: 380px;
+}
+
+.span4 {
+  width: 300px;
+}
+
+.span3 {
+  width: 220px;
+}
+
+.span2 {
+  width: 140px;
+}
+
+.span1 {
+  width: 60px;
+}
+
+.offset12 {
+  margin-left: 980px;
+}
+
+.offset11 {
+  margin-left: 900px;
+}
+
+.offset10 {
+  margin-left: 820px;
+}
+
+.offset9 {
+  margin-left: 740px;
+}
+
+.offset8 {
+  margin-left: 660px;
+}
+
+.offset7 {
+  margin-left: 580px;
+}
+
+.offset6 {
+  margin-left: 500px;
+}
+
+.offset5 {
+  margin-left: 420px;
+}
+
+.offset4 {
+  margin-left: 340px;
+}
+
+.offset3 {
+  margin-left: 260px;
+}
+
+.offset2 {
+  margin-left: 180px;
+}
+
+.offset1 {
+  margin-left: 100px;
+}
+
+.row-fluid {
+  width: 100%;
+  *zoom: 1;
+}
+
+.row-fluid:before,
+.row-fluid:after {
+  display: table;
+  line-height: 0;
+  content: "";
+}
+
+.row-fluid:after {
+  clear: both;
+}
+
+.row-fluid [class*="span"] {
+  display: block;
+  float: left;
+  width: 100%;
+  min-height: 30px;
+  margin-left: 2.127659574468085%;
+  *margin-left: 2.074468085106383%;
+  -webkit-box-sizing: border-box;
+     -moz-box-sizing: border-box;
+          box-sizing: border-box;
+}
+
+.row-fluid [class*="span"]:first-child {
+  margin-left: 0;
+}
+
+.row-fluid .controls-row [class*="span"] + [class*="span"] {
+  margin-left: 2.127659574468085%;
+}
+
+.row-fluid .span12 {
+  width: 100%;
+  *width: 99.94680851063829%;
+}
+
+.row-fluid .span11 {
+  width: 91.48936170212765%;
+  *width: 91.43617021276594%;
+}
+
+.row-fluid .span10 {
+  width: 82.97872340425532%;
+  *width: 82.92553191489361%;
+}
+
+.row-fluid .span9 {
+  width: 74.46808510638297%;
+  *width: 74.41489361702126%;
+}
+
+.row-fluid .span8 {
+  width: 65.95744680851064%;
+  *width: 65.90425531914893%;
+}
+
+.row-fluid .span7 {
+  width: 57.44680851063829%;
+  *width: 57.39361702127659%;
+}
+
+.row-fluid .span6 {
+  width: 48.93617021276595%;
+  *width: 48.88297872340425%;
+}
+
+.row-fluid .span5 {
+  width: 40.42553191489362%;
+  *width: 40.37234042553192%;
+}
+
+.row-fluid .span4 {
+  width: 31.914893617021278%;
+  *width: 31.861702127659576%;
+}
+
+.row-fluid .span3 {
+  width: 23.404255319148934%;
+  *width: 23.351063829787233%;
+}
+
+.row-fluid .span2 {
+  width: 14.893617021276595%;
+  *width: 14.840425531914894%;
+}
+
+.row-fluid .span1 {
+  width: 6.382978723404255%;
+  *width: 6.329787234042553%;
+}
+
+.row-fluid .offset12 {
+  margin-left: 104.25531914893617%;
+  *margin-left: 104.14893617021275%;
+}
+
+.row-fluid .offset12:first-child {
+  margin-left: 102.12765957446808%;
+  *margin-left: 102.02127659574467%;
+}
+
+.row-fluid .offset11 {
+  margin-left: 95.74468085106382%;
+  *margin-left: 95.6382978723404%;
+}
+
+.row-fluid .offset11:first-child {
+  margin-left: 93.61702127659574%;
+  *margin-left: 93.51063829787232%;
+}
+
+.row-fluid .offset10 {
+  margin-left: 87.23404255319149%;
+  *margin-left: 87.12765957446807%;
+}
+
+.row-fluid .offset10:first-child {
+  margin-left: 85.1063829787234%;
+  *margin-left: 84.99999999999999%;
+}
+
+.row-fluid .offset9 {
+  margin-left: 78.72340425531914%;
+  *margin-left: 78.61702127659572%;
+}
+
+.row-fluid .offset9:first-child {
+  margin-left: 76.59574468085106%;
+  *margin-left: 76.48936170212764%;
+}
+
+.row-fluid .offset8 {
+  margin-left: 70.2127659574468%;
+  *margin-left: 70.10638297872339%;
+}
+
+.row-fluid .offset8:first-child {
+  margin-left: 68.08510638297872%;
+  *margin-left: 67.9787234042553%;
+}
+
+.row-fluid .offset7 {
+  margin-left: 61.70212765957446%;
+  *margin-left: 61.59574468085106%;
+}
+
+.row-fluid .offset7:first-child {
+  margin-left: 59.574468085106375%;
+  *margin-left: 59.46808510638297%;
+}
+
+.row-fluid .offset6 {
+  margin-left: 53.191489361702125%;
+  *margin-left: 53.085106382978715%;
+}
+
+.row-fluid .offset6:first-child {
+  margin-left: 51.063829787234035%;
+  *margin-left: 50.95744680851063%;
+}
+
+.row-fluid .offset5 {
+  margin-left: 44.68085106382979%;
+  *margin-left: 44.57446808510638%;
+}
+
+.row-fluid .offset5:first-child {
+  margin-left: 42.5531914893617%;
+  *margin-left: 42.4468085106383%;
+}
+
+.row-fluid .offset4 {
+  margin-left: 36.170212765957444%;
+  *margin-left: 36.06382978723405%;
+}
+
+.row-fluid .offset4:first-child {
+  margin-left: 34.04255319148936%;
+  *margin-left: 33.93617021276596%;
+}
+
+.row-fluid .offset3 {
+  margin-left: 27.659574468085104%;
+  *margin-left: 27.5531914893617%;
+}
+
+.row-fluid .offset3:first-child {
+  margin-left: 25.53191489361702%;
+  *margin-left: 25.425531914893618%;
+}
+
+.row-fluid .offset2 {
+  margin-left: 19.148936170212764%;
+  *margin-left: 19.04255319148936%;
+}
+
+.row-fluid .offset2:first-child {
+  margin-left: 17.02127659574468%;
+  *margin-left: 16.914893617021278%;
+}
+
+.row-fluid .offset1 {
+  margin-left: 10.638297872340425%;
+  *margin-left: 10.53191489361702%;
+}
+
+.row-fluid .offset1:first-child {
+  margin-left: 8.51063829787234%;
+  *margin-left: 8.404255319148938%;
+}
+
+[class*="span"].hide,
+.row-fluid [class*="span"].hide {
+  display: none;
+}
+
+[class*="span"].pull-right,
+.row-fluid [class*="span"].pull-right {
+  float: right;
+}
+
+.container {
+  margin-right: auto;
+  margin-left: auto;
+  *zoom: 1;
+}
+
+.container:before,
+.container:after {
+  display: table;
+  line-height: 0;
+  content: "";
+}
+
+.container:after {
+  clear: both;
+}
+
+.container-fluid {
+  padding-right: 20px;
+  padding-left: 20px;
+  *zoom: 1;
+}
+
+.container-fluid:before,
+.container-fluid:after {
+  display: table;
+  line-height: 0;
+  content: "";
+}
+
+.container-fluid:after {
+  clear: both;
+}
+
+p {
+  margin: 0 0 10px;
+}
+
+.lead {
+  margin-bottom: 20px;
+  font-size: 21px;
+  font-weight: 200;
+  line-height: 30px;
+}
+
+small {
+  font-size: 85%;
+}
+
+strong {
+  font-weight: bold;
+}
+
+em {
+  font-style: italic;
+}
+
+cite {
+  font-style: normal;
+}
+
+.muted {
+  color: #dfdfdf;
+}
+
+a.muted:hover,
+a.muted:focus {
+  color: #c6c6c6;
+}
+
+.text-warning {
+  color: #ffffff;
+}
+
+a.text-warning:hover,
+a.text-warning:focus {
+  color: #e6e6e6;
+}
+
+.text-error {
+  color: #ffffff;
+}
+
+a.text-error:hover,
+a.text-error:focus {
+  color: #e6e6e6;
+}
+
+.text-info {
+  color: #ffffff;
+}
+
+a.text-info:hover,
+a.text-info:focus {
+  color: #e6e6e6;
+}
+
+.text-success {
+  color: #ffffff;
+}
+
+a.text-success:hover,
+a.text-success:focus {
+  color: #e6e6e6;
+}
+
+.text-left {
+  text-align: left;
+}
+
+.text-right {
+  text-align: right;
+}
+
+.text-center {
+  text-align: center;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+  margin: 10px 0;
+  font-family: inherit;
+  font-weight: 300;
+  line-height: 20px;
+  color: #080808;
+  text-rendering: optimizelegibility;
+}
+
+h1 small,
+h2 small,
+h3 small,
+h4 small,
+h5 small,
+h6 small {
+  font-weight: normal;
+  line-height: 1;
+  color: #dfdfdf;
+}
+
+h1,
+h2,
+h3 {
+  line-height: 40px;
+}
+
+h1 {
+  font-size: 38.5px;
+}
+
+h2 {
+  font-size: 31.5px;
+}
+
+h3 {
+  font-size: 24.5px;
+}
+
+h4 {
+  font-size: 17.5px;
+}
+
+h5 {
+  font-size: 14px;
+}
+
+h6 {
+  font-size: 11.9px;
+}
+
+h1 small {
+  font-size: 24.5px;
+}
+
+h2 small {
+  font-size: 17.5px;
+}
+
+h3 small {
+  font-size: 14px;
+}
+
+h4 small {
+  font-size: 14px;
+}
+
+.page-header {
+  padding-bottom: 9px;
+  margin: 20px 0 30px;
+  border-bottom: 1px solid #eeeeee;
+}
+
+ul,
+ol {
+  padding: 0;
+  margin: 0 0 10px 25px;
+}
+
+ul ul,
+ul ol,
+ol ol,
+ol ul {
+  margin-bottom: 0;
+}
+
+li {
+  line-height: 20px;
+}
+
+ul.unstyled,
+ol.unstyled {
+  margin-left: 0;
+  list-style: none;
+}
+
+ul.inline,
+ol.inline {
+  margin-left: 0;
+  list-style: none;
+}
+
+ul.inline > li,
+ol.inline > li {
+  display: inline-block;
+  *display: inline;
+  padding-right: 5px;
+  padding-left: 5px;
+  *zoom: 1;
+}
+
+dl {
+  margin-bottom: 20px;
+}
+
+dt,
+dd {
+  line-height: 20px;
+}
+
+dt {
+  font-weight: bold;
+}
+
+dd {
+  margin-left: 10px;
+}
+
+.dl-horizontal {
+  *zoom: 1;
+}
+
+.dl-horizontal:before,
+.dl-horizontal:after {
+  display: table;
+  line-height: 0;
+  content: "";
+}
+
+.dl-horizontal:after {
+  clear: both;
+}
+
+.dl-horizontal dt {
+  float: left;
+  width: 160px;
+  overflow: hidden;
+  clear: left;
+  text-align: right;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.dl-horizontal dd {
+  margin-left: 180px;
+}
+
+hr {
+  margin: 20px 0;
+  border: 0;
+  border-top: 1px solid #eeeeee;
+  border-bottom: 1px solid #ffffff;
+}
+
+abbr[title],
+abbr[data-original-title] {
+  cursor: help;
+  border-bottom: 1px dotted #dfdfdf;
+}
+
+abbr.initialism {
+  font-size: 90%;
+  text-transform: uppercase;
+}
+
+blockquote {
+  padding: 0 0 0 15px;
+  margin: 0 0 20px;
+  border-left: 5px solid #eeeeee;
+}
+
+blockquote p {
+  margin-bottom: 0;
+  font-size: 17.5px;
+  font-weight: 300;
+  line-height: 1.25;
+}
+
+blockquote small {
+  display: block;
+  line-height: 20px;
+  color: #dfdfdf;
+}
+
+blockquote small:before {
+  content: '\2014 \00A0';
+}
+
+blockquote.pull-right {
+  float: right;
+  padding-right: 15px;
+  padding-left: 0;
+  border-right: 5px solid #eeeeee;
+  border-left: 0;
+}
+
+blockquote.pull-right p,
+blockquote.pull-right small {
+  text-align: right;
+}
+
+blockquote.pull-right small:before {
+  content: '';
+}
+
+blockquote.pull-right small:after {
+  content: '\00A0 \2014';
+}
+
+q:before,
+q:after,
+blockquote:before,
+blockquote:after {
+  content: "";
+}
+
+address {
+  display: block;
+  margin-bottom: 20px;
+  font-style: normal;
+  line-height: 20px;
+}
+
+code,
+pre {
+  padding: 0 3px 2px;
+  font-family: Monaco, Menlo, Consolas, "Courier New", monospace;
+  font-size: 12px;
+  /* color: #999999; */
+  -webkit-border-radius: 3px;
+     -moz-border-radius: 3px;
+          border-radius: 3px;
+}
+
+code {
+  padding: 2px 4px;
+  color: #d14;
+  white-space: nowrap;
+  background-color: #f7f7f9;
+  border: 1px solid #e1e1e8;
+}
+
+pre {
+  display: block;
+  padding: 9.5px;
+  margin: 0 0 10px;
+  font-size: 13px;
+  line-height: 20px;
+  word-break: break-all;
+  word-wrap: break-word;
+  white-space: pre;
+  white-space: pre-wrap;
+  background-color: #f5f5f5;
+  border: 1px solid #ccc;
+  border: 1px solid rgba(0, 0, 0, 0.15);
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+}
+
+pre.prettyprint {
+  margin-bottom: 20px;
+}
+
+pre code {
+  padding: 0;
+  color: inherit;
+  white-space: pre;
+  white-space: pre-wrap;
+  background-color: transparent;
+  border: 0;
+}
+
+.pre-scrollable {
+  max-height: 340px;
+  overflow-y: scroll;
+}
+
+form {
+  margin: 0 0 20px;
+}
+
+fieldset {
+  padding: 0;
+  margin: 0;
+  border: 0;
+}
+
+legend {
+  display: block;
+  width: 100%;
+  padding: 0;
+  margin-bottom: 20px;
+  font-size: 21px;
+  line-height: 40px;
+  color: #999999;
+  border: 0;
+  border-bottom: 1px solid #e5e5e5;
+}
+
+legend small {
+  font-size: 15px;
+  color: #dfdfdf;
+}
+
+label,
+input,
+button,
+select,
+textarea {
+  font-size: 14px;
+  font-weight: normal;
+  line-height: 20px;
+}
+
+input,
+button,
+select,
+textarea {
+  font-family: "Open Sans", Calibri, Candara, Arial, sans-serif;
+}
+
+label {
+  display: block;
+  margin-bottom: 5px;
+}
+
+select,
+textarea,
+input[type="text"],
+input[type="password"],
+input[type="datetime"],
+input[type="datetime-local"],
+input[type="date"],
+input[type="month"],
+input[type="time"],
+input[type="week"],
+input[type="number"],
+input[type="email"],
+input[type="url"],
+input[type="search"],
+input[type="tel"],
+input[type="color"],
+.uneditable-input {
+  display: inline-block;
+  height: 20px;
+  padding: 4px 6px;
+  margin-bottom: 10px;
+  font-size: 14px;
+  line-height: 20px;
+  color: #bbbbbb;
+  vertical-align: middle;
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+}
+
+input,
+textarea,
+.uneditable-input {
+  width: 206px;
+}
+
+textarea {
+  height: auto;
+}
+
+textarea,
+input[type="text"],
+input[type="password"],
+input[type="datetime"],
+input[type="datetime-local"],
+input[type="date"],
+input[type="month"],
+input[type="time"],
+input[type="week"],
+input[type="number"],
+input[type="email"],
+input[type="url"],
+input[type="search"],
+input[type="tel"],
+input[type="color"],
+.uneditable-input {
+  background-color: #ffffff;
+  border: 1px solid #bbbbbb;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+     -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+  -webkit-transition: border linear 0.2s, box-shadow linear 0.2s;
+     -moz-transition: border linear 0.2s, box-shadow linear 0.2s;
+       -o-transition: border linear 0.2s, box-shadow linear 0.2s;
+          transition: border linear 0.2s, box-shadow linear 0.2s;
+}
+
+textarea:focus,
+input[type="text"]:focus,
+input[type="password"]:focus,
+input[type="datetime"]:focus,
+input[type="datetime-local"]:focus,
+input[type="date"]:focus,
+input[type="month"]:focus,
+input[type="time"]:focus,
+input[type="week"]:focus,
+input[type="number"]:focus,
+input[type="email"]:focus,
+input[type="url"]:focus,
+input[type="search"]:focus,
+input[type="tel"]:focus,
+input[type="color"]:focus,
+.uneditable-input:focus {
+  border-color: rgba(82, 168, 236, 0.8);
+  outline: 0;
+  outline: thin dotted \9;
+  /* IE6-9 */
+
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
+     -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
+}
+
+input[type="radio"],
+input[type="checkbox"] {
+  margin: 4px 0 0;
+  margin-top: 1px \9;
+  *margin-top: 0;
+  line-height: normal;
+}
+
+input[type="file"],
+input[type="image"],
+input[type="submit"],
+input[type="reset"],
+input[type="button"],
+input[type="radio"],
+input[type="checkbox"] {
+  width: auto;
+}
+
+select,
+input[type="file"] {
+  height: 30px;
+  /* In IE7, the height of the select element cannot be changed by height, only font-size */
+
+  *margin-top: 4px;
+  /* For IE7, add top margin to align select with labels */
+
+  line-height: 30px;
+}
+
+select {
+  width: 220px;
+  background-color: #ffffff;
+  border: 1px solid #bbbbbb;
+}
+
+select[multiple],
+select[size] {
+  height: auto;
+}
+
+select:focus,
+input[type="file"]:focus,
+input[type="radio"]:focus,
+input[type="checkbox"]:focus {
+  outline: thin dotted #333;
+  outline: 5px auto -webkit-focus-ring-color;
+  outline-offset: -2px;
+}
+
+.uneditable-input,
+.uneditable-textarea {
+  color: #dfdfdf;
+  cursor: not-allowed;
+  background-color: #fcfcfc;
+  border-color: #bbbbbb;
+  -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
+     -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
+          box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.025);
+}
+
+.uneditable-input {
+  overflow: hidden;
+  white-space: nowrap;
+}
+
+.uneditable-textarea {
+  width: auto;
+  height: auto;
+}
+
+input:-moz-placeholder,
+textarea:-moz-placeholder {
+  color: #bbbbbb;
+}
+
+input:-ms-input-placeholder,
+textarea:-ms-input-placeholder {
+  color: #bbbbbb;
+}
+
+input::-webkit-input-placeholder,
+textarea::-webkit-input-placeholder {
+  color: #bbbbbb;
+}
+
+.radio,
+.checkbox {
+  min-height: 20px;
+  padding-left: 20px;
+}
+
+.radio input[type="radio"],
+.checkbox input[type="checkbox"] {
+  float: left;
+  margin-left: -20px;
+}
+
+.controls > .radio:first-child,
+.controls > .checkbox:first-child {
+  padding-top: 5px;
+}
+
+.radio.inline,
+.checkbox.inline {
+  display: inline-block;
+  padding-top: 5px;
+  margin-bottom: 0;
+  vertical-align: middle;
+}
+
+.radio.inline + .radio.inline,
+.checkbox.inline + .checkbox.inline {
+  margin-left: 10px;
+}
+
+.input-mini {
+  width: 60px;
+}
+
+.input-small {
+  width: 90px;
+}
+
+.input-medium {
+  width: 150px;
+}
+
+.input-large {
+  width: 210px;
+}
+
+.input-xlarge {
+  width: 270px;
+}
+
+.input-xxlarge {
+  width: 530px;
+}
+
+input[class*="span"],
+select[class*="span"],
+textarea[class*="span"],
+.uneditable-input[class*="span"],
+.row-fluid input[class*="span"],
+.row-fluid select[class*="span"],
+.row-fluid textarea[class*="span"],
+.row-fluid .uneditable-input[class*="span"] {
+  float: none;
+  margin-left: 0;
+}
+
+.input-append input[class*="span"],
+.input-append .uneditable-input[class*="span"],
+.input-prepend input[class*="span"],
+.input-prepend .uneditable-input[class*="span"],
+.row-fluid input[class*="span"],
+.row-fluid select[class*="span"],
+.row-fluid textarea[class*="span"],
+.row-fluid .uneditable-input[class*="span"],
+.row-fluid .input-prepend [class*="span"],
+.row-fluid .input-append [class*="span"] {
+  display: inline-block;
+}
+
+input,
+textarea,
+.uneditable-input {
+  margin-left: 0;
+}
+
+.controls-row [class*="span"] + [class*="span"] {
+  margin-left: 20px;
+}
+
+input.span12,
+textarea.span12,
+.uneditable-input.span12 {
+  width: 926px;
+}
+
+input.span11,
+textarea.span11,
+.uneditable-input.span11 {
+  width: 846px;
+}
+
+input.span10,
+textarea.span10,
+.uneditable-input.span10 {
+  width: 766px;
+}
+
+input.span9,
+textarea.span9,
+.uneditable-input.span9 {
+  width: 686px;
+}
+
+input.span8,
+textarea.span8,
+.uneditable-input.span8 {
+  width: 606px;
+}
+
+input.span7,
+textarea.span7,
+.uneditable-input.span7 {
+  width: 526px;
+}
+
+input.span6,
+textarea.span6,
+.uneditable-input.span6 {
+  width: 446px;
+}
+
+input.span5,
+textarea.span5,
+.uneditable-input.span5 {
+  width: 366px;
+}
+
+input.span4,
+textarea.span4,
+.uneditable-input.span4 {
+  width: 286px;
+}
+
+input.span3,
+textarea.span3,
+.uneditable-input.span3 {
+  width: 206px;
+}
+
+input.span2,
+textarea.span2,
+.uneditable-input.span2 {
+  width: 126px;
+}
+
+input.span1,
+textarea.span1,
+.uneditable-input.span1 {
+  width: 46px;
+}
+
+.controls-row {
+  *zoom: 1;
+}
+
+.controls-row:before,
+.controls-row:after {
+  display: table;
+  line-height: 0;
+  content: "";
+}
+
+.controls-row:after {
+  clear: both;
+}
+
+.controls-row [class*="span"],
+.row-fluid .controls-row [class*="span"] {
+  float: left;
+}
+
+.controls-row .checkbox[class*="span"],
+.controls-row .radio[class*="span"] {
+  padding-top: 5px;
+}
+
+input[disabled],
+select[disabled],
+textarea[disabled],
+input[readonly],
+select[readonly],
+textarea[readonly] {
+  cursor: not-allowed;
+  background-color: #eeeeee;
+}
+
+input[type="radio"][disabled],
+input[type="checkbox"][disabled],
+input[type="radio"][readonly],
+input[type="checkbox"][readonly] {
+  background-color: transparent;
+}
+
+.control-group.warning .control-label,
+.control-group.warning .help-block,
+.control-group.warning .help-inline {
+  color: #ffffff;
+}
+
+.control-group.warning .checkbox,
+.control-group.warning .radio,
+.control-group.warning input,
+.control-group.warning select,
+.control-group.warning textarea {
+  color: #ffffff;
+}
+
+.control-group.warning input,
+.control-group.warning select,
+.control-group.warning textarea {
+  border-color: #ffffff;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+     -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+}
+
+.control-group.warning input:focus,
+.control-group.warning select:focus,
+.control-group.warning textarea:focus {
+  border-color: #e6e6e6;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ffffff;
+     -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ffffff;
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ffffff;
+}
+
+.control-group.warning .input-prepend .add-on,
+.control-group.warning .input-append .add-on {
+  color: #ffffff;
+  background-color: #ff7518;
+  border-color: #ffffff;
+}
+
+.control-group.error .control-label,
+.control-group.error .help-block,
+.control-group.error .help-inline {
+  color: #ffffff;
+}
+
+.control-group.error .checkbox,
+.control-group.error .radio,
+.control-group.error input,
+.control-group.error select,
+.control-group.error textarea {
+  color: #ffffff;
+}
+
+.control-group.error input,
+.control-group.error select,
+.control-group.error textarea {
+  border-color: #ffffff;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+     -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+}
+
+.control-group.error input:focus,
+.control-group.error select:focus,
+.control-group.error textarea:focus {
+  border-color: #e6e6e6;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ffffff;
+     -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ffffff;
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ffffff;
+}
+
+.control-group.error .input-prepend .add-on,
+.control-group.error .input-append .add-on {
+  color: #ffffff;
+  background-color: #ff0039;
+  border-color: #ffffff;
+}
+
+.control-group.success .control-label,
+.control-group.success .help-block,
+.control-group.success .help-inline {
+  color: #ffffff;
+}
+
+.control-group.success .checkbox,
+.control-group.success .radio,
+.control-group.success input,
+.control-group.success select,
+.control-group.success textarea {
+  color: #ffffff;
+}
+
+.control-group.success input,
+.control-group.success select,
+.control-group.success textarea {
+  border-color: #ffffff;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+     -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+}
+
+.control-group.success input:focus,
+.control-group.success select:focus,
+.control-group.success textarea:focus {
+  border-color: #e6e6e6;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ffffff;
+     -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ffffff;
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ffffff;
+}
+
+.control-group.success .input-prepend .add-on,
+.control-group.success .input-append .add-on {
+  color: #ffffff;
+  background-color: #3fb618;
+  border-color: #ffffff;
+}
+
+.control-group.info .control-label,
+.control-group.info .help-block,
+.control-group.info .help-inline {
+  color: #ffffff;
+}
+
+.control-group.info .checkbox,
+.control-group.info .radio,
+.control-group.info input,
+.control-group.info select,
+.control-group.info textarea {
+  color: #ffffff;
+}
+
+.control-group.info input,
+.control-group.info select,
+.control-group.info textarea {
+  border-color: #ffffff;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+     -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
+}
+
+.control-group.info input:focus,
+.control-group.info select:focus,
+.control-group.info textarea:focus {
+  border-color: #e6e6e6;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ffffff;
+     -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ffffff;
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ffffff;
+}
+
+.control-group.info .input-prepend .add-on,
+.control-group.info .input-append .add-on {
+  color: #ffffff;
+  background-color: #9954bb;
+  border-color: #ffffff;
+}
+
+input:focus:invalid,
+textarea:focus:invalid,
+select:focus:invalid {
+  color: #b94a48;
+  border-color: #ee5f5b;
+}
+
+input:focus:invalid:focus,
+textarea:focus:invalid:focus,
+select:focus:invalid:focus {
+  border-color: #e9322d;
+  -webkit-box-shadow: 0 0 6px #f8b9b7;
+     -moz-box-shadow: 0 0 6px #f8b9b7;
+          box-shadow: 0 0 6px #f8b9b7;
+}
+
+.form-actions {
+  padding: 19px 20px 20px;
+  margin-top: 20px;
+  margin-bottom: 20px;
+  background-color: #f5f5f5;
+  border-top: 1px solid #e5e5e5;
+  *zoom: 1;
+}
+
+.form-actions:before,
+.form-actions:after {
+  display: table;
+  line-height: 0;
+  content: "";
+}
+
+.form-actions:after {
+  clear: both;
+}
+
+.help-block,
+.help-inline {
+  color: #7b7b7b;
+}
+
+.help-block {
+  display: block;
+  margin-bottom: 10px;
+}
+
+.help-inline {
+  display: inline-block;
+  *display: inline;
+  padding-left: 5px;
+  vertical-align: middle;
+  *zoom: 1;
+}
+
+.input-append,
+.input-prepend {
+  display: inline-block;
+  margin-bottom: 10px;
+  font-size: 0;
+  white-space: nowrap;
+  vertical-align: middle;
+}
+
+.input-append input,
+.input-prepend input,
+.input-append select,
+.input-prepend select,
+.input-append .uneditable-input,
+.input-prepend .uneditable-input,
+.input-append .dropdown-menu,
+.input-prepend .dropdown-menu,
+.input-append .popover,
+.input-prepend .popover {
+  font-size: 14px;
+}
+
+.input-append input,
+.input-prepend input,
+.input-append select,
+.input-prepend select,
+.input-append .uneditable-input,
+.input-prepend .uneditable-input {
+  position: relative;
+  margin-bottom: 0;
+  *margin-left: 0;
+  vertical-align: top;
+  -webkit-border-radius: 0 0 0px 0;
+     -moz-border-radius: 0 0 0px 0;
+          border-radius: 0 0 0px 0;
+}
+
+.input-append input:focus,
+.input-prepend input:focus,
+.input-append select:focus,
+.input-prepend select:focus,
+.input-append .uneditable-input:focus,
+.input-prepend .uneditable-input:focus {
+  z-index: 2;
+}
+
+.input-append .add-on,
+.input-prepend .add-on {
+  display: inline-block;
+  width: auto;
+  height: 20px;
+  min-width: 16px;
+  padding: 4px 5px;
+  font-size: 14px;
+  font-weight: normal;
+  line-height: 20px;
+  text-align: center;
+  text-shadow: 0 1px 0 #ffffff;
+  background-color: #eeeeee;
+  border: 1px solid #ccc;
+}
+
+.input-append .add-on,
+.input-prepend .add-on,
+.input-append .btn,
+.input-prepend .btn,
+.input-append .btn-group > .dropdown-toggle,
+.input-prepend .btn-group > .dropdown-toggle {
+  vertical-align: top;
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+}
+
+.input-append .active,
+.input-prepend .active {
+  background-color: #96ed7a;
+  border-color: #3fb618;
+}
+
+.input-prepend .add-on,
+.input-prepend .btn {
+  margin-right: -1px;
+}
+
+.input-prepend .add-on:first-child,
+.input-prepend .btn:first-child {
+  -webkit-border-radius: 0 0 0 0px;
+     -moz-border-radius: 0 0 0 0px;
+          border-radius: 0 0 0 0px;
+}
+
+.input-append input,
+.input-append select,
+.input-append .uneditable-input {
+  -webkit-border-radius: 0 0 0 0px;
+     -moz-border-radius: 0 0 0 0px;
+          border-radius: 0 0 0 0px;
+}
+
+.input-append input + .btn-group .btn:last-child,
+.input-append select + .btn-group .btn:last-child,
+.input-append .uneditable-input + .btn-group .btn:last-child {
+  -webkit-border-radius: 0 0 0px 0;
+     -moz-border-radius: 0 0 0px 0;
+          border-radius: 0 0 0px 0;
+}
+
+.input-append .add-on,
+.input-append .btn,
+.input-append .btn-group {
+  margin-left: -1px;
+}
+
+.input-append .add-on:last-child,
+.input-append .btn:last-child,
+.input-append .btn-group:last-child > .dropdown-toggle {
+  -webkit-border-radius: 0 0 0px 0;
+     -moz-border-radius: 0 0 0px 0;
+          border-radius: 0 0 0px 0;
+}
+
+.input-prepend.input-append input,
+.input-prepend.input-append select,
+.input-prepend.input-append .uneditable-input {
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+}
+
+.input-prepend.input-append input + .btn-group .btn,
+.input-prepend.input-append select + .btn-group .btn,
+.input-prepend.input-append .uneditable-input + .btn-group .btn {
+  -webkit-border-radius: 0 0 0px 0;
+     -moz-border-radius: 0 0 0px 0;
+          border-radius: 0 0 0px 0;
+}
+
+.input-prepend.input-append .add-on:first-child,
+.input-prepend.input-append .btn:first-child {
+  margin-right: -1px;
+  -webkit-border-radius: 0 0 0 0px;
+     -moz-border-radius: 0 0 0 0px;
+          border-radius: 0 0 0 0px;
+}
+
+.input-prepend.input-append .add-on:last-child,
+.input-prepend.input-append .btn:last-child {
+  margin-left: -1px;
+  -webkit-border-radius: 0 0 0px 0;
+     -moz-border-radius: 0 0 0px 0;
+          border-radius: 0 0 0px 0;
+}
+
+.input-prepend.input-append .btn-group:first-child {
+  margin-left: 0;
+}
+
+input.search-query {
+  padding-right: 14px;
+  padding-right: 4px \9;
+  padding-left: 14px;
+  padding-left: 4px \9;
+  /* IE7-8 doesn't have border-radius, so don't indent the padding */
+
+  margin-bottom: 0;
+  -webkit-border-radius: 15px;
+     -moz-border-radius: 15px;
+          border-radius: 15px;
+}
+
+/* Allow for input prepend/append in search forms */
+
+.form-search .input-append .search-query,
+.form-search .input-prepend .search-query {
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+}
+
+.form-search .input-append .search-query {
+  -webkit-border-radius: 14px 0 0 14px;
+     -moz-border-radius: 14px 0 0 14px;
+          border-radius: 14px 0 0 14px;
+}
+
+.form-search .input-append .btn {
+  -webkit-border-radius: 0 14px 14px 0;
+     -moz-border-radius: 0 14px 14px 0;
+          border-radius: 0 14px 14px 0;
+}
+
+.form-search .input-prepend .search-query {
+  -webkit-border-radius: 0 14px 14px 0;
+     -moz-border-radius: 0 14px 14px 0;
+          border-radius: 0 14px 14px 0;
+}
+
+.form-search .input-prepend .btn {
+  -webkit-border-radius: 14px 0 0 14px;
+     -moz-border-radius: 14px 0 0 14px;
+          border-radius: 14px 0 0 14px;
+}
+
+.form-search input,
+.form-inline input,
+.form-horizontal input,
+.form-search textarea,
+.form-inline textarea,
+.form-horizontal textarea,
+.form-search select,
+.form-inline select,
+.form-horizontal select,
+.form-search .help-inline,
+.form-inline .help-inline,
+.form-horizontal .help-inline,
+.form-search .uneditable-input,
+.form-inline .uneditable-input,
+.form-horizontal .uneditable-input,
+.form-search .input-prepend,
+.form-inline .input-prepend,
+.form-horizontal .input-prepend,
+.form-search .input-append,
+.form-inline .input-append,
+.form-horizontal .input-append {
+  display: inline-block;
+  *display: inline;
+  margin-bottom: 0;
+  vertical-align: middle;
+  *zoom: 1;
+}
+
+.form-search .hide,
+.form-inline .hide,
+.form-horizontal .hide {
+  display: none;
+}
+
+.form-search label,
+.form-inline label,
+.form-search .btn-group,
+.form-inline .btn-group {
+  display: inline-block;
+}
+
+.form-search .input-append,
+.form-inline .input-append,
+.form-search .input-prepend,
+.form-inline .input-prepend {
+  margin-bottom: 0;
+}
+
+.form-search .radio,
+.form-search .checkbox,
+.form-inline .radio,
+.form-inline .checkbox {
+  padding-left: 0;
+  margin-bottom: 0;
+  vertical-align: middle;
+}
+
+.form-search .radio input[type="radio"],
+.form-search .checkbox input[type="checkbox"],
+.form-inline .radio input[type="radio"],
+.form-inline .checkbox input[type="checkbox"] {
+  float: left;
+  margin-right: 3px;
+  margin-left: 0;
+}
+
+.control-group {
+  margin-bottom: 10px;
+}
+
+legend + .control-group {
+  margin-top: 20px;
+  -webkit-margin-top-collapse: separate;
+}
+
+.form-horizontal .control-group {
+  margin-bottom: 20px;
+  *zoom: 1;
+}
+
+.form-horizontal .control-group:before,
+.form-horizontal .control-group:after {
+  display: table;
+  line-height: 0;
+  content: "";
+}
+
+.form-horizontal .control-group:after {
+  clear: both;
+}
+
+.form-horizontal .control-label {
+  float: left;
+  width: 160px;
+  padding-top: 5px;
+  text-align: right;
+}
+
+.form-horizontal .controls {
+  *display: inline-block;
+  *padding-left: 20px;
+  margin-left: 180px;
+  *margin-left: 0;
+}
+
+.form-horizontal .controls:first-child {
+  *padding-left: 180px;
+}
+
+.form-horizontal .help-block {
+  margin-bottom: 0;
+}
+
+.form-horizontal input + .help-block,
+.form-horizontal select + .help-block,
+.form-horizontal textarea + .help-block,
+.form-horizontal .uneditable-input + .help-block,
+.form-horizontal .input-prepend + .help-block,
+.form-horizontal .input-append + .help-block {
+  margin-top: 10px;
+}
+
+.form-horizontal .form-actions {
+  padding-left: 180px;
+}
+
+table {
+  max-width: 100%;
+  background-color: transparent;
+  border-collapse: collapse;
+  border-spacing: 0;
+}
+
+.table {
+  width: 100%;
+  margin-bottom: 20px;
+}
+
+.table th,
+.table td {
+  padding: 8px;
+  line-height: 20px;
+  text-align: left;
+  vertical-align: top;
+  border-top: 1px solid #dddddd;
+}
+
+.table th {
+  font-weight: bold;
+}
+
+.table thead th {
+  vertical-align: bottom;
+}
+
+.table caption + thead tr:first-child th,
+.table caption + thead tr:first-child td,
+.table colgroup + thead tr:first-child th,
+.table colgroup + thead tr:first-child td,
+.table thead:first-child tr:first-child th,
+.table thead:first-child tr:first-child td {
+  border-top: 0;
+}
+
+.table tbody + tbody {
+  border-top: 2px solid #dddddd;
+}
+
+.table .table {
+  background-color: #ffffff;
+}
+
+.table-condensed th,
+.table-condensed td {
+  padding: 4px 5px;
+}
+
+.table-bordered {
+  border: 1px solid #dddddd;
+  border-collapse: separate;
+  *border-collapse: collapse;
+  border-left: 0;
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+}
+
+.table-bordered th,
+.table-bordered td {
+  border-left: 1px solid #dddddd;
+}
+
+.table-bordered caption + thead tr:first-child th,
+.table-bordered caption + tbody tr:first-child th,
+.table-bordered caption + tbody tr:first-child td,
+.table-bordered colgroup + thead tr:first-child th,
+.table-bordered colgroup + tbody tr:first-child th,
+.table-bordered colgroup + tbody tr:first-child td,
+.table-bordered thead:first-child tr:first-child th,
+.table-bordered tbody:first-child tr:first-child th,
+.table-bordered tbody:first-child tr:first-child td {
+  border-top: 0;
+}
+
+.table-bordered thead:first-child tr:first-child > th:first-child,
+.table-bordered tbody:first-child tr:first-child > td:first-child,
+.table-bordered tbody:first-child tr:first-child > th:first-child {
+  -webkit-border-top-left-radius: 0;
+          border-top-left-radius: 0;
+  -moz-border-radius-topleft: 0;
+}
+
+.table-bordered thead:first-child tr:first-child > th:last-child,
+.table-bordered tbody:first-child tr:first-child > td:last-child,
+.table-bordered tbody:first-child tr:first-child > th:last-child {
+  -webkit-border-top-right-radius: 0;
+          border-top-right-radius: 0;
+  -moz-border-radius-topright: 0;
+}
+
+.table-bordered thead:last-child tr:last-child > th:first-child,
+.table-bordered tbody:last-child tr:last-child > td:first-child,
+.table-bordered tbody:last-child tr:last-child > th:first-child,
+.table-bordered tfoot:last-child tr:last-child > td:first-child,
+.table-bordered tfoot:last-child tr:last-child > th:first-child {
+  -webkit-border-bottom-left-radius: 0;
+          border-bottom-left-radius: 0;
+  -moz-border-radius-bottomleft: 0;
+}
+
+.table-bordered thead:last-child tr:last-child > th:last-child,
+.table-bordered tbody:last-child tr:last-child > td:last-child,
+.table-bordered tbody:last-child tr:last-child > th:last-child,
+.table-bordered tfoot:last-child tr:last-child > td:last-child,
+.table-bordered tfoot:last-child tr:last-child > th:last-child {
+  -webkit-border-bottom-right-radius: 0;
+          border-bottom-right-radius: 0;
+  -moz-border-radius-bottomright: 0;
+}
+
+.table-bordered tfoot + tbody:last-child tr:last-child td:first-child {
+  -webkit-border-bottom-left-radius: 0;
+          border-bottom-left-radius: 0;
+  -moz-border-radius-bottomleft: 0;
+}
+
+.table-bordered tfoot + tbody:last-child tr:last-child td:last-child {
+  -webkit-border-bottom-right-radius: 0;
+          border-bottom-right-radius: 0;
+  -moz-border-radius-bottomright: 0;
+}
+
+.table-bordered caption + thead tr:first-child th:first-child,
+.table-bordered caption + tbody tr:first-child td:first-child,
+.table-bordered colgroup + thead tr:first-child th:first-child,
+.table-bordered colgroup + tbody tr:first-child td:first-child {
+  -webkit-border-top-left-radius: 0;
+          border-top-left-radius: 0;
+  -moz-border-radius-topleft: 0;
+}
+
+.table-bordered caption + thead tr:first-child th:last-child,
+.table-bordered caption + tbody tr:first-child td:last-child,
+.table-bordered colgroup + thead tr:first-child th:last-child,
+.table-bordered colgroup + tbody tr:first-child td:last-child {
+  -webkit-border-top-right-radius: 0;
+          border-top-right-radius: 0;
+  -moz-border-radius-topright: 0;
+}
+
+.table-striped tbody > tr:nth-child(odd) > td,
+.table-striped tbody > tr:nth-child(odd) > th {
+  background-color: #f9f9f9;
+}
+
+.table-hover tbody tr:hover > td,
+.table-hover tbody tr:hover > th {
+  background-color: #e8f8fd;
+}
+
+table td[class*="span"],
+table th[class*="span"],
+.row-fluid table td[class*="span"],
+.row-fluid table th[class*="span"] {
+  display: table-cell;
+  float: none;
+  margin-left: 0;
+}
+
+.table td.span1,
+.table th.span1 {
+  float: none;
+  width: 44px;
+  margin-left: 0;
+}
+
+.table td.span2,
+.table th.span2 {
+  float: none;
+  width: 124px;
+  margin-left: 0;
+}
+
+.table td.span3,
+.table th.span3 {
+  float: none;
+  width: 204px;
+  margin-left: 0;
+}
+
+.table td.span4,
+.table th.span4 {
+  float: none;
+  width: 284px;
+  margin-left: 0;
+}
+
+.table td.span5,
+.table th.span5 {
+  float: none;
+  width: 364px;
+  margin-left: 0;
+}
+
+.table td.span6,
+.table th.span6 {
+  float: none;
+  width: 444px;
+  margin-left: 0;
+}
+
+.table td.span7,
+.table th.span7 {
+  float: none;
+  width: 524px;
+  margin-left: 0;
+}
+
+.table td.span8,
+.table th.span8 {
+  float: none;
+  width: 604px;
+  margin-left: 0;
+}
+
+.table td.span9,
+.table th.span9 {
+  float: none;
+  width: 684px;
+  margin-left: 0;
+}
+
+.table td.span10,
+.table th.span10 {
+  float: none;
+  width: 764px;
+  margin-left: 0;
+}
+
+.table td.span11,
+.table th.span11 {
+  float: none;
+  width: 844px;
+  margin-left: 0;
+}
+
+.table td.span12,
+.table th.span12 {
+  float: none;
+  width: 924px;
+  margin-left: 0;
+}
+
+.table tbody tr.success > td {
+  background-color: #3fb618;
+}
+
+.table tbody tr.error > td {
+  background-color: #ff0039;
+}
+
+.table tbody tr.warning > td {
+  background-color: #ff7518;
+}
+
+.table tbody tr.info > td {
+  background-color: #9954bb;
+}
+
+.table-hover tbody tr.success:hover > td {
+  background-color: #379f15;
+}
+
+.table-hover tbody tr.error:hover > td {
+  background-color: #e60033;
+}
+
+.table-hover tbody tr.warning:hover > td {
+  background-color: #fe6600;
+}
+
+.table-hover tbody tr.info:hover > td {
+  background-color: #8d46b0;
+}
+
+/* White icons with optional class, or on hover/focus/active states of certain elements */
+
+.icon-white,
+.nav-pills > .active > a > [class^="icon-"],
+.nav-pills > .active > a > [class*=" icon-"],
+.nav-list > .active > a > [class^="icon-"],
+.nav-list > .active > a > [class*=" icon-"],
+.navbar-inverse .nav > .active > a > [class^="icon-"],
+.navbar-inverse .nav > .active > a > [class*=" icon-"],
+.dropdown-menu > li > a:hover > [class^="icon-"],
+.dropdown-menu > li > a:focus > [class^="icon-"],
+.dropdown-menu > li > a:hover > [class*=" icon-"],
+.dropdown-menu > li > a:focus > [class*=" icon-"],
+.dropdown-menu > .active > a > [class^="icon-"],
+.dropdown-menu > .active > a > [class*=" icon-"],
+.dropdown-submenu:hover > a > [class^="icon-"],
+.dropdown-submenu:focus > a > [class^="icon-"],
+.dropdown-submenu:hover > a > [class*=" icon-"],
+.dropdown-submenu:focus > a > [class*=" icon-"] {
+  background-image: url("../img/glyphicons-halflings-white.png");
+}
+
+.icon-glass {
+  background-position: 0      0;
+}
+
+.icon-music {
+  background-position: -24px 0;
+}
+
+.icon-search {
+  background-position: -48px 0;
+}
+
+.icon-envelope {
+  background-position: -72px 0;
+}
+
+.icon-heart {
+  background-position: -96px 0;
+}
+
+.icon-star {
+  background-position: -120px 0;
+}
+
+.icon-star-empty {
+  background-position: -144px 0;
+}
+
+.icon-user {
+  background-position: -168px 0;
+}
+
+.icon-film {
+  background-position: -192px 0;
+}
+
+.icon-th-large {
+  background-position: -216px 0;
+}
+
+.icon-th {
+  background-position: -240px 0;
+}
+
+.icon-th-list {
+  background-position: -264px 0;
+}
+
+.icon-ok {
+  background-position: -288px 0;
+}
+
+.icon-remove {
+  background-position: -312px 0;
+}
+
+.icon-zoom-in {
+  background-position: -336px 0;
+}
+
+.icon-zoom-out {
+  background-position: -360px 0;
+}
+
+.icon-off {
+  background-position: -384px 0;
+}
+
+.icon-signal {
+  background-position: -408px 0;
+}
+
+.icon-cog {
+  background-position: -432px 0;
+}
+
+.icon-trash {
+  background-position: -456px 0;
+}
+
+.icon-home {
+  background-position: 0 -24px;
+}
+
+.icon-file {
+  background-position: -24px -24px;
+}
+
+.icon-time {
+  background-position: -48px -24px;
+}
+
+.icon-road {
+  background-position: -72px -24px;
+}
+
+.icon-download-alt {
+  background-position: -96px -24px;
+}
+
+.icon-download {
+  background-position: -120px -24px;
+}
+
+.icon-upload {
+  background-position: -144px -24px;
+}
+
+.icon-inbox {
+  background-position: -168px -24px;
+}
+
+.icon-play-circle {
+  background-position: -192px -24px;
+}
+
+.icon-repeat {
+  background-position: -216px -24px;
+}
+
+.icon-refresh {
+  background-position: -240px -24px;
+}
+
+.icon-list-alt {
+  background-position: -264px -24px;
+}
+
+.icon-lock {
+  background-position: -287px -24px;
+}
+
+.icon-flag {
+  background-position: -312px -24px;
+}
+
+.icon-headphones {
+  background-position: -336px -24px;
+}
+
+.icon-volume-off {
+  background-position: -360px -24px;
+}
+
+.icon-volume-down {
+  background-position: -384px -24px;
+}
+
+.icon-volume-up {
+  background-position: -408px -24px;
+}
+
+.icon-qrcode {
+  background-position: -432px -24px;
+}
+
+.icon-barcode {
+  background-position: -456px -24px;
+}
+
+.icon-tag {
+  background-position: 0 -48px;
+}
+
+.icon-tags {
+  background-position: -25px -48px;
+}
+
+.icon-book {
+  background-position: -48px -48px;
+}
+
+.icon-bookmark {
+  background-position: -72px -48px;
+}
+
+.icon-print {
+  background-position: -96px -48px;
+}
+
+.icon-camera {
+  background-position: -120px -48px;
+}
+
+.icon-font {
+  background-position: -144px -48px;
+}
+
+.icon-bold {
+  background-position: -167px -48px;
+}
+
+.icon-italic {
+  background-position: -192px -48px;
+}
+
+.icon-text-height {
+  background-position: -216px -48px;
+}
+
+.icon-text-width {
+  background-position: -240px -48px;
+}
+
+.icon-align-left {
+  background-position: -264px -48px;
+}
+
+.icon-align-center {
+  background-position: -288px -48px;
+}
+
+.icon-align-right {
+  background-position: -312px -48px;
+}
+
+.icon-align-justify {
+  background-position: -336px -48px;
+}
+
+.icon-list {
+  background-position: -360px -48px;
+}
+
+.icon-indent-left {
+  background-position: -384px -48px;
+}
+
+.icon-indent-right {
+  background-position: -408px -48px;
+}
+
+.icon-facetime-video {
+  background-position: -432px -48px;
+}
+
+.icon-picture {
+  background-position: -456px -48px;
+}
+
+.icon-pencil {
+  background-position: 0 -72px;
+}
+
+.icon-map-marker {
+  background-position: -24px -72px;
+}
+
+.icon-adjust {
+  background-position: -48px -72px;
+}
+
+.icon-tint {
+  background-position: -72px -72px;
+}
+
+.icon-edit {
+  background-position: -96px -72px;
+}
+
+.icon-share {
+  background-position: -120px -72px;
+}
+
+.icon-check {
+  background-position: -144px -72px;
+}
+
+.icon-move {
+  background-position: -168px -72px;
+}
+
+.icon-step-backward {
+  background-position: -192px -72px;
+}
+
+.icon-fast-backward {
+  background-position: -216px -72px;
+}
+
+.icon-backward {
+  background-position: -240px -72px;
+}
+
+.icon-play {
+  background-position: -264px -72px;
+}
+
+.icon-pause {
+  background-position: -288px -72px;
+}
+
+.icon-stop {
+  background-position: -312px -72px;
+}
+
+.icon-forward {
+  background-position: -336px -72px;
+}
+
+.icon-fast-forward {
+  background-position: -360px -72px;
+}
+
+.icon-step-forward {
+  background-position: -384px -72px;
+}
+
+.icon-eject {
+  background-position: -408px -72px;
+}
+
+.icon-chevron-left {
+  background-position: -432px -72px;
+}
+
+.icon-chevron-right {
+  background-position: -456px -72px;
+}
+
+.icon-plus-sign {
+  background-position: 0 -96px;
+}
+
+.icon-minus-sign {
+  background-position: -24px -96px;
+}
+
+.icon-remove-sign {
+  background-position: -48px -96px;
+}
+
+.icon-ok-sign {
+  background-position: -72px -96px;
+}
+
+.icon-question-sign {
+  background-position: -96px -96px;
+}
+
+.icon-info-sign {
+  background-position: -120px -96px;
+}
+
+.icon-screenshot {
+  background-position: -144px -96px;
+}
+
+.icon-remove-circle {
+  background-position: -168px -96px;
+}
+
+.icon-ok-circle {
+  background-position: -192px -96px;
+}
+
+.icon-ban-circle {
+  background-position: -216px -96px;
+}
+
+.icon-arrow-left {
+  background-position: -240px -96px;
+}
+
+.icon-arrow-right {
+  background-position: -264px -96px;
+}
+
+.icon-arrow-up {
+  background-position: -289px -96px;
+}
+
+.icon-arrow-down {
+  background-position: -312px -96px;
+}
+
+.icon-share-alt {
+  background-position: -336px -96px;
+}
+
+.icon-resize-full {
+  background-position: -360px -96px;
+}
+
+.icon-resize-small {
+  background-position: -384px -96px;
+}
+
+.icon-plus {
+  background-position: -408px -96px;
+}
+
+.icon-minus {
+  background-position: -433px -96px;
+}
+
+.icon-asterisk {
+  background-position: -456px -96px;
+}
+
+.icon-exclamation-sign {
+  background-position: 0 -120px;
+}
+
+.icon-gift {
+  background-position: -24px -120px;
+}
+
+.icon-leaf {
+  background-position: -48px -120px;
+}
+
+.icon-fire {
+  background-position: -72px -120px;
+}
+
+.icon-eye-open {
+  background-position: -96px -120px;
+}
+
+.icon-eye-close {
+  background-position: -120px -120px;
+}
+
+.icon-warning-sign {
+  background-position: -144px -120px;
+}
+
+.icon-plane {
+  background-position: -168px -120px;
+}
+
+.icon-calendar {
+  background-position: -192px -120px;
+}
+
+.icon-random {
+  width: 16px;
+  background-position: -216px -120px;
+}
+
+.icon-comment {
+  background-position: -240px -120px;
+}
+
+.icon-magnet {
+  background-position: -264px -120px;
+}
+
+.icon-chevron-up {
+  background-position: -288px -120px;
+}
+
+.icon-chevron-down {
+  background-position: -313px -119px;
+}
+
+.icon-retweet {
+  background-position: -336px -120px;
+}
+
+.icon-shopping-cart {
+  background-position: -360px -120px;
+}
+
+.icon-folder-close {
+  width: 16px;
+  background-position: -384px -120px;
+}
+
+.icon-folder-open {
+  width: 16px;
+  background-position: -408px -120px;
+}
+
+.icon-resize-vertical {
+  background-position: -432px -119px;
+}
+
+.icon-resize-horizontal {
+  background-position: -456px -118px;
+}
+
+.icon-hdd {
+  background-position: 0 -144px;
+}
+
+.icon-bullhorn {
+  background-position: -24px -144px;
+}
+
+.icon-bell {
+  background-position: -48px -144px;
+}
+
+.icon-certificate {
+  background-position: -72px -144px;
+}
+
+.icon-thumbs-up {
+  background-position: -96px -144px;
+}
+
+.icon-thumbs-down {
+  background-position: -120px -144px;
+}
+
+.icon-hand-right {
+  background-position: -144px -144px;
+}
+
+.icon-hand-left {
+  background-position: -168px -144px;
+}
+
+.icon-hand-up {
+  background-position: -192px -144px;
+}
+
+.icon-hand-down {
+  background-position: -216px -144px;
+}
+
+.icon-circle-arrow-right {
+  background-position: -240px -144px;
+}
+
+.icon-circle-arrow-left {
+  background-position: -264px -144px;
+}
+
+.icon-circle-arrow-up {
+  background-position: -288px -144px;
+}
+
+.icon-circle-arrow-down {
+  background-position: -312px -144px;
+}
+
+.icon-globe {
+  background-position: -336px -144px;
+}
+
+.icon-wrench {
+  background-position: -360px -144px;
+}
+
+.icon-tasks {
+  background-position: -384px -144px;
+}
+
+.icon-filter {
+  background-position: -408px -144px;
+}
+
+.icon-briefcase {
+  background-position: -432px -144px;
+}
+
+.icon-fullscreen {
+  background-position: -456px -144px;
+}
+
+.dropup,
+.dropdown {
+  position: relative;
+}
+
+.dropdown-toggle {
+  *margin-bottom: -3px;
+}
+
+.dropdown-toggle:active,
+.open .dropdown-toggle {
+  outline: 0;
+}
+
+.caret {
+  display: inline-block;
+  width: 0;
+  height: 0;
+  vertical-align: top;
+  border-top: 4px solid #000000;
+  border-right: 4px solid transparent;
+  border-left: 4px solid transparent;
+  content: "";
+}
+
+.dropdown .caret {
+  margin-top: 8px;
+  margin-left: 2px;
+}
+
+.dropdown-menu {
+  position: absolute;
+  top: 100%;
+  left: 0;
+  z-index: 1000;
+  display: none;
+  float: left;
+  min-width: 160px;
+  padding: 5px 0;
+  margin: 2px 0 0;
+  list-style: none;
+  background-color: #ffffff;
+  border: 1px solid #ccc;
+  border: 1px solid rgba(0, 0, 0, 0.2);
+  *border-right-width: 2px;
+  *border-bottom-width: 2px;
+  -webkit-border-radius: 6px;
+     -moz-border-radius: 6px;
+          border-radius: 6px;
+  -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+     -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+          box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+  -webkit-background-clip: padding-box;
+     -moz-background-clip: padding;
+          background-clip: padding-box;
+}
+
+.dropdown-menu.pull-right {
+  right: 0;
+  left: auto;
+}
+
+.dropdown-menu .divider {
+  *width: 100%;
+  height: 1px;
+  margin: 9px 1px;
+  *margin: -5px 0 5px;
+  overflow: hidden;
+  background-color: #e5e5e5;
+  border-bottom: 1px solid #ffffff;
+}
+
+.dropdown-menu > li > a {
+  display: block;
+  padding: 3px 20px;
+  clear: both;
+  font-weight: normal;
+  line-height: 20px;
+  color: #999999;
+  white-space: nowrap;
+}
+
+.dropdown-menu > li > a:hover,
+.dropdown-menu > li > a:focus,
+.dropdown-submenu:hover > a,
+.dropdown-submenu:focus > a {
+  color: #ffffff;
+  text-decoration: none;
+  background-color: #007af5;
+  background-image: -moz-linear-gradient(top, #007fff, #0072e6);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#007fff), to(#0072e6));
+  background-image: -webkit-linear-gradient(top, #007fff, #0072e6);
+  background-image: -o-linear-gradient(top, #007fff, #0072e6);
+  background-image: linear-gradient(to bottom, #007fff, #0072e6);
+  background-repeat: repeat-x;
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff007fff', endColorstr='#ff0072e6', GradientType=0);
+}
+
+.dropdown-menu > .active > a,
+.dropdown-menu > .active > a:hover,
+.dropdown-menu > .active > a:focus {
+  color: #ffffff;
+  text-decoration: none;
+  background-color: #007af5;
+  background-image: -moz-linear-gradient(top, #007fff, #0072e6);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#007fff), to(#0072e6));
+  background-image: -webkit-linear-gradient(top, #007fff, #0072e6);
+  background-image: -o-linear-gradient(top, #007fff, #0072e6);
+  background-image: linear-gradient(to bottom, #007fff, #0072e6);
+  background-repeat: repeat-x;
+  outline: 0;
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff007fff', endColorstr='#ff0072e6', GradientType=0);
+}
+
+.dropdown-menu > .disabled > a,
+.dropdown-menu > .disabled > a:hover,
+.dropdown-menu > .disabled > a:focus {
+  color: #dfdfdf;
+}
+
+.dropdown-menu > .disabled > a:hover,
+.dropdown-menu > .disabled > a:focus {
+  text-decoration: none;
+  cursor: default;
+  background-color: transparent;
+  background-image: none;
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+}
+
+.open {
+  *z-index: 1000;
+}
+
+.open > .dropdown-menu {
+  display: block;
+}
+
+.pull-right > .dropdown-menu {
+  right: 0;
+  left: auto;
+}
+
+.dropup .caret,
+.navbar-fixed-bottom .dropdown .caret {
+  border-top: 0;
+  border-bottom: 4px solid #000000;
+  content: "";
+}
+
+.dropup .dropdown-menu,
+.navbar-fixed-bottom .dropdown .dropdown-menu {
+  top: auto;
+  bottom: 100%;
+  margin-bottom: 1px;
+}
+
+.dropdown-submenu {
+  position: relative;
+}
+
+.dropdown-submenu > .dropdown-menu {
+  top: 0;
+  left: 100%;
+  margin-top: -6px;
+  margin-left: -1px;
+  -webkit-border-radius: 0 6px 6px 6px;
+     -moz-border-radius: 0 6px 6px 6px;
+          border-radius: 0 6px 6px 6px;
+}
+
+.dropdown-submenu:hover > .dropdown-menu {
+  display: block;
+}
+
+.dropup .dropdown-submenu > .dropdown-menu {
+  top: auto;
+  bottom: 0;
+  margin-top: 0;
+  margin-bottom: -2px;
+  -webkit-border-radius: 5px 5px 5px 0;
+     -moz-border-radius: 5px 5px 5px 0;
+          border-radius: 5px 5px 5px 0;
+}
+
+.dropdown-submenu > a:after {
+  display: block;
+  float: right;
+  width: 0;
+  height: 0;
+  margin-top: 5px;
+  margin-right: -10px;
+  border-color: transparent;
+  border-left-color: #cccccc;
+  border-style: solid;
+  border-width: 5px 0 5px 5px;
+  content: " ";
+}
+
+.dropdown-submenu:hover > a:after {
+  border-left-color: #ffffff;
+}
+
+.dropdown-submenu.pull-left {
+  float: none;
+}
+
+.dropdown-submenu.pull-left > .dropdown-menu {
+  left: -100%;
+  margin-left: 10px;
+  -webkit-border-radius: 6px 0 6px 6px;
+     -moz-border-radius: 6px 0 6px 6px;
+          border-radius: 6px 0 6px 6px;
+}
+
+.dropdown .dropdown-menu .nav-header {
+  padding-right: 20px;
+  padding-left: 20px;
+}
+
+.typeahead {
+  z-index: 1051;
+  margin-top: 2px;
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+}
+
+.well {
+  min-height: 20px;
+  padding: 19px;
+  margin-bottom: 20px;
+  background-color: #eeeeee;
+  border: 1px solid #dcdcdc;
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+  -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
+     -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
+          box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05);
+}
+
+.well blockquote {
+  border-color: #ddd;
+  border-color: rgba(0, 0, 0, 0.15);
+}
+
+.well-large {
+  padding: 24px;
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+}
+
+.well-small {
+  padding: 9px;
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+}
+
+.fade {
+  opacity: 0;
+  -webkit-transition: opacity 0.15s linear;
+     -moz-transition: opacity 0.15s linear;
+       -o-transition: opacity 0.15s linear;
+          transition: opacity 0.15s linear;
+}
+
+.fade.in {
+  opacity: 1;
+}
+
+.collapse {
+  position: relative;
+  height: 0;
+  overflow: hidden;
+  -webkit-transition: height 0.35s ease;
+     -moz-transition: height 0.35s ease;
+       -o-transition: height 0.35s ease;
+          transition: height 0.35s ease;
+}
+
+.collapse.in {
+  height: auto;
+}
+
+.close {
+  float: right;
+  font-size: 20px;
+  font-weight: bold;
+  line-height: 20px;
+  color: #000000;
+  text-shadow: 0 1px 0 #ffffff;
+  opacity: 0.2;
+  filter: alpha(opacity=20);
+}
+
+.close:hover,
+.close:focus {
+  color: #000000;
+  text-decoration: none;
+  cursor: pointer;
+  opacity: 0.4;
+  filter: alpha(opacity=40);
+}
+
+button.close {
+  padding: 0;
+  cursor: pointer;
+  background: transparent;
+  border: 0;
+  -webkit-appearance: none;
+}
+
+.btn {
+  display: inline-block;
+  *display: inline;
+  padding: 4px 12px;
+  margin-bottom: 0;
+  *margin-left: .3em;
+  font-size: 14px;
+  line-height: 20px;
+  color: #999999;
+  text-align: center;
+  text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75);
+  vertical-align: middle;
+  cursor: pointer;
+  background-color: #dfdfdf;
+  *background-color: #c8c8c8;
+  background-image: -moz-linear-gradient(top, #eeeeee, #c8c8c8);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#eeeeee), to(#c8c8c8));
+  background-image: -webkit-linear-gradient(top, #eeeeee, #c8c8c8);
+  background-image: -o-linear-gradient(top, #eeeeee, #c8c8c8);
+  background-image: linear-gradient(to bottom, #eeeeee, #c8c8c8);
+  background-repeat: repeat-x;
+  border: 1px solid #bbbbbb;
+  *border: 0;
+  border-color: #c8c8c8 #c8c8c8 #a2a2a2;
+  border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+  border-bottom-color: #a2a2a2;
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffeeeeee', endColorstr='#ffc8c8c8', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+  *zoom: 1;
+  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+     -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+          box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+}
+
+.btn:hover,
+.btn:focus,
+.btn:active,
+.btn.active,
+.btn.disabled,
+.btn[disabled] {
+  color: #999999;
+  background-color: #c8c8c8;
+  *background-color: #bbbbbb;
+}
+
+.btn:active,
+.btn.active {
+  background-color: #aeaeae \9;
+}
+
+.btn:first-child {
+  *margin-left: 0;
+}
+
+.btn:hover,
+.btn:focus {
+  color: #999999;
+  text-decoration: none;
+  background-position: 0 -15px;
+  -webkit-transition: background-position 0.1s linear;
+     -moz-transition: background-position 0.1s linear;
+       -o-transition: background-position 0.1s linear;
+          transition: background-position 0.1s linear;
+}
+
+.btn:focus {
+  outline: thin dotted #333;
+  outline: 5px auto -webkit-focus-ring-color;
+  outline-offset: -2px;
+}
+
+.btn.active,
+.btn:active {
+  background-image: none;
+  outline: 0;
+  -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
+     -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
+          box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
+}
+
+.btn.disabled,
+.btn[disabled] {
+  cursor: default;
+  background-image: none;
+  opacity: 0.65;
+  filter: alpha(opacity=65);
+  -webkit-box-shadow: none;
+     -moz-box-shadow: none;
+          box-shadow: none;
+}
+
+.btn-large {
+  padding: 22px 30px;
+  font-size: 17.5px;
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+}
+
+.btn-large [class^="icon-"],
+.btn-large [class*=" icon-"] {
+  margin-top: 4px;
+}
+
+.btn-small {
+  padding: 2px 10px;
+  font-size: 11.9px;
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+}
+
+.btn-small [class^="icon-"],
+.btn-small [class*=" icon-"] {
+  margin-top: 0;
+}
+
+.btn-mini [class^="icon-"],
+.btn-mini [class*=" icon-"] {
+  margin-top: -1px;
+}
+
+.btn-mini {
+  padding: 2px 6px;
+  font-size: 10.5px;
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+}
+
+.btn-block {
+  display: block;
+  width: 100%;
+  padding-right: 0;
+  padding-left: 0;
+  -webkit-box-sizing: border-box;
+     -moz-box-sizing: border-box;
+          box-sizing: border-box;
+}
+
+.btn-block + .btn-block {
+  margin-top: 5px;
+}
+
+input[type="submit"].btn-block,
+input[type="reset"].btn-block,
+input[type="button"].btn-block {
+  width: 100%;
+}
+
+.btn-primary.active,
+.btn-warning.active,
+.btn-danger.active,
+.btn-success.active,
+.btn-info.active,
+.btn-inverse.active {
+  color: rgba(255, 255, 255, 0.75);
+}
+
+.btn-primary {
+  color: #ffffff;
+  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+  background-color: #0f82f5;
+  *background-color: #0072e6;
+  background-image: -moz-linear-gradient(top, #1a8cff, #0072e6);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#1a8cff), to(#0072e6));
+  background-image: -webkit-linear-gradient(top, #1a8cff, #0072e6);
+  background-image: -o-linear-gradient(top, #1a8cff, #0072e6);
+  background-image: linear-gradient(to bottom, #1a8cff, #0072e6);
+  background-repeat: repeat-x;
+  border-color: #0072e6 #0072e6 #004c99;
+  border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff1a8cff', endColorstr='#ff0072e6', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+}
+
+.btn-primary:hover,
+.btn-primary:focus,
+.btn-primary:active,
+.btn-primary.active,
+.btn-primary.disabled,
+.btn-primary[disabled] {
+  color: #ffffff;
+  background-color: #0072e6;
+  *background-color: #0066cc;
+}
+
+.btn-primary:active,
+.btn-primary.active {
+  background-color: #0059b3 \9;
+}
+
+.btn-warning {
+  color: #ffffff;
+  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+  background-color: #fe781e;
+  *background-color: #fe6600;
+  background-image: -moz-linear-gradient(top, #ff8432, #fe6600);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ff8432), to(#fe6600));
+  background-image: -webkit-linear-gradient(top, #ff8432, #fe6600);
+  background-image: -o-linear-gradient(top, #ff8432, #fe6600);
+  background-image: linear-gradient(to bottom, #ff8432, #fe6600);
+  background-repeat: repeat-x;
+  border-color: #fe6600 #fe6600 #b14700;
+  border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffff8432', endColorstr='#fffe6600', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+}
+
+.btn-warning:hover,
+.btn-warning:focus,
+.btn-warning:active,
+.btn-warning.active,
+.btn-warning.disabled,
+.btn-warning[disabled] {
+  color: #ffffff;
+  background-color: #fe6600;
+  *background-color: #e45c00;
+}
+
+.btn-warning:active,
+.btn-warning.active {
+  background-color: #cb5200 \9;
+}
+
+.btn-danger {
+  color: #ffffff;
+  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+  background-color: #f50f43;
+  *background-color: #e60033;
+  background-image: -moz-linear-gradient(top, #ff1a4d, #e60033);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ff1a4d), to(#e60033));
+  background-image: -webkit-linear-gradient(top, #ff1a4d, #e60033);
+  background-image: -o-linear-gradient(top, #ff1a4d, #e60033);
+  background-image: linear-gradient(to bottom, #ff1a4d, #e60033);
+  background-repeat: repeat-x;
+  border-color: #e60033 #e60033 #990022;
+  border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffff1a4d', endColorstr='#ffe60033', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+}
+
+.btn-danger:hover,
+.btn-danger:focus,
+.btn-danger:active,
+.btn-danger.active,
+.btn-danger.disabled,
+.btn-danger[disabled] {
+  color: #ffffff;
+  background-color: #e60033;
+  *background-color: #cc002e;
+}
+
+.btn-danger:active,
+.btn-danger.active {
+  background-color: #b30028 \9;
+}
+
+.btn-success {
+  color: #ffffff;
+  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+  background-color: #41bb19;
+  *background-color: #379f15;
+  background-image: -moz-linear-gradient(top, #47cd1b, #379f15);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#47cd1b), to(#379f15));
+  background-image: -webkit-linear-gradient(top, #47cd1b, #379f15);
+  background-image: -o-linear-gradient(top, #47cd1b, #379f15);
+  background-image: linear-gradient(to bottom, #47cd1b, #379f15);
+  background-repeat: repeat-x;
+  border-color: #379f15 #379f15 #205c0c;
+  border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff47cd1b', endColorstr='#ff379f15', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+}
+
+.btn-success:hover,
+.btn-success:focus,
+.btn-success:active,
+.btn-success.active,
+.btn-success.disabled,
+.btn-success[disabled] {
+  color: #ffffff;
+  background-color: #379f15;
+  *background-color: #2f8912;
+}
+
+.btn-success:active,
+.btn-success.active {
+  background-color: #28720f \9;
+}
+
+.btn-info {
+  color: #ffffff;
+  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+  background-color: #9b59bb;
+  *background-color: #8d46b0;
+  background-image: -moz-linear-gradient(top, #a466c2, #8d46b0);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#a466c2), to(#8d46b0));
+  background-image: -webkit-linear-gradient(top, #a466c2, #8d46b0);
+  background-image: -o-linear-gradient(top, #a466c2, #8d46b0);
+  background-image: linear-gradient(to bottom, #a466c2, #8d46b0);
+  background-repeat: repeat-x;
+  border-color: #8d46b0 #8d46b0 #613079;
+  border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffa466c2', endColorstr='#ff8d46b0', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+}
+
+.btn-info:hover,
+.btn-info:focus,
+.btn-info:active,
+.btn-info.active,
+.btn-info.disabled,
+.btn-info[disabled] {
+  color: #ffffff;
+  background-color: #8d46b0;
+  *background-color: #7e3f9d;
+}
+
+.btn-info:active,
+.btn-info.active {
+  background-color: #6f378b \9;
+}
+
+.btn-inverse {
+  color: #ffffff;
+  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+  background-color: #080808;
+  *background-color: #000000;
+  background-image: -moz-linear-gradient(top, #0d0d0d, #000000);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0d0d0d), to(#000000));
+  background-image: -webkit-linear-gradient(top, #0d0d0d, #000000);
+  background-image: -o-linear-gradient(top, #0d0d0d, #000000);
+  background-image: linear-gradient(to bottom, #0d0d0d, #000000);
+  background-repeat: repeat-x;
+  border-color: #000000 #000000 #000000;
+  border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0d0d0d', endColorstr='#ff000000', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+}
+
+.btn-inverse:hover,
+.btn-inverse:focus,
+.btn-inverse:active,
+.btn-inverse.active,
+.btn-inverse.disabled,
+.btn-inverse[disabled] {
+  color: #ffffff;
+  background-color: #000000;
+  *background-color: #000000;
+}
+
+.btn-inverse:active,
+.btn-inverse.active {
+  background-color: #000000 \9;
+}
+
+button.btn,
+input[type="submit"].btn {
+  *padding-top: 3px;
+  *padding-bottom: 3px;
+}
+
+button.btn::-moz-focus-inner,
+input[type="submit"].btn::-moz-focus-inner {
+  padding: 0;
+  border: 0;
+}
+
+button.btn.btn-large,
+input[type="submit"].btn.btn-large {
+  *padding-top: 7px;
+  *padding-bottom: 7px;
+}
+
+button.btn.btn-small,
+input[type="submit"].btn.btn-small {
+  *padding-top: 3px;
+  *padding-bottom: 3px;
+}
+
+button.btn.btn-mini,
+input[type="submit"].btn.btn-mini {
+  *padding-top: 1px;
+  *padding-bottom: 1px;
+}
+
+.btn-link,
+.btn-link:active,
+.btn-link[disabled] {
+  background-color: transparent;
+  background-image: none;
+  -webkit-box-shadow: none;
+     -moz-box-shadow: none;
+          box-shadow: none;
+}
+
+.btn-link {
+  color: #007fff;
+  cursor: pointer;
+  border-color: transparent;
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+}
+
+.btn-link:hover,
+.btn-link:focus {
+  color: #0066cc;
+  text-decoration: underline;
+  background-color: transparent;
+}
+
+.btn-link[disabled]:hover,
+.btn-link[disabled]:focus {
+  color: #999999;
+  text-decoration: none;
+}
+
+.btn-group {
+  position: relative;
+  display: inline-block;
+  *display: inline;
+  *margin-left: .3em;
+  font-size: 0;
+  white-space: nowrap;
+  vertical-align: middle;
+  *zoom: 1;
+}
+
+.btn-group:first-child {
+  *margin-left: 0;
+}
+
+.btn-group + .btn-group {
+  margin-left: 5px;
+}
+
+.btn-toolbar {
+  margin-top: 10px;
+  margin-bottom: 10px;
+  font-size: 0;
+}
+
+.btn-toolbar > .btn + .btn,
+.btn-toolbar > .btn-group + .btn,
+.btn-toolbar > .btn + .btn-group {
+  margin-left: 5px;
+}
+
+.btn-group > .btn {
+  position: relative;
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+}
+
+.btn-group > .btn + .btn {
+  margin-left: -1px;
+}
+
+.btn-group > .btn,
+.btn-group > .dropdown-menu,
+.btn-group > .popover {
+  font-size: 14px;
+}
+
+.btn-group > .btn-mini {
+  font-size: 10.5px;
+}
+
+.btn-group > .btn-small {
+  font-size: 11.9px;
+}
+
+.btn-group > .btn-large {
+  font-size: 17.5px;
+}
+
+.btn-group > .btn:first-child {
+  margin-left: 0;
+  -webkit-border-bottom-left-radius: 0;
+          border-bottom-left-radius: 0;
+  -webkit-border-top-left-radius: 0;
+          border-top-left-radius: 0;
+  -moz-border-radius-bottomleft: 0;
+  -moz-border-radius-topleft: 0;
+}
+
+.btn-group > .btn:last-child,
+.btn-group > .dropdown-toggle {
+  -webkit-border-top-right-radius: 0;
+          border-top-right-radius: 0;
+  -webkit-border-bottom-right-radius: 0;
+          border-bottom-right-radius: 0;
+  -moz-border-radius-topright: 0;
+  -moz-border-radius-bottomright: 0;
+}
+
+.btn-group > .btn.large:first-child {
+  margin-left: 0;
+  -webkit-border-bottom-left-radius: 0;
+          border-bottom-left-radius: 0;
+  -webkit-border-top-left-radius: 0;
+          border-top-left-radius: 0;
+  -moz-border-radius-bottomleft: 0;
+  -moz-border-radius-topleft: 0;
+}
+
+.btn-group > .btn.large:last-child,
+.btn-group > .large.dropdown-toggle {
+  -webkit-border-top-right-radius: 0;
+          border-top-right-radius: 0;
+  -webkit-border-bottom-right-radius: 0;
+          border-bottom-right-radius: 0;
+  -moz-border-radius-topright: 0;
+  -moz-border-radius-bottomright: 0;
+}
+
+.btn-group > .btn:hover,
+.btn-group > .btn:focus,
+.btn-group > .btn:active,
+.btn-group > .btn.active {
+  z-index: 2;
+}
+
+.btn-group .dropdown-toggle:active,
+.btn-group.open .dropdown-toggle {
+  outline: 0;
+}
+
+.btn-group > .btn + .dropdown-toggle {
+  *padding-top: 5px;
+  padding-right: 8px;
+  *padding-bottom: 5px;
+  padding-left: 8px;
+  -webkit-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+     -moz-box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+          box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.125), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
+}
+
+.btn-group > .btn-mini + .dropdown-toggle {
+  *padding-top: 2px;
+  padding-right: 5px;
+  *padding-bottom: 2px;
+  padding-left: 5px;
+}
+
+.btn-group > .btn-small + .dropdown-toggle {
+  *padding-top: 5px;
+  *padding-bottom: 4px;
+}
+
+.btn-group > .btn-large + .dropdown-toggle {
+  *padding-top: 7px;
+  padding-right: 12px;
+  *padding-bottom: 7px;
+  padding-left: 12px;
+}
+
+.btn-group.open .dropdown-toggle {
+  background-image: none;
+  -webkit-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
+     -moz-box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
+          box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
+}
+
+.btn-group.open .btn.dropdown-toggle {
+  background-color: #c8c8c8;
+}
+
+.btn-group.open .btn-primary.dropdown-toggle {
+  background-color: #0072e6;
+}
+
+.btn-group.open .btn-warning.dropdown-toggle {
+  background-color: #fe6600;
+}
+
+.btn-group.open .btn-danger.dropdown-toggle {
+  background-color: #e60033;
+}
+
+.btn-group.open .btn-success.dropdown-toggle {
+  background-color: #379f15;
+}
+
+.btn-group.open .btn-info.dropdown-toggle {
+  background-color: #8d46b0;
+}
+
+.btn-group.open .btn-inverse.dropdown-toggle {
+  background-color: #000000;
+}
+
+.btn .caret {
+  margin-top: 8px;
+  margin-left: 0;
+}
+
+.btn-large .caret {
+  margin-top: 6px;
+}
+
+.btn-large .caret {
+  border-top-width: 5px;
+  border-right-width: 5px;
+  border-left-width: 5px;
+}
+
+.btn-mini .caret,
+.btn-small .caret {
+  margin-top: 8px;
+}
+
+.dropup .btn-large .caret {
+  border-bottom-width: 5px;
+}
+
+.btn-primary .caret,
+.btn-warning .caret,
+.btn-danger .caret,
+.btn-info .caret,
+.btn-success .caret,
+.btn-inverse .caret {
+  border-top-color: #ffffff;
+  border-bottom-color: #ffffff;
+}
+
+.btn-group-vertical {
+  display: inline-block;
+  *display: inline;
+  /* IE7 inline-block hack */
+
+  *zoom: 1;
+}
+
+.btn-group-vertical > .btn {
+  display: block;
+  float: none;
+  max-width: 100%;
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+}
+
+.btn-group-vertical > .btn + .btn {
+  margin-top: -1px;
+  margin-left: 0;
+}
+
+.btn-group-vertical > .btn:first-child {
+  -webkit-border-radius: 0 0px 0 0;
+     -moz-border-radius: 0 0px 0 0;
+          border-radius: 0 0px 0 0;
+}
+
+.btn-group-vertical > .btn:last-child {
+  -webkit-border-radius: 0 0 0 0px;
+     -moz-border-radius: 0 0 0 0px;
+          border-radius: 0 0 0 0px;
+}
+
+.btn-group-vertical > .btn-large:first-child {
+  -webkit-border-radius: 0 0px 0 0;
+     -moz-border-radius: 0 0px 0 0;
+          border-radius: 0 0px 0 0;
+}
+
+.btn-group-vertical > .btn-large:last-child {
+  -webkit-border-radius: 0 0 0 0px;
+     -moz-border-radius: 0 0 0 0px;
+          border-radius: 0 0 0 0px;
+}
+
+.alert {
+  padding: 8px 35px 8px 14px;
+  margin-bottom: 20px;
+  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
+  background-color: #ff7518;
+  border: 1px solid transparent;
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+}
+
+.alert,
+.alert h4 {
+  color: #ffffff;
+}
+
+.alert h4 {
+  margin: 0;
+}
+
+.alert .close {
+  position: relative;
+  top: -2px;
+  right: -21px;
+  line-height: 20px;
+}
+
+.alert-success {
+  color: #ffffff;
+  background-color: #3fb618;
+  border-color: transparent;
+}
+
+.alert-success h4 {
+  color: #ffffff;
+}
+
+.alert-danger,
+.alert-error {
+  color: #ffffff;
+  background-color: #ff0039;
+  border-color: transparent;
+}
+
+.alert-danger h4,
+.alert-error h4 {
+  color: #ffffff;
+}
+
+.alert-info {
+  color: #ffffff;
+  background-color: #9954bb;
+  border-color: transparent;
+}
+
+.alert-info h4 {
+  color: #ffffff;
+}
+
+.alert-block {
+  padding-top: 14px;
+  padding-bottom: 14px;
+}
+
+.alert-block > p,
+.alert-block > ul {
+  margin-bottom: 0;
+}
+
+.alert-block p + p {
+  margin-top: 5px;
+}
+
+.nav {
+  margin-bottom: 20px;
+  margin-left: 0;
+  list-style: none;
+}
+
+.nav > li > a {
+  display: block;
+}
+
+.nav > li > a:hover,
+.nav > li > a:focus {
+  text-decoration: none;
+  background-color: #eeeeee;
+}
+
+.nav > li > a > img {
+  max-width: none;
+}
+
+.nav > .pull-right {
+  float: right;
+}
+
+.nav-header {
+  display: block;
+  padding: 3px 15px;
+  font-size: 11px;
+  font-weight: bold;
+  line-height: 20px;
+  color: #dfdfdf;
+  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
+  text-transform: uppercase;
+}
+
+.nav li + .nav-header {
+  margin-top: 9px;
+}
+
+.nav-list {
+  padding-right: 15px;
+  padding-left: 15px;
+  margin-bottom: 0;
+}
+
+.nav-list > li > a,
+.nav-list .nav-header {
+  margin-right: -15px;
+  margin-left: -15px;
+  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
+}
+
+.nav-list > li > a {
+  padding: 3px 15px;
+}
+
+.nav-list > .active > a,
+.nav-list > .active > a:hover,
+.nav-list > .active > a:focus {
+  color: #ffffff;
+  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.2);
+  background-color: #007fff;
+}
+
+.nav-list [class^="icon-"],
+.nav-list [class*=" icon-"] {
+  margin-right: 2px;
+}
+
+.nav-list .divider {
+  *width: 100%;
+  height: 1px;
+  margin: 9px 1px;
+  *margin: -5px 0 5px;
+  overflow: hidden;
+  background-color: #e5e5e5;
+  border-bottom: 1px solid #ffffff;
+}
+
+.nav-tabs,
+.nav-pills {
+  *zoom: 1;
+}
+
+.nav-tabs:before,
+.nav-pills:before,
+.nav-tabs:after,
+.nav-pills:after {
+  display: table;
+  line-height: 0;
+  content: "";
+}
+
+.nav-tabs:after,
+.nav-pills:after {
+  clear: both;
+}
+
+.nav-tabs > li,
+.nav-pills > li {
+  float: left;
+}
+
+.nav-tabs > li > a,
+.nav-pills > li > a {
+  padding-right: 12px;
+  padding-left: 12px;
+  margin-right: 2px;
+  line-height: 14px;
+}
+
+.nav-tabs {
+  border-bottom: 1px solid #ddd;
+}
+
+.nav-tabs > li {
+  margin-bottom: -1px;
+}
+
+.nav-tabs > li > a {
+  padding-top: 8px;
+  padding-bottom: 8px;
+  line-height: 20px;
+  border: 1px solid transparent;
+  -webkit-border-radius: 4px 4px 0 0;
+     -moz-border-radius: 4px 4px 0 0;
+          border-radius: 4px 4px 0 0;
+}
+
+.nav-tabs > li > a:hover,
+.nav-tabs > li > a:focus {
+  border-color: #eeeeee #eeeeee #dddddd;
+}
+
+.nav-tabs > .active > a,
+.nav-tabs > .active > a:hover,
+.nav-tabs > .active > a:focus {
+  color: #bbbbbb;
+  cursor: default;
+  background-color: #ffffff;
+  border: 1px solid #ddd;
+  border-bottom-color: transparent;
+}
+
+.nav-pills > li > a {
+  padding-top: 8px;
+  padding-bottom: 8px;
+  margin-top: 2px;
+  margin-bottom: 2px;
+  -webkit-border-radius: 5px;
+     -moz-border-radius: 5px;
+          border-radius: 5px;
+}
+
+.nav-pills > .active > a,
+.nav-pills > .active > a:hover,
+.nav-pills > .active > a:focus {
+  color: #ffffff;
+  background-color: #007fff;
+}
+
+.nav-stacked > li {
+  float: none;
+}
+
+.nav-stacked > li > a {
+  margin-right: 0;
+}
+
+.nav-tabs.nav-stacked {
+  border-bottom: 0;
+}
+
+.nav-tabs.nav-stacked > li > a {
+  border: 1px solid #ddd;
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+}
+
+.nav-tabs.nav-stacked > li:first-child > a {
+  -webkit-border-top-right-radius: 4px;
+          border-top-right-radius: 4px;
+  -webkit-border-top-left-radius: 4px;
+          border-top-left-radius: 4px;
+  -moz-border-radius-topright: 4px;
+  -moz-border-radius-topleft: 4px;
+}
+
+.nav-tabs.nav-stacked > li:last-child > a {
+  -webkit-border-bottom-right-radius: 4px;
+          border-bottom-right-radius: 4px;
+  -webkit-border-bottom-left-radius: 4px;
+          border-bottom-left-radius: 4px;
+  -moz-border-radius-bottomright: 4px;
+  -moz-border-radius-bottomleft: 4px;
+}
+
+.nav-tabs.nav-stacked > li > a:hover,
+.nav-tabs.nav-stacked > li > a:focus {
+  z-index: 2;
+  border-color: #ddd;
+}
+
+.nav-pills.nav-stacked > li > a {
+  margin-bottom: 3px;
+}
+
+.nav-pills.nav-stacked > li:last-child > a {
+  margin-bottom: 1px;
+}
+
+.nav-tabs .dropdown-menu {
+  -webkit-border-radius: 0 0 6px 6px;
+     -moz-border-radius: 0 0 6px 6px;
+          border-radius: 0 0 6px 6px;
+}
+
+.nav-pills .dropdown-menu {
+  -webkit-border-radius: 6px;
+     -moz-border-radius: 6px;
+          border-radius: 6px;
+}
+
+.nav .dropdown-toggle .caret {
+  margin-top: 6px;
+  border-top-color: #007fff;
+  border-bottom-color: #007fff;
+}
+
+.nav .dropdown-toggle:hover .caret,
+.nav .dropdown-toggle:focus .caret {
+  border-top-color: #0066cc;
+  border-bottom-color: #0066cc;
+}
+
+/* move down carets for tabs */
+
+.nav-tabs .dropdown-toggle .caret {
+  margin-top: 8px;
+}
+
+.nav .active .dropdown-toggle .caret {
+  border-top-color: #fff;
+  border-bottom-color: #fff;
+}
+
+.nav-tabs .active .dropdown-toggle .caret {
+  border-top-color: #bbbbbb;
+  border-bottom-color: #bbbbbb;
+}
+
+.nav > .dropdown.active > a:hover,
+.nav > .dropdown.active > a:focus {
+  cursor: pointer;
+}
+
+.nav-tabs .open .dropdown-toggle,
+.nav-pills .open .dropdown-toggle,
+.nav > li.dropdown.open.active > a:hover,
+.nav > li.dropdown.open.active > a:focus {
+  color: #ffffff;
+  background-color: #dfdfdf;
+  border-color: #dfdfdf;
+}
+
+.nav li.dropdown.open .caret,
+.nav li.dropdown.open.active .caret,
+.nav li.dropdown.open a:hover .caret,
+.nav li.dropdown.open a:focus .caret {
+  border-top-color: #ffffff;
+  border-bottom-color: #ffffff;
+  opacity: 1;
+  filter: alpha(opacity=100);
+}
+
+.tabs-stacked .open > a:hover,
+.tabs-stacked .open > a:focus {
+  border-color: #dfdfdf;
+}
+
+.tabbable {
+  *zoom: 1;
+}
+
+.tabbable:before,
+.tabbable:after {
+  display: table;
+  line-height: 0;
+  content: "";
+}
+
+.tabbable:after {
+  clear: both;
+}
+
+.tab-content {
+  overflow: auto;
+}
+
+.tabs-below > .nav-tabs,
+.tabs-right > .nav-tabs,
+.tabs-left > .nav-tabs {
+  border-bottom: 0;
+}
+
+.tab-content > .tab-pane,
+.pill-content > .pill-pane {
+  display: none;
+}
+
+.tab-content > .active,
+.pill-content > .active {
+  display: block;
+}
+
+.tabs-below > .nav-tabs {
+  border-top: 1px solid #ddd;
+}
+
+.tabs-below > .nav-tabs > li {
+  margin-top: -1px;
+  margin-bottom: 0;
+}
+
+.tabs-below > .nav-tabs > li > a {
+  -webkit-border-radius: 0 0 4px 4px;
+     -moz-border-radius: 0 0 4px 4px;
+          border-radius: 0 0 4px 4px;
+}
+
+.tabs-below > .nav-tabs > li > a:hover,
+.tabs-below > .nav-tabs > li > a:focus {
+  border-top-color: #ddd;
+  border-bottom-color: transparent;
+}
+
+.tabs-below > .nav-tabs > .active > a,
+.tabs-below > .nav-tabs > .active > a:hover,
+.tabs-below > .nav-tabs > .active > a:focus {
+  border-color: transparent #ddd #ddd #ddd;
+}
+
+.tabs-left > .nav-tabs > li,
+.tabs-right > .nav-tabs > li {
+  float: none;
+}
+
+.tabs-left > .nav-tabs > li > a,
+.tabs-right > .nav-tabs > li > a {
+  min-width: 74px;
+  margin-right: 0;
+  margin-bottom: 3px;
+}
+
+.tabs-left > .nav-tabs {
+  float: left;
+  margin-right: 19px;
+  border-right: 1px solid #ddd;
+}
+
+.tabs-left > .nav-tabs > li > a {
+  margin-right: -1px;
+  -webkit-border-radius: 4px 0 0 4px;
+     -moz-border-radius: 4px 0 0 4px;
+          border-radius: 4px 0 0 4px;
+}
+
+.tabs-left > .nav-tabs > li > a:hover,
+.tabs-left > .nav-tabs > li > a:focus {
+  border-color: #eeeeee #dddddd #eeeeee #eeeeee;
+}
+
+.tabs-left > .nav-tabs .active > a,
+.tabs-left > .nav-tabs .active > a:hover,
+.tabs-left > .nav-tabs .active > a:focus {
+  border-color: #ddd transparent #ddd #ddd;
+  *border-right-color: #ffffff;
+}
+
+.tabs-right > .nav-tabs {
+  float: right;
+  margin-left: 19px;
+  border-left: 1px solid #ddd;
+}
+
+.tabs-right > .nav-tabs > li > a {
+  margin-left: -1px;
+  -webkit-border-radius: 0 4px 4px 0;
+     -moz-border-radius: 0 4px 4px 0;
+          border-radius: 0 4px 4px 0;
+}
+
+.tabs-right > .nav-tabs > li > a:hover,
+.tabs-right > .nav-tabs > li > a:focus {
+  border-color: #eeeeee #eeeeee #eeeeee #dddddd;
+}
+
+.tabs-right > .nav-tabs .active > a,
+.tabs-right > .nav-tabs .active > a:hover,
+.tabs-right > .nav-tabs .active > a:focus {
+  border-color: #ddd #ddd #ddd transparent;
+  *border-left-color: #ffffff;
+}
+
+.nav > .disabled > a {
+  color: #dfdfdf;
+}
+
+.nav > .disabled > a:hover,
+.nav > .disabled > a:focus {
+  text-decoration: none;
+  cursor: default;
+  background-color: transparent;
+}
+
+.navbar {
+  *position: relative;
+  *z-index: 2;
+  margin-bottom: 20px;
+  overflow: visible;
+}
+
+.navbar-inner {
+  min-height: 50px;
+  padding-right: 20px;
+  padding-left: 20px;
+  background-color: #080808;
+  background-image: -moz-linear-gradient(top, #080808, #080808);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#080808), to(#080808));
+  background-image: -webkit-linear-gradient(top, #080808, #080808);
+  background-image: -o-linear-gradient(top, #080808, #080808);
+  background-image: linear-gradient(to bottom, #080808, #080808);
+  background-repeat: repeat-x;
+  border: 1px solid transparent;
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff080808', GradientType=0);
+  *zoom: 1;
+  -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
+     -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
+          box-shadow: 0 1px 4px rgba(0, 0, 0, 0.065);
+}
+
+.navbar-inner:before,
+.navbar-inner:after {
+  display: table;
+  line-height: 0;
+  content: "";
+}
+
+.navbar-inner:after {
+  clear: both;
+}
+
+.navbar .container {
+  width: auto;
+}
+
+.nav-collapse.collapse {
+  height: auto;
+  overflow: visible;
+}
+
+.navbar .brand {
+  display: block;
+  float: left;
+  padding: 15px 20px 15px;
+  margin-left: -20px;
+  font-size: 20px;
+  font-weight: 200;
+  color: #ffffff;
+  text-shadow: 0 1px 0 #080808;
+}
+
+.navbar .brand:hover,
+.navbar .brand:focus {
+  text-decoration: none;
+}
+
+.navbar-text {
+  margin-bottom: 0;
+  line-height: 50px;
+  color: #ffffff;
+}
+
+.navbar-link {
+  color: #ffffff;
+}
+
+.navbar-link:hover,
+.navbar-link:focus {
+  color: #bbbbbb;
+}
+
+.navbar .divider-vertical {
+  height: 50px;
+  margin: 0 9px;
+  border-right: 1px solid #080808;
+  border-left: 1px solid #080808;
+}
+
+.navbar .btn,
+.navbar .btn-group {
+  margin-top: 10px;
+}
+
+.navbar .btn-group .btn,
+.navbar .input-prepend .btn,
+.navbar .input-append .btn,
+.navbar .input-prepend .btn-group,
+.navbar .input-append .btn-group {
+  margin-top: 0;
+}
+
+.navbar-form {
+  margin-bottom: 0;
+  *zoom: 1;
+}
+
+.navbar-form:before,
+.navbar-form:after {
+  display: table;
+  line-height: 0;
+  content: "";
+}
+
+.navbar-form:after {
+  clear: both;
+}
+
+.navbar-form input,
+.navbar-form select,
+.navbar-form .radio,
+.navbar-form .checkbox {
+  margin-top: 10px;
+}
+
+.navbar-form input,
+.navbar-form select,
+.navbar-form .btn {
+  display: inline-block;
+  margin-bottom: 0;
+}
+
+.navbar-form input[type="image"],
+.navbar-form input[type="checkbox"],
+.navbar-form input[type="radio"] {
+  margin-top: 3px;
+}
+
+.navbar-form .input-append,
+.navbar-form .input-prepend {
+  margin-top: 5px;
+  white-space: nowrap;
+}
+
+.navbar-form .input-append input,
+.navbar-form .input-prepend input {
+  margin-top: 0;
+}
+
+.navbar-search {
+  position: relative;
+  float: left;
+  margin-top: 10px;
+  margin-bottom: 0;
+}
+
+.navbar-search .search-query {
+  padding: 4px 14px;
+  margin-bottom: 0;
+  font-family: "Open Sans", Calibri, Candara, Arial, sans-serif;
+  font-size: 13px;
+  font-weight: normal;
+  line-height: 1;
+  -webkit-border-radius: 15px;
+     -moz-border-radius: 15px;
+          border-radius: 15px;
+}
+
+.navbar-static-top {
+  position: static;
+  margin-bottom: 0;
+}
+
+.navbar-static-top .navbar-inner {
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+}
+
+.navbar-fixed-top,
+.navbar-fixed-bottom {
+  position: fixed;
+  right: 0;
+  left: 0;
+  z-index: 1030;
+  margin-bottom: 0;
+}
+
+.navbar-fixed-top .navbar-inner,
+.navbar-static-top .navbar-inner {
+  border-width: 0 0 1px;
+}
+
+.navbar-fixed-bottom .navbar-inner {
+  border-width: 1px 0 0;
+}
+
+.navbar-fixed-top .navbar-inner,
+.navbar-fixed-bottom .navbar-inner {
+  padding-right: 0;
+  padding-left: 0;
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+}
+
+.navbar-static-top .container,
+.navbar-fixed-top .container,
+.navbar-fixed-bottom .container {
+  width: 940px;
+}
+
+.navbar-fixed-top {
+  top: 0;
+}
+
+.navbar-fixed-top .navbar-inner,
+.navbar-static-top .navbar-inner {
+  -webkit-box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1);
+     -moz-box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1);
+          box-shadow: 0 1px 10px rgba(0, 0, 0, 0.1);
+}
+
+.navbar-fixed-bottom {
+  bottom: 0;
+}
+
+.navbar-fixed-bottom .navbar-inner {
+  -webkit-box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1);
+     -moz-box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1);
+          box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.1);
+}
+
+.navbar .nav {
+  position: relative;
+  left: 0;
+  display: block;
+  float: left;
+  margin: 0 10px 0 0;
+}
+
+.navbar .nav.pull-right {
+  float: right;
+  margin-right: 0;
+}
+
+.navbar .nav > li {
+  float: left;
+}
+
+.navbar .nav > li > a {
+  float: none;
+  padding: 15px 15px 15px;
+  color: #ffffff;
+  text-decoration: none;
+  text-shadow: 0 1px 0 #080808;
+}
+
+.navbar .nav .dropdown-toggle .caret {
+  margin-top: 8px;
+}
+
+.navbar .nav > li > a:focus,
+.navbar .nav > li > a:hover {
+  color: #bbbbbb;
+  text-decoration: none;
+  background-color: rgba(0, 0, 0, 0.05);
+}
+
+.navbar .nav > .active > a,
+.navbar .nav > .active > a:hover,
+.navbar .nav > .active > a:focus {
+  color: #ffffff;
+  text-decoration: none;
+  background-color: transparent;
+  -webkit-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125);
+     -moz-box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125);
+          box-shadow: inset 0 3px 8px rgba(0, 0, 0, 0.125);
+}
+
+.navbar .btn-navbar {
+  display: none;
+  float: right;
+  padding: 7px 10px;
+  margin-right: 5px;
+  margin-left: 5px;
+  color: #ffffff;
+  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+  background-color: #000000;
+  *background-color: #000000;
+  background-image: -moz-linear-gradient(top, #000000, #000000);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#000000), to(#000000));
+  background-image: -webkit-linear-gradient(top, #000000, #000000);
+  background-image: -o-linear-gradient(top, #000000, #000000);
+  background-image: linear-gradient(to bottom, #000000, #000000);
+  background-repeat: repeat-x;
+  border-color: #000000 #000000 #000000;
+  border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff000000', endColorstr='#ff000000', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);
+     -moz-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);
+          box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.075);
+}
+
+.navbar .btn-navbar:hover,
+.navbar .btn-navbar:focus,
+.navbar .btn-navbar:active,
+.navbar .btn-navbar.active,
+.navbar .btn-navbar.disabled,
+.navbar .btn-navbar[disabled] {
+  color: #ffffff;
+  background-color: #000000;
+  *background-color: #000000;
+}
+
+.navbar .btn-navbar:active,
+.navbar .btn-navbar.active {
+  background-color: #000000 \9;
+}
+
+.navbar .btn-navbar .icon-bar {
+  display: block;
+  width: 18px;
+  height: 2px;
+  background-color: #f5f5f5;
+  -webkit-border-radius: 1px;
+     -moz-border-radius: 1px;
+          border-radius: 1px;
+  -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25);
+     -moz-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25);
+          box-shadow: 0 1px 0 rgba(0, 0, 0, 0.25);
+}
+
+.btn-navbar .icon-bar + .icon-bar {
+  margin-top: 3px;
+}
+
+.navbar .nav > li > .dropdown-menu:before {
+  position: absolute;
+  top: -7px;
+  left: 9px;
+  display: inline-block;
+  border-right: 7px solid transparent;
+  border-bottom: 7px solid #ccc;
+  border-left: 7px solid transparent;
+  border-bottom-color: rgba(0, 0, 0, 0.2);
+  content: '';
+}
+
+.navbar .nav > li > .dropdown-menu:after {
+  position: absolute;
+  top: -6px;
+  left: 10px;
+  display: inline-block;
+  border-right: 6px solid transparent;
+  border-bottom: 6px solid #ffffff;
+  border-left: 6px solid transparent;
+  content: '';
+}
+
+.navbar-fixed-bottom .nav > li > .dropdown-menu:before {
+  top: auto;
+  bottom: -7px;
+  border-top: 7px solid #ccc;
+  border-bottom: 0;
+  border-top-color: rgba(0, 0, 0, 0.2);
+}
+
+.navbar-fixed-bottom .nav > li > .dropdown-menu:after {
+  top: auto;
+  bottom: -6px;
+  border-top: 6px solid #ffffff;
+  border-bottom: 0;
+}
+
+.navbar .nav li.dropdown > a:hover .caret,
+.navbar .nav li.dropdown > a:focus .caret {
+  border-top-color: #bbbbbb;
+  border-bottom-color: #bbbbbb;
+}
+
+.navbar .nav li.dropdown.open > .dropdown-toggle,
+.navbar .nav li.dropdown.active > .dropdown-toggle,
+.navbar .nav li.dropdown.open.active > .dropdown-toggle {
+  color: #ffffff;
+  background-color: transparent;
+}
+
+.navbar .nav li.dropdown > .dropdown-toggle .caret {
+  border-top-color: #ffffff;
+  border-bottom-color: #ffffff;
+}
+
+.navbar .nav li.dropdown.open > .dropdown-toggle .caret,
+.navbar .nav li.dropdown.active > .dropdown-toggle .caret,
+.navbar .nav li.dropdown.open.active > .dropdown-toggle .caret {
+  border-top-color: #ffffff;
+  border-bottom-color: #ffffff;
+}
+
+.navbar .pull-right > li > .dropdown-menu,
+.navbar .nav > li > .dropdown-menu.pull-right {
+  right: 0;
+  left: auto;
+}
+
+.navbar .pull-right > li > .dropdown-menu:before,
+.navbar .nav > li > .dropdown-menu.pull-right:before {
+  right: 12px;
+  left: auto;
+}
+
+.navbar .pull-right > li > .dropdown-menu:after,
+.navbar .nav > li > .dropdown-menu.pull-right:after {
+  right: 13px;
+  left: auto;
+}
+
+.navbar .pull-right > li > .dropdown-menu .dropdown-menu,
+.navbar .nav > li > .dropdown-menu.pull-right .dropdown-menu {
+  right: 100%;
+  left: auto;
+  margin-right: -1px;
+  margin-left: 0;
+  -webkit-border-radius: 6px 0 6px 6px;
+     -moz-border-radius: 6px 0 6px 6px;
+          border-radius: 6px 0 6px 6px;
+}
+
+.navbar-inverse .navbar-inner {
+  background-color: #007fff;
+  background-image: -moz-linear-gradient(top, #007fff, #007fff);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#007fff), to(#007fff));
+  background-image: -webkit-linear-gradient(top, #007fff, #007fff);
+  background-image: -o-linear-gradient(top, #007fff, #007fff);
+  background-image: linear-gradient(to bottom, #007fff, #007fff);
+  background-repeat: repeat-x;
+  border-color: transparent;
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff007fff', endColorstr='#ff007fff', GradientType=0);
+}
+
+.navbar-inverse .brand,
+.navbar-inverse .nav > li > a {
+  color: #ffffff;
+  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+}
+
+.navbar-inverse .brand:hover,
+.navbar-inverse .nav > li > a:hover,
+.navbar-inverse .brand:focus,
+.navbar-inverse .nav > li > a:focus {
+  color: #ffffff;
+}
+
+.navbar-inverse .brand {
+  color: #ffffff;
+}
+
+.navbar-inverse .navbar-text {
+  color: #ffffff;
+}
+
+.navbar-inverse .nav > li > a:focus,
+.navbar-inverse .nav > li > a:hover {
+  color: #ffffff;
+  background-color: rgba(0, 0, 0, 0.05);
+}
+
+.navbar-inverse .nav .active > a,
+.navbar-inverse .nav .active > a:hover,
+.navbar-inverse .nav .active > a:focus {
+  color: #ffffff;
+  background-color: #007fff;
+}
+
+.navbar-inverse .navbar-link {
+  color: #ffffff;
+}
+
+.navbar-inverse .navbar-link:hover,
+.navbar-inverse .navbar-link:focus {
+  color: #ffffff;
+}
+
+.navbar-inverse .divider-vertical {
+  border-right-color: #007fff;
+  border-left-color: #007fff;
+}
+
+.navbar-inverse .nav li.dropdown.open > .dropdown-toggle,
+.navbar-inverse .nav li.dropdown.active > .dropdown-toggle,
+.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle {
+  color: #ffffff;
+  background-color: #007fff;
+}
+
+.navbar-inverse .nav li.dropdown > a:hover .caret,
+.navbar-inverse .nav li.dropdown > a:focus .caret {
+  border-top-color: #ffffff;
+  border-bottom-color: #ffffff;
+}
+
+.navbar-inverse .nav li.dropdown > .dropdown-toggle .caret {
+  border-top-color: #ffffff;
+  border-bottom-color: #ffffff;
+}
+
+.navbar-inverse .nav li.dropdown.open > .dropdown-toggle .caret,
+.navbar-inverse .nav li.dropdown.active > .dropdown-toggle .caret,
+.navbar-inverse .nav li.dropdown.open.active > .dropdown-toggle .caret {
+  border-top-color: #ffffff;
+  border-bottom-color: #ffffff;
+}
+
+.navbar-inverse .navbar-search .search-query {
+  color: #ffffff;
+  background-color: #80bfff;
+  border-color: #007fff;
+  -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15);
+     -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15);
+          box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1), 0 1px 0 rgba(255, 255, 255, 0.15);
+  -webkit-transition: none;
+     -moz-transition: none;
+       -o-transition: none;
+          transition: none;
+}
+
+.navbar-inverse .navbar-search .search-query:-moz-placeholder {
+  color: #999999;
+}
+
+.navbar-inverse .navbar-search .search-query:-ms-input-placeholder {
+  color: #999999;
+}
+
+.navbar-inverse .navbar-search .search-query::-webkit-input-placeholder {
+  color: #999999;
+}
+
+.navbar-inverse .navbar-search .search-query:focus,
+.navbar-inverse .navbar-search .search-query.focused {
+  padding: 5px 15px;
+  color: #999999;
+  text-shadow: 0 1px 0 #ffffff;
+  background-color: #ffffff;
+  border: 0;
+  outline: 0;
+  -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15);
+     -moz-box-shadow: 0 0 3px rgba(0, 0, 0, 0.15);
+          box-shadow: 0 0 3px rgba(0, 0, 0, 0.15);
+}
+
+.navbar-inverse .btn-navbar {
+  color: #ffffff;
+  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+  background-color: #0072e6;
+  *background-color: #0072e6;
+  background-image: -moz-linear-gradient(top, #0072e6, #0072e6);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0072e6), to(#0072e6));
+  background-image: -webkit-linear-gradient(top, #0072e6, #0072e6);
+  background-image: -o-linear-gradient(top, #0072e6, #0072e6);
+  background-image: linear-gradient(to bottom, #0072e6, #0072e6);
+  background-repeat: repeat-x;
+  border-color: #0072e6 #0072e6 #004c99;
+  border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0072e6', endColorstr='#ff0072e6', GradientType=0);
+  filter: progid:DXImageTransform.Microsoft.gradient(enabled=false);
+}
+
+.navbar-inverse .btn-navbar:hover,
+.navbar-inverse .btn-navbar:focus,
+.navbar-inverse .btn-navbar:active,
+.navbar-inverse .btn-navbar.active,
+.navbar-inverse .btn-navbar.disabled,
+.navbar-inverse .btn-navbar[disabled] {
+  color: #ffffff;
+  background-color: #0072e6;
+  *background-color: #0066cc;
+}
+
+.navbar-inverse .btn-navbar:active,
+.navbar-inverse .btn-navbar.active {
+  background-color: #0059b3 \9;
+}
+
+.breadcrumb {
+  padding: 8px 15px;
+  margin: 0 0 20px;
+  list-style: none;
+  background-color: #f5f5f5;
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+}
+
+.breadcrumb > li {
+  display: inline-block;
+  *display: inline;
+  text-shadow: 0 1px 0 #ffffff;
+  *zoom: 1;
+}
+
+.breadcrumb > li > .divider {
+  padding: 0 5px;
+  color: #ccc;
+}
+
+.breadcrumb > .active {
+  color: #dfdfdf;
+}
+
+.pagination {
+  margin: 20px 0;
+}
+
+.pagination ul {
+  display: inline-block;
+  *display: inline;
+  margin-bottom: 0;
+  margin-left: 0;
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+  *zoom: 1;
+  -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
+     -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
+          box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
+}
+
+.pagination ul > li {
+  display: inline;
+}
+
+.pagination ul > li > a,
+.pagination ul > li > span {
+  float: left;
+  padding: 4px 12px;
+  line-height: 20px;
+  text-decoration: none;
+  background-color: #dfdfdf;
+  border: 1px solid transparent;
+  border-left-width: 0;
+}
+
+.pagination ul > li > a:hover,
+.pagination ul > li > a:focus,
+.pagination ul > .active > a,
+.pagination ul > .active > span {
+  background-color: #007fff;
+}
+
+.pagination ul > .active > a,
+.pagination ul > .active > span {
+  color: #dfdfdf;
+  cursor: default;
+}
+
+.pagination ul > .disabled > span,
+.pagination ul > .disabled > a,
+.pagination ul > .disabled > a:hover,
+.pagination ul > .disabled > a:focus {
+  color: #dfdfdf;
+  cursor: default;
+  background-color: transparent;
+}
+
+.pagination ul > li:first-child > a,
+.pagination ul > li:first-child > span {
+  border-left-width: 1px;
+  -webkit-border-bottom-left-radius: 0;
+          border-bottom-left-radius: 0;
+  -webkit-border-top-left-radius: 0;
+          border-top-left-radius: 0;
+  -moz-border-radius-bottomleft: 0;
+  -moz-border-radius-topleft: 0;
+}
+
+.pagination ul > li:last-child > a,
+.pagination ul > li:last-child > span {
+  -webkit-border-top-right-radius: 0;
+          border-top-right-radius: 0;
+  -webkit-border-bottom-right-radius: 0;
+          border-bottom-right-radius: 0;
+  -moz-border-radius-topright: 0;
+  -moz-border-radius-bottomright: 0;
+}
+
+.pagination-centered {
+  text-align: center;
+}
+
+.pagination-right {
+  text-align: right;
+}
+
+.pagination-large ul > li > a,
+.pagination-large ul > li > span {
+  padding: 22px 30px;
+  font-size: 17.5px;
+}
+
+.pagination-large ul > li:first-child > a,
+.pagination-large ul > li:first-child > span {
+  -webkit-border-bottom-left-radius: 0;
+          border-bottom-left-radius: 0;
+  -webkit-border-top-left-radius: 0;
+          border-top-left-radius: 0;
+  -moz-border-radius-bottomleft: 0;
+  -moz-border-radius-topleft: 0;
+}
+
+.pagination-large ul > li:last-child > a,
+.pagination-large ul > li:last-child > span {
+  -webkit-border-top-right-radius: 0;
+          border-top-right-radius: 0;
+  -webkit-border-bottom-right-radius: 0;
+          border-bottom-right-radius: 0;
+  -moz-border-radius-topright: 0;
+  -moz-border-radius-bottomright: 0;
+}
+
+.pagination-mini ul > li:first-child > a,
+.pagination-small ul > li:first-child > a,
+.pagination-mini ul > li:first-child > span,
+.pagination-small ul > li:first-child > span {
+  -webkit-border-bottom-left-radius: 0;
+          border-bottom-left-radius: 0;
+  -webkit-border-top-left-radius: 0;
+          border-top-left-radius: 0;
+  -moz-border-radius-bottomleft: 0;
+  -moz-border-radius-topleft: 0;
+}
+
+.pagination-mini ul > li:last-child > a,
+.pagination-small ul > li:last-child > a,
+.pagination-mini ul > li:last-child > span,
+.pagination-small ul > li:last-child > span {
+  -webkit-border-top-right-radius: 0;
+          border-top-right-radius: 0;
+  -webkit-border-bottom-right-radius: 0;
+          border-bottom-right-radius: 0;
+  -moz-border-radius-topright: 0;
+  -moz-border-radius-bottomright: 0;
+}
+
+.pagination-small ul > li > a,
+.pagination-small ul > li > span {
+  padding: 2px 10px;
+  font-size: 11.9px;
+}
+
+.pagination-mini ul > li > a,
+.pagination-mini ul > li > span {
+  padding: 2px 6px;
+  font-size: 10.5px;
+}
+
+.pager {
+  margin: 20px 0;
+  text-align: center;
+  list-style: none;
+  *zoom: 1;
+}
+
+.pager:before,
+.pager:after {
+  display: table;
+  line-height: 0;
+  content: "";
+}
+
+.pager:after {
+  clear: both;
+}
+
+.pager li {
+  display: inline;
+}
+
+.pager li > a,
+.pager li > span {
+  display: inline-block;
+  padding: 5px 14px;
+  background-color: #fff;
+  border: 1px solid #ddd;
+  -webkit-border-radius: 15px;
+     -moz-border-radius: 15px;
+          border-radius: 15px;
+}
+
+.pager li > a:hover,
+.pager li > a:focus {
+  text-decoration: none;
+  background-color: #f5f5f5;
+}
+
+.pager .next > a,
+.pager .next > span {
+  float: right;
+}
+
+.pager .previous > a,
+.pager .previous > span {
+  float: left;
+}
+
+.pager .disabled > a,
+.pager .disabled > a:hover,
+.pager .disabled > a:focus,
+.pager .disabled > span {
+  color: #dfdfdf;
+  cursor: default;
+  background-color: #fff;
+}
+
+.modal-backdrop {
+  position: fixed;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  z-index: 1040;
+  background-color: #000000;
+}
+
+.modal-backdrop.fade {
+  opacity: 0;
+}
+
+.modal-backdrop,
+.modal-backdrop.fade.in {
+  opacity: 0.8;
+  filter: alpha(opacity=80);
+}
+
+.modal {
+  position: fixed;
+  top: 10%;
+  left: 50%;
+  z-index: 1050;
+  width: 560px;
+  margin-left: -280px;
+  background-color: #ffffff;
+  border: 1px solid #999;
+  border: 1px solid rgba(0, 0, 0, 0.3);
+  *border: 1px solid #999;
+  -webkit-border-radius: 6px;
+     -moz-border-radius: 6px;
+          border-radius: 6px;
+  outline: none;
+  -webkit-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
+     -moz-box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
+          box-shadow: 0 3px 7px rgba(0, 0, 0, 0.3);
+  -webkit-background-clip: padding-box;
+     -moz-background-clip: padding-box;
+          background-clip: padding-box;
+}
+
+.modal.fade {
+  top: -25%;
+  -webkit-transition: opacity 0.3s linear, top 0.3s ease-out;
+     -moz-transition: opacity 0.3s linear, top 0.3s ease-out;
+       -o-transition: opacity 0.3s linear, top 0.3s ease-out;
+          transition: opacity 0.3s linear, top 0.3s ease-out;
+}
+
+.modal.fade.in {
+  top: 10%;
+}
+
+.modal-header {
+  padding: 9px 15px;
+  border-bottom: 1px solid #eee;
+}
+
+.modal-header .close {
+  margin-top: 2px;
+}
+
+.modal-header h3 {
+  margin: 0;
+  line-height: 30px;
+}
+
+.modal-body {
+  position: relative;
+  max-height: 400px;
+  padding: 15px;
+  overflow-y: auto;
+}
+
+.modal-form {
+  margin-bottom: 0;
+}
+
+.modal-footer {
+  padding: 14px 15px 15px;
+  margin-bottom: 0;
+  text-align: right;
+  background-color: #f5f5f5;
+  border-top: 1px solid #ddd;
+  -webkit-border-radius: 0 0 6px 6px;
+     -moz-border-radius: 0 0 6px 6px;
+          border-radius: 0 0 6px 6px;
+  *zoom: 1;
+  -webkit-box-shadow: inset 0 1px 0 #ffffff;
+     -moz-box-shadow: inset 0 1px 0 #ffffff;
+          box-shadow: inset 0 1px 0 #ffffff;
+}
+
+.modal-footer:before,
+.modal-footer:after {
+  display: table;
+  line-height: 0;
+  content: "";
+}
+
+.modal-footer:after {
+  clear: both;
+}
+
+.modal-footer .btn + .btn {
+  margin-bottom: 0;
+  margin-left: 5px;
+}
+
+.modal-footer .btn-group .btn + .btn {
+  margin-left: -1px;
+}
+
+.modal-footer .btn-block + .btn-block {
+  margin-left: 0;
+}
+
+.tooltip {
+  position: absolute;
+  z-index: 1030;
+  display: block;
+  font-size: 11px;
+  line-height: 1.4;
+  opacity: 0;
+  filter: alpha(opacity=0);
+  visibility: visible;
+}
+
+.tooltip.in {
+  opacity: 0.8;
+  filter: alpha(opacity=80);
+}
+
+.tooltip.top {
+  padding: 5px 0;
+  margin-top: -3px;
+}
+
+.tooltip.right {
+  padding: 0 5px;
+  margin-left: 3px;
+}
+
+.tooltip.bottom {
+  padding: 5px 0;
+  margin-top: 3px;
+}
+
+.tooltip.left {
+  padding: 0 5px;
+  margin-left: -3px;
+}
+
+.tooltip-inner {
+  max-width: 200px;
+  padding: 8px;
+  color: #ffffff;
+  text-align: center;
+  text-decoration: none;
+  background-color: #000000;
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+}
+
+.tooltip-arrow {
+  position: absolute;
+  width: 0;
+  height: 0;
+  border-color: transparent;
+  border-style: solid;
+}
+
+.tooltip.top .tooltip-arrow {
+  bottom: 0;
+  left: 50%;
+  margin-left: -5px;
+  border-top-color: #000000;
+  border-width: 5px 5px 0;
+}
+
+.tooltip.right .tooltip-arrow {
+  top: 50%;
+  left: 0;
+  margin-top: -5px;
+  border-right-color: #000000;
+  border-width: 5px 5px 5px 0;
+}
+
+.tooltip.left .tooltip-arrow {
+  top: 50%;
+  right: 0;
+  margin-top: -5px;
+  border-left-color: #000000;
+  border-width: 5px 0 5px 5px;
+}
+
+.tooltip.bottom .tooltip-arrow {
+  top: 0;
+  left: 50%;
+  margin-left: -5px;
+  border-bottom-color: #000000;
+  border-width: 0 5px 5px;
+}
+
+.popover {
+  position: absolute;
+  top: 0;
+  left: 0;
+  z-index: 1010;
+  display: none;
+  max-width: 276px;
+  padding: 1px;
+  text-align: left;
+  white-space: normal;
+  background-color: #ff7518;
+  border: 1px solid #ccc;
+  border: 1px solid rgba(0, 0, 0, 0.2);
+  -webkit-border-radius: 6px;
+     -moz-border-radius: 6px;
+          border-radius: 6px;
+  -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+     -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+          box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
+  -webkit-background-clip: padding-box;
+     -moz-background-clip: padding;
+          background-clip: padding-box;
+}
+
+.popover.top {
+  margin-top: -10px;
+}
+
+.popover.right {
+  margin-left: 10px;
+}
+
+.popover.bottom {
+  margin-top: 10px;
+}
+
+.popover.left {
+  margin-left: -10px;
+}
+
+.popover-title {
+  padding: 8px 14px;
+  margin: 0;
+  font-size: 14px;
+  font-weight: normal;
+  line-height: 18px;
+  background-color: #ff7518;
+  border-bottom: 1px solid #fe6600;
+  -webkit-border-radius: 5px 5px 0 0;
+     -moz-border-radius: 5px 5px 0 0;
+          border-radius: 5px 5px 0 0;
+}
+
+.popover-title:empty {
+  display: none;
+}
+
+.popover-content {
+  padding: 9px 14px;
+}
+
+.popover .arrow,
+.popover .arrow:after {
+  position: absolute;
+  display: block;
+  width: 0;
+  height: 0;
+  border-color: transparent;
+  border-style: solid;
+}
+
+.popover .arrow {
+  border-width: 16px;
+}
+
+.popover .arrow:after {
+  border-width: 15px;
+  content: "";
+}
+
+.popover.top .arrow {
+  bottom: -16px;
+  left: 50%;
+  margin-left: -16px;
+  border-top-color: #999;
+  border-top-color: transparent;
+  border-bottom-width: 0;
+}
+
+.popover.top .arrow:after {
+  bottom: 1px;
+  margin-left: -15px;
+  border-top-color: #ff7518;
+  border-bottom-width: 0;
+}
+
+.popover.right .arrow {
+  top: 50%;
+  left: -16px;
+  margin-top: -16px;
+  border-right-color: #999;
+  border-right-color: transparent;
+  border-left-width: 0;
+}
+
+.popover.right .arrow:after {
+  bottom: -15px;
+  left: 1px;
+  border-right-color: #ff7518;
+  border-left-width: 0;
+}
+
+.popover.bottom .arrow {
+  top: -16px;
+  left: 50%;
+  margin-left: -16px;
+  border-bottom-color: #999;
+  border-bottom-color: transparent;
+  border-top-width: 0;
+}
+
+.popover.bottom .arrow:after {
+  top: 1px;
+  margin-left: -15px;
+  border-bottom-color: #ff7518;
+  border-top-width: 0;
+}
+
+.popover.left .arrow {
+  top: 50%;
+  right: -16px;
+  margin-top: -16px;
+  border-left-color: #999;
+  border-left-color: transparent;
+  border-right-width: 0;
+}
+
+.popover.left .arrow:after {
+  right: 1px;
+  bottom: -15px;
+  border-left-color: #ff7518;
+  border-right-width: 0;
+}
+
+.thumbnails {
+  margin-left: -20px;
+  list-style: none;
+  *zoom: 1;
+}
+
+.thumbnails:before,
+.thumbnails:after {
+  display: table;
+  line-height: 0;
+  content: "";
+}
+
+.thumbnails:after {
+  clear: both;
+}
+
+.row-fluid .thumbnails {
+  margin-left: 0;
+}
+
+.thumbnails > li {
+  float: left;
+  margin-bottom: 20px;
+  margin-left: 20px;
+}
+
+.thumbnail {
+  display: block;
+  padding: 4px;
+  line-height: 20px;
+  border: 1px solid #ddd;
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+  -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055);
+     -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055);
+          box-shadow: 0 1px 3px rgba(0, 0, 0, 0.055);
+  -webkit-transition: all 0.2s ease-in-out;
+     -moz-transition: all 0.2s ease-in-out;
+       -o-transition: all 0.2s ease-in-out;
+          transition: all 0.2s ease-in-out;
+}
+
+a.thumbnail:hover,
+a.thumbnail:focus {
+  border-color: #007fff;
+  -webkit-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25);
+     -moz-box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25);
+          box-shadow: 0 1px 4px rgba(0, 105, 214, 0.25);
+}
+
+.thumbnail > img {
+  display: block;
+  max-width: 100%;
+  margin-right: auto;
+  margin-left: auto;
+}
+
+.thumbnail .caption {
+  padding: 9px;
+  color: #bbbbbb;
+}
+
+.media,
+.media-body {
+  overflow: hidden;
+  *overflow: visible;
+  zoom: 1;
+}
+
+.media,
+.media .media {
+  margin-top: 15px;
+}
+
+.media:first-child {
+  margin-top: 0;
+}
+
+.media-object {
+  display: block;
+}
+
+.media-heading {
+  margin: 0 0 5px;
+}
+
+.media > .pull-left {
+  margin-right: 10px;
+}
+
+.media > .pull-right {
+  margin-left: 10px;
+}
+
+.media-list {
+  margin-left: 0;
+  list-style: none;
+}
+
+.label,
+.badge {
+  display: inline-block;
+  padding: 2px 4px;
+  font-size: 11.844px;
+  font-weight: bold;
+  line-height: 14px;
+  color: #ffffff;
+  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+  white-space: nowrap;
+  vertical-align: baseline;
+  background-color: #dfdfdf;
+}
+
+.label {
+  -webkit-border-radius: 3px;
+     -moz-border-radius: 3px;
+          border-radius: 3px;
+}
+
+.badge {
+  padding-right: 9px;
+  padding-left: 9px;
+  -webkit-border-radius: 9px;
+     -moz-border-radius: 9px;
+          border-radius: 9px;
+}
+
+.label:empty,
+.badge:empty {
+  display: none;
+}
+
+a.label:hover,
+a.label:focus,
+a.badge:hover,
+a.badge:focus {
+  color: #ffffff;
+  text-decoration: none;
+  cursor: pointer;
+}
+
+.label-important,
+.badge-important {
+  background-color: #ffffff;
+}
+
+.label-important[href],
+.badge-important[href] {
+  background-color: #e6e6e6;
+}
+
+.label-warning,
+.badge-warning {
+  background-color: #ff7518;
+}
+
+.label-warning[href],
+.badge-warning[href] {
+  background-color: #e45c00;
+}
+
+.label-success,
+.badge-success {
+  background-color: #ffffff;
+}
+
+.label-success[href],
+.badge-success[href] {
+  background-color: #e6e6e6;
+}
+
+.label-info,
+.badge-info {
+  background-color: #ffffff;
+}
+
+.label-info[href],
+.badge-info[href] {
+  background-color: #e6e6e6;
+}
+
+.label-inverse,
+.badge-inverse {
+  background-color: #999999;
+}
+
+.label-inverse[href],
+.badge-inverse[href] {
+  background-color: #808080;
+}
+
+.btn .label,
+.btn .badge {
+  position: relative;
+  top: -1px;
+}
+
+.btn-mini .label,
+.btn-mini .badge {
+  top: 0;
+}
+
+@-webkit-keyframes progress-bar-stripes {
+  from {
+    background-position: 40px 0;
+  }
+  to {
+    background-position: 0 0;
+  }
+}
+
+@-moz-keyframes progress-bar-stripes {
+  from {
+    background-position: 40px 0;
+  }
+  to {
+    background-position: 0 0;
+  }
+}
+
+@-ms-keyframes progress-bar-stripes {
+  from {
+    background-position: 40px 0;
+  }
+  to {
+    background-position: 0 0;
+  }
+}
+
+@-o-keyframes progress-bar-stripes {
+  from {
+    background-position: 0 0;
+  }
+  to {
+    background-position: 40px 0;
+  }
+}
+
+@keyframes progress-bar-stripes {
+  from {
+    background-position: 40px 0;
+  }
+  to {
+    background-position: 0 0;
+  }
+}
+
+.progress {
+  height: 20px;
+  margin-bottom: 20px;
+  overflow: hidden;
+  background-color: #f7f7f7;
+  background-image: -moz-linear-gradient(top, #f5f5f5, #f9f9f9);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#f9f9f9));
+  background-image: -webkit-linear-gradient(top, #f5f5f5, #f9f9f9);
+  background-image: -o-linear-gradient(top, #f5f5f5, #f9f9f9);
+  background-image: linear-gradient(to bottom, #f5f5f5, #f9f9f9);
+  background-repeat: repeat-x;
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0);
+  -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+     -moz-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+          box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1);
+}
+
+.progress .bar {
+  float: left;
+  width: 0;
+  height: 100%;
+  font-size: 12px;
+  color: #ffffff;
+  text-align: center;
+  text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25);
+  background-color: #0e90d2;
+  background-image: -moz-linear-gradient(top, #149bdf, #0480be);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#149bdf), to(#0480be));
+  background-image: -webkit-linear-gradient(top, #149bdf, #0480be);
+  background-image: -o-linear-gradient(top, #149bdf, #0480be);
+  background-image: linear-gradient(to bottom, #149bdf, #0480be);
+  background-repeat: repeat-x;
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0);
+  -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+     -moz-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+          box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+  -webkit-box-sizing: border-box;
+     -moz-box-sizing: border-box;
+          box-sizing: border-box;
+  -webkit-transition: width 0.6s ease;
+     -moz-transition: width 0.6s ease;
+       -o-transition: width 0.6s ease;
+          transition: width 0.6s ease;
+}
+
+.progress .bar + .bar {
+  -webkit-box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+     -moz-box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+          box-shadow: inset 1px 0 0 rgba(0, 0, 0, 0.15), inset 0 -1px 0 rgba(0, 0, 0, 0.15);
+}
+
+.progress-striped .bar {
+  background-color: #149bdf;
+  background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+  background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+  background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+  -webkit-background-size: 40px 40px;
+     -moz-background-size: 40px 40px;
+       -o-background-size: 40px 40px;
+          background-size: 40px 40px;
+}
+
+.progress.active .bar {
+  -webkit-animation: progress-bar-stripes 2s linear infinite;
+     -moz-animation: progress-bar-stripes 2s linear infinite;
+      -ms-animation: progress-bar-stripes 2s linear infinite;
+       -o-animation: progress-bar-stripes 2s linear infinite;
+          animation: progress-bar-stripes 2s linear infinite;
+}
+
+.progress-danger .bar,
+.progress .bar-danger {
+  background-color: #dd514c;
+  background-image: -moz-linear-gradient(top, #ee5f5b, #c43c35);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ee5f5b), to(#c43c35));
+  background-image: -webkit-linear-gradient(top, #ee5f5b, #c43c35);
+  background-image: -o-linear-gradient(top, #ee5f5b, #c43c35);
+  background-image: linear-gradient(to bottom, #ee5f5b, #c43c35);
+  background-repeat: repeat-x;
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffee5f5b', endColorstr='#ffc43c35', GradientType=0);
+}
+
+.progress-danger.progress-striped .bar,
+.progress-striped .bar-danger {
+  background-color: #ee5f5b;
+  background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+  background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+  background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+}
+
+.progress-success .bar,
+.progress .bar-success {
+  background-color: #5eb95e;
+  background-image: -moz-linear-gradient(top, #62c462, #57a957);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#62c462), to(#57a957));
+  background-image: -webkit-linear-gradient(top, #62c462, #57a957);
+  background-image: -o-linear-gradient(top, #62c462, #57a957);
+  background-image: linear-gradient(to bottom, #62c462, #57a957);
+  background-repeat: repeat-x;
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff62c462', endColorstr='#ff57a957', GradientType=0);
+}
+
+.progress-success.progress-striped .bar,
+.progress-striped .bar-success {
+  background-color: #62c462;
+  background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+  background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+  background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+}
+
+.progress-info .bar,
+.progress .bar-info {
+  background-color: #4bb1cf;
+  background-image: -moz-linear-gradient(top, #5bc0de, #339bb9);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#5bc0de), to(#339bb9));
+  background-image: -webkit-linear-gradient(top, #5bc0de, #339bb9);
+  background-image: -o-linear-gradient(top, #5bc0de, #339bb9);
+  background-image: linear-gradient(to bottom, #5bc0de, #339bb9);
+  background-repeat: repeat-x;
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff339bb9', GradientType=0);
+}
+
+.progress-info.progress-striped .bar,
+.progress-striped .bar-info {
+  background-color: #5bc0de;
+  background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+  background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+  background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+}
+
+.progress-warning .bar,
+.progress .bar-warning {
+  background-color: #ff9046;
+  background-image: -moz-linear-gradient(top, #ffa365, #ff7518);
+  background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffa365), to(#ff7518));
+  background-image: -webkit-linear-gradient(top, #ffa365, #ff7518);
+  background-image: -o-linear-gradient(top, #ffa365, #ff7518);
+  background-image: linear-gradient(to bottom, #ffa365, #ff7518);
+  background-repeat: repeat-x;
+  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffa365', endColorstr='#ffff7518', GradientType=0);
+}
+
+.progress-warning.progress-striped .bar,
+.progress-striped .bar-warning {
+  background-color: #ffa365;
+  background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(0.25, rgba(255, 255, 255, 0.15)), color-stop(0.25, transparent), color-stop(0.5, transparent), color-stop(0.5, rgba(255, 255, 255, 0.15)), color-stop(0.75, rgba(255, 255, 255, 0.15)), color-stop(0.75, transparent), to(transparent));
+  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+  background-image: -moz-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+  background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+  background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent);
+}
+
+.accordion {
+  margin-bottom: 20px;
+}
+
+.accordion-group {
+  margin-bottom: 2px;
+  border: 1px solid #e5e5e5;
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+}
+
+.accordion-heading {
+  border-bottom: 0;
+}
+
+.accordion-heading .accordion-toggle {
+  display: block;
+  padding: 8px 15px;
+}
+
+.accordion-toggle {
+  cursor: pointer;
+}
+
+.accordion-inner {
+  padding: 9px 15px;
+  border-top: 1px solid #e5e5e5;
+}
+
+.carousel {
+  position: relative;
+  margin-bottom: 20px;
+  line-height: 1;
+}
+
+.carousel-inner {
+  position: relative;
+  width: 100%;
+  overflow: hidden;
+}
+
+.carousel-inner > .item {
+  position: relative;
+  display: none;
+  -webkit-transition: 0.6s ease-in-out left;
+     -moz-transition: 0.6s ease-in-out left;
+       -o-transition: 0.6s ease-in-out left;
+          transition: 0.6s ease-in-out left;
+}
+
+.carousel-inner > .item > img,
+.carousel-inner > .item > a > img {
+  display: block;
+  line-height: 1;
+}
+
+.carousel-inner > .active,
+.carousel-inner > .next,
+.carousel-inner > .prev {
+  display: block;
+}
+
+.carousel-inner > .active {
+  left: 0;
+}
+
+.carousel-inner > .next,
+.carousel-inner > .prev {
+  position: absolute;
+  top: 0;
+  width: 100%;
+}
+
+.carousel-inner > .next {
+  left: 100%;
+}
+
+.carousel-inner > .prev {
+  left: -100%;
+}
+
+.carousel-inner > .next.left,
+.carousel-inner > .prev.right {
+  left: 0;
+}
+
+.carousel-inner > .active.left {
+  left: -100%;
+}
+
+.carousel-inner > .active.right {
+  left: 100%;
+}
+
+.carousel-control {
+  position: absolute;
+  top: 40%;
+  left: 15px;
+  width: 40px;
+  height: 40px;
+  margin-top: -20px;
+  font-size: 60px;
+  font-weight: 100;
+  line-height: 30px;
+  color: #ffffff;
+  text-align: center;
+  background: #080808;
+  border: 3px solid #ffffff;
+  -webkit-border-radius: 23px;
+     -moz-border-radius: 23px;
+          border-radius: 23px;
+  opacity: 0.5;
+  filter: alpha(opacity=50);
+}
+
+.carousel-control.right {
+  right: 15px;
+  left: auto;
+}
+
+.carousel-control:hover,
+.carousel-control:focus {
+  color: #ffffff;
+  text-decoration: none;
+  opacity: 0.9;
+  filter: alpha(opacity=90);
+}
+
+.carousel-indicators {
+  position: absolute;
+  top: 15px;
+  right: 15px;
+  z-index: 5;
+  margin: 0;
+  list-style: none;
+}
+
+.carousel-indicators li {
+  display: block;
+  float: left;
+  width: 10px;
+  height: 10px;
+  margin-left: 5px;
+  text-indent: -999px;
+  background-color: #ccc;
+  background-color: rgba(255, 255, 255, 0.25);
+  border-radius: 5px;
+}
+
+.carousel-indicators .active {
+  background-color: #fff;
+}
+
+.carousel-caption {
+  position: absolute;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  padding: 15px;
+  background: #999999;
+  background: rgba(0, 0, 0, 0.75);
+}
+
+.carousel-caption h4,
+.carousel-caption p {
+  line-height: 20px;
+  color: #ffffff;
+}
+
+.carousel-caption h4 {
+  margin: 0 0 5px;
+}
+
+.carousel-caption p {
+  margin-bottom: 0;
+}
+
+.hero-unit {
+  padding: 60px;
+  margin-bottom: 30px;
+  font-size: 18px;
+  font-weight: 200;
+  line-height: 30px;
+  color: inherit;
+  background-color: #eeeeee;
+  -webkit-border-radius: 6px;
+     -moz-border-radius: 6px;
+          border-radius: 6px;
+}
+
+.hero-unit h1 {
+  margin-bottom: 0;
+  font-size: 60px;
+  line-height: 1;
+  letter-spacing: -1px;
+  color: inherit;
+}
+
+.hero-unit li {
+  line-height: 30px;
+}
+
+.pull-right {
+  float: right;
+}
+
+.pull-left {
+  float: left;
+}
+
+.hide {
+  display: none;
+}
+
+.show {
+  display: block;
+}
+
+.invisible {
+  visibility: hidden;
+}
+
+.affix {
+  position: fixed;
+}
+
+body {
+  font-weight: 300;
+}
+
+h1 {
+  font-size: 50px;
+}
+
+h2,
+h3 {
+  font-size: 26px;
+}
+
+h4 {
+  font-size: 14px;
+}
+
+h5,
+h6 {
+  font-size: 11px;
+}
+
+blockquote {
+  padding: 10px 15px;
+  background-color: #eeeeee;
+  border-left-color: #bbbbbb;
+}
+
+blockquote.pull-right {
+  padding: 10px 15px;
+  border-right-color: #bbbbbb;
+}
+
+blockquote small {
+  color: #bbbbbb;
+}
+
+.muted {
+  color: #bbbbbb;
+}
+
+.text-warning {
+  color: #ff7518;
+}
+
+a.text-warning:hover {
+  color: #e45c00;
+}
+
+.text-error {
+  color: #ff0039;
+}
+
+a.text-error:hover {
+  color: #cc002e;
+}
+
+.text-info {
+  color: #9954bb;
+}
+
+a.text-info:hover {
+  color: #7e3f9d;
+}
+
+.text-success {
+  color: #3fb618;
+}
+
+a.text-success:hover {
+  color: #2f8912;
+}
+
+.navbar .navbar-inner {
+  background-image: none;
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+  -webkit-box-shadow: none;
+     -moz-box-shadow: none;
+          box-shadow: none;
+}
+
+.navbar .brand:hover {
+  color: #bbbbbb;
+}
+
+.navbar .nav > .active > a,
+.navbar .nav > .active > a:hover,
+.navbar .nav > .active > a:focus {
+  background-color: rgba(0, 0, 0, 0.05);
+  -webkit-box-shadow: none;
+     -moz-box-shadow: none;
+          box-shadow: none;
+}
+
+.navbar .nav li.dropdown.open > .dropdown-toggle,
+.navbar .nav li.dropdown.active > .dropdown-toggle,
+.navbar .nav li.dropdown.open.active > .dropdown-toggle {
+  color: #ffffff;
+}
+
+.navbar .nav li.dropdown.open > .dropdown-toggle:hover,
+.navbar .nav li.dropdown.active > .dropdown-toggle:hover,
+.navbar .nav li.dropdown.open.active > .dropdown-toggle:hover {
+  color: #eeeeee;
+}
+
+.navbar .navbar-search .search-query {
+  line-height: normal;
+}
+
+.navbar-inverse .brand,
+.navbar-inverse .nav > li > a {
+  text-shadow: none;
+}
+
+.navbar-inverse .brand:hover,
+.navbar-inverse .nav > .active > a,
+.navbar-inverse .nav > .active > a:hover,
+.navbar-inverse .nav > .active > a:focus {
+  color: #ffffff;
+  background-color: rgba(0, 0, 0, 0.05);
+  -webkit-box-shadow: none;
+     -moz-box-shadow: none;
+          box-shadow: none;
+}
+
+.navbar-inverse .navbar-search .search-query {
+  color: #080808;
+}
+
+div.subnav {
+  margin: 0 1px;
+  background: #dfdfdf none;
+  border: none;
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+  -webkit-box-shadow: none;
+     -moz-box-shadow: none;
+          box-shadow: none;
+}
+
+div.subnav .nav {
+  background-color: transparent;
+}
+
+div.subnav .nav > li > a {
+  border-color: transparent;
+}
+
+div.subnav .nav > .active > a,
+div.subnav .nav > .active > a:hover {
+  color: #ffffff;
+  background-color: #000000;
+  border-color: transparent;
+  -webkit-box-shadow: none;
+     -moz-box-shadow: none;
+          box-shadow: none;
+}
+
+div.subnav-fixed {
+  top: 51px;
+  margin: 0;
+}
+
+.nav .open .dropdown-toggle,
+.nav > li.dropdown.open.active > a:hover {
+  color: #007fff;
+}
+
+.nav-tabs > li > a {
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+}
+
+.nav-tabs.nav-stacked > li > a:hover {
+  color: #ffffff;
+  background-color: #007fff;
+}
+
+.nav-tabs.nav-stacked > .active > a,
+.nav-tabs.nav-stacked > .active > a:hover {
+  color: #bbbbbb;
+  background-color: #ffffff;
+}
+
+.nav-tabs.nav-stacked > li:first-child > a,
+.nav-tabs.nav-stacked > li:last-child > a {
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+}
+
+.tabs-below > .nav-tabs > li > a,
+.tabs-left > .nav-tabs > li > a,
+.tabs-right > .nav-tabs > li > a {
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+}
+
+.nav-pills > li > a {
+  color: #000000;
+  background-color: #dfdfdf;
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+}
+
+.nav-pills > li > a:hover {
+  color: #ffffff;
+  background-color: #000000;
+}
+
+.nav-pills > .disabled > a,
+.nav-pills > .disabled > a:hover {
+  color: #999999;
+  background-color: #eeeeee;
+}
+
+.nav-list > li > a {
+  color: #080808;
+}
+
+.nav-list > li > a:hover {
+  color: #ffffff;
+  text-shadow: none;
+  background-color: #007fff;
+}
+
+.nav-list .nav-header {
+  color: #080808;
+}
+
+.nav-list .divider {
+  background-color: #bbbbbb;
+  border-bottom: none;
+}
+
+.pagination ul {
+  -webkit-box-shadow: none;
+     -moz-box-shadow: none;
+          box-shadow: none;
+}
+
+.pagination ul > li > a,
+.pagination ul > li > span {
+  margin-right: 6px;
+  color: #080808;
+}
+
+.pagination ul > li > a:hover,
+.pagination ul > li > span:hover {
+  color: #ffffff;
+  background-color: #080808;
+}
+
+.pagination ul > li:last-child > a,
+.pagination ul > li:last-child > span {
+  margin-right: 0;
+}
+
+.pagination ul > .active > a,
+.pagination ul > .active > span {
+  color: #ffffff;
+}
+
+.pagination ul > .disabled > span,
+.pagination ul > .disabled > a,
+.pagination ul > .disabled > a:hover {
+  color: #999999;
+  background-color: #eeeeee;
+}
+
+.pager li > a,
+.pager li > span {
+  color: #080808;
+  background-color: #dfdfdf;
+  border: none;
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+}
+
+.pager li > a:hover,
+.pager li > span:hover {
+  color: #ffffff;
+  background-color: #080808;
+}
+
+.pager .disabled > a,
+.pager .disabled > a:hover,
+.pager .disabled > span {
+  color: #999999;
+  background-color: #eeeeee;
+}
+
+.breadcrumb {
+  background-color: #dfdfdf;
+}
+
+.breadcrumb li {
+  text-shadow: none;
+}
+
+.breadcrumb .divider,
+.breadcrumb .active {
+  color: #080808;
+  text-shadow: none;
+}
+
+.btn {
+  padding: 5px 12px;
+  text-shadow: none;
+  background-image: none;
+  border: none;
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+  -webkit-box-shadow: none;
+     -moz-box-shadow: none;
+          box-shadow: none;
+}
+
+.btn.disabled {
+  box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05);
+}
+
+.btn-large {
+  padding: 22px 30px;
+}
+
+.btn-small {
+  padding: 2px 10px;
+}
+
+.btn-mini {
+  padding: 2px 6px;
+}
+
+.btn-group > .btn:first-child,
+.btn-group > .btn:last-child,
+.btn-group > .dropdown-toggle {
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+}
+
+.btn-group > .btn + .dropdown-toggle {
+  -webkit-box-shadow: none;
+     -moz-box-shadow: none;
+          box-shadow: none;
+}
+
+.table tbody tr.success td {
+  color: #ffffff;
+}
+
+.table tbody tr.error td {
+  color: #ffffff;
+}
+
+.table tbody tr.info td {
+  color: #ffffff;
+}
+
+.table-bordered {
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+}
+
+.table-bordered thead:first-child tr:first-child th:first-child,
+.table-bordered tbody:first-child tr:first-child td:first-child {
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+}
+
+.table-bordered thead:last-child tr:last-child th:first-child,
+.table-bordered tbody:last-child tr:last-child td:first-child,
+.table-bordered tfoot:last-child tr:last-child td:first-child {
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+}
+
+select,
+textarea,
+input[type="text"],
+input[type="password"],
+input[type="datetime"],
+input[type="datetime-local"],
+input[type="date"],
+input[type="month"],
+input[type="time"],
+input[type="week"],
+input[type="number"],
+input[type="email"],
+input[type="url"],
+input[type="search"],
+input[type="tel"],
+input[type="color"] {
+  color: #080808;
+}
+
+.control-group.warning .control-label,
+.control-group.warning .help-block,
+.control-group.warning .help-inline {
+  color: #ff7518;
+}
+
+.control-group.warning input,
+.control-group.warning select,
+.control-group.warning textarea {
+  color: #080808;
+  border-color: #ff7518;
+}
+
+.control-group.error .control-label,
+.control-group.error .help-block,
+.control-group.error .help-inline {
+  color: #ff0039;
+}
+
+.control-group.error input,
+.control-group.error select,
+.control-group.error textarea {
+  color: #080808;
+  border-color: #ff0039;
+}
+
+.control-group.success .control-label,
+.control-group.success .help-block,
+.control-group.success .help-inline {
+  color: #3fb618;
+}
+
+.control-group.success input,
+.control-group.success select,
+.control-group.success textarea {
+  color: #080808;
+  border-color: #3fb618;
+}
+
+legend {
+  color: #080808;
+  border-bottom: none;
+}
+
+.form-actions {
+  background-color: #eeeeee;
+  border-top: none;
+}
+
+.dropdown-menu {
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+}
+
+.alert {
+  text-shadow: none;
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+}
+
+.alert-heading,
+.alert h1,
+.alert h2,
+.alert h3,
+.alert h4,
+.alert h5,
+.alert h6 {
+  color: #ffffff;
+}
+
+.label {
+  min-width: 80px;
+  min-height: 80px;
+  font-weight: 300;
+  text-shadow: none;
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+}
+
+.label-success {
+  background-color: #3fb618;
+}
+
+.label-important {
+  background-color: #ff0039;
+}
+
+.label-info {
+  background-color: #9954bb;
+}
+
+.label-inverse {
+  background-color: #000000;
+}
+
+.badge {
+  font-weight: 300;
+  text-shadow: none;
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+}
+
+.badge-success {
+  background-color: #3fb618;
+}
+
+.badge-important {
+  background-color: #ff0039;
+}
+
+.badge-info {
+  background-color: #9954bb;
+}
+
+.badge-inverse {
+  background-color: #000000;
+}
+
+.hero-unit {
+  border: none;
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+  -webkit-box-shadow: none;
+     -moz-box-shadow: none;
+          box-shadow: none;
+}
+
+.well {
+  border: none;
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+  -webkit-box-shadow: none;
+     -moz-box-shadow: none;
+          box-shadow: none;
+}
+
+[class^="icon-"],
+[class*=" icon-"] {
+  margin: 0 2px;
+  vertical-align: -2px;
+}
+
+a.thumbnail {
+  background-color: #dfdfdf;
+}
+
+a.thumbnail:hover {
+  background-color: #bbbbbb;
+  border-color: transparent;
+}
+
+.progress {
+  height: 6px;
+  background-color: #eeeeee;
+  background-image: none;
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+  -webkit-box-shadow: none;
+     -moz-box-shadow: none;
+          box-shadow: none;
+}
+
+.progress .bar {
+  background-color: #007fff;
+/*  background-image: none;*/
+}
+
+.progress-info {
+  background-color: #9954bb;
+}
+
+.progress-success {
+  background-color: #3fb618;
+}
+
+.progress-warning {
+  background-color: #ff7518;
+}
+
+.progress-danger {
+  background-color: #ff0039;
+}
+
+.modal {
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+}
+
+.modal-header {
+  border-bottom: none;
+}
+
+.modal-footer {
+  background-color: transparent;
+  border-top: none;
+}
+
+.popover {
+  color: #ffffff;
+  -webkit-border-radius: 0;
+     -moz-border-radius: 0;
+          border-radius: 0;
+}
+
+.popover-title {
+  color: #ffffff;
+  border-bottom: none;
+}
+
+.pull-right {
+  float: right;
+}
+
+.pull-left {
+  float: left;
+}
+
+.hide {
+  display: none;
+}
+
+.show {
+  display: block;
+}
+
+.invisible {
+  visibility: hidden;
+}
+
+.affix {
+  position: fixed;
+}
diff --git a/website/lib/bootstrap-responsive.min.css b/website/lib/bootstrap-responsive.min.css
new file mode 100644
index 0000000..557b492
--- /dev/null
+++ b/website/lib/bootstrap-responsive.min.css
@@ -0,0 +1,11 @@
+/* Forked from https://microsoft.github.io/monaco-editor/ */
+
+/*!
+ * Bootstrap Responsive v2.3.0
+ *
+ * Copyright 2012 Twitter, Inc
+ * Licensed under the Apache License v2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Designed and built with all the love in the world @twitter by @mdo and @fat.
+ */.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;line-height:0;content:""}.clearfix:after{clear:both}.hide-text{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.input-block-level{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}@-ms-viewport{width:device-width}.hidden{display:none;visibility:hidden}.visible-phone{display:none!important}.visible-tablet{display:none!important}.hidden-desktop{display:none!important}.visible-desktop{display:inherit!important}@media(min-width:768px) and (max-width:979px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-tablet{display:inherit!important}.hidden-tablet{display:none!important}}@media(max-width:767px){.hidden-desktop{display:inherit!important}.visible-desktop{display:none!important}.visible-phone{display:inherit!important}.hidden-phone{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:inherit!important}.hidden-print{display:none!important}}@media(min-width:1200px){.row{margin-left:-30px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:30px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:1170px}.span12{width:1170px}.span11{width:1070px}.span10{width:970px}.span9{width:870px}.span8{width:770px}.span7{width:670px}.span6{width:570px}.span5{width:470px}.span4{width:370px}.span3{width:270px}.span2{width:170px}.span1{width:70px}.offset12{margin-left:1230px}.offset11{margin-left:1130px}.offset10{margin-left:1030px}.offset9{margin-left:930px}.offset8{margin-left:830px}.offset7{margin-left:730px}.offset6{margin-left:630px}.offset5{margin-left:530px}.offset4{margin-left:430px}.offset3{margin-left:330px}.offset2{margin-left:230px}.offset1{margin-left:130px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.564102564102564%;*margin-left:2.5109110747408616%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.564102564102564%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.45299145299145%;*width:91.39979996362975%}.row-fluid .span10{width:82.90598290598291%;*width:82.8527914166212%}.row-fluid .span9{width:74.35897435897436%;*width:74.30578286961266%}.row-fluid .span8{width:65.81196581196582%;*width:65.75877432260411%}.row-fluid .span7{width:57.26495726495726%;*width:57.21176577559556%}.row-fluid .span6{width:48.717948717948715%;*width:48.664757228587014%}.row-fluid .span5{width:40.17094017094017%;*width:40.11774868157847%}.row-fluid .span4{width:31.623931623931625%;*width:31.570740134569924%}.row-fluid .span3{width:23.076923076923077%;*width:23.023731587561375%}.row-fluid .span2{width:14.52991452991453%;*width:14.476723040552828%}.row-fluid .span1{width:5.982905982905983%;*width:5.929714493544281%}.row-fluid .offset12{margin-left:105.12820512820512%;*margin-left:105.02182214948171%}.row-fluid .offset12:first-child{margin-left:102.56410256410257%;*margin-left:102.45771958537915%}.row-fluid .offset11{margin-left:96.58119658119658%;*margin-left:96.47481360247316%}.row-fluid .offset11:first-child{margin-left:94.01709401709402%;*margin-left:93.91071103837061%}.row-fluid .offset10{margin-left:88.03418803418803%;*margin-left:87.92780505546462%}.row-fluid .offset10:first-child{margin-left:85.47008547008548%;*margin-left:85.36370249136206%}.row-fluid .offset9{margin-left:79.48717948717949%;*margin-left:79.38079650845607%}.row-fluid .offset9:first-child{margin-left:76.92307692307693%;*margin-left:76.81669394435352%}.row-fluid .offset8{margin-left:70.94017094017094%;*margin-left:70.83378796144753%}.row-fluid .offset8:first-child{margin-left:68.37606837606839%;*margin-left:68.26968539734497%}.row-fluid .offset7{margin-left:62.393162393162385%;*margin-left:62.28677941443899%}.row-fluid .offset7:first-child{margin-left:59.82905982905982%;*margin-left:59.72267685033642%}.row-fluid .offset6{margin-left:53.84615384615384%;*margin-left:53.739770867430444%}.row-fluid .offset6:first-child{margin-left:51.28205128205128%;*margin-left:51.175668303327875%}.row-fluid .offset5{margin-left:45.299145299145295%;*margin-left:45.1927623204219%}.row-fluid .offset5:first-child{margin-left:42.73504273504273%;*margin-left:42.62865975631933%}.row-fluid .offset4{margin-left:36.75213675213675%;*margin-left:36.645753773413354%}.row-fluid .offset4:first-child{margin-left:34.18803418803419%;*margin-left:34.081651209310785%}.row-fluid .offset3{margin-left:28.205128205128204%;*margin-left:28.0987452264048%}.row-fluid .offset3:first-child{margin-left:25.641025641025642%;*margin-left:25.53464266230224%}.row-fluid .offset2{margin-left:19.65811965811966%;*margin-left:19.551736679396257%}.row-fluid .offset2:first-child{margin-left:17.094017094017094%;*margin-left:16.98763411529369%}.row-fluid .offset1{margin-left:11.11111111111111%;*margin-left:11.004728132387708%}.row-fluid .offset1:first-child{margin-left:8.547008547008547%;*margin-left:8.440625568285142%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:30px}input.span12,textarea.span12,.uneditable-input.span12{width:1156px}input.span11,textarea.span11,.uneditable-input.span11{width:1056px}input.span10,textarea.span10,.uneditable-input.span10{width:956px}input.span9,textarea.span9,.uneditable-input.span9{width:856px}input.span8,textarea.span8,.uneditable-input.span8{width:756px}input.span7,textarea.span7,.uneditable-input.span7{width:656px}input.span6,textarea.span6,.uneditable-input.span6{width:556px}input.span5,textarea.span5,.uneditable-input.span5{width:456px}input.span4,textarea.span4,.uneditable-input.span4{width:356px}input.span3,textarea.span3,.uneditable-input.span3{width:256px}input.span2,textarea.span2,.uneditable-input.span2{width:156px}input.span1,textarea.span1,.uneditable-input.span1{width:56px}.thumbnails{margin-left:-30px}.thumbnails>li{margin-left:30px}.row-fluid .thumbnails{margin-left:0}}@media(min-width:768px) and (max-width:979px){.row{margin-left:-20px;*zoom:1}.row:before,.row:after{display:table;line-height:0;content:""}.row:after{clear:both}[class*="span"]{float:left;min-height:1px;margin-left:20px}.container,.navbar-static-top .container,.navbar-fixed-top .container,.navbar-fixed-bottom .container{width:724px}.span12{width:724px}.span11{width:662px}.span10{width:600px}.span9{width:538px}.span8{width:476px}.span7{width:414px}.span6{width:352px}.span5{width:290px}.span4{width:228px}.span3{width:166px}.span2{width:104px}.span1{width:42px}.offset12{margin-left:764px}.offset11{margin-left:702px}.offset10{margin-left:640px}.offset9{margin-left:578px}.offset8{margin-left:516px}.offset7{margin-left:454px}.offset6{margin-left:392px}.offset5{margin-left:330px}.offset4{margin-left:268px}.offset3{margin-left:206px}.offset2{margin-left:144px}.offset1{margin-left:82px}.row-fluid{width:100%;*zoom:1}.row-fluid:before,.row-fluid:after{display:table;line-height:0;content:""}.row-fluid:after{clear:both}.row-fluid [class*="span"]{display:block;float:left;width:100%;min-height:30px;margin-left:2.7624309392265194%;*margin-left:2.709239449864817%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="span"]:first-child{margin-left:0}.row-fluid .controls-row [class*="span"]+[class*="span"]{margin-left:2.7624309392265194%}.row-fluid .span12{width:100%;*width:99.94680851063829%}.row-fluid .span11{width:91.43646408839778%;*width:91.38327259903608%}.row-fluid .span10{width:82.87292817679558%;*width:82.81973668743387%}.row-fluid .span9{width:74.30939226519337%;*width:74.25620077583166%}.row-fluid .span8{width:65.74585635359117%;*width:65.69266486422946%}.row-fluid .span7{width:57.18232044198895%;*width:57.12912895262725%}.row-fluid .span6{width:48.61878453038674%;*width:48.56559304102504%}.row-fluid .span5{width:40.05524861878453%;*width:40.00205712942283%}.row-fluid .span4{width:31.491712707182323%;*width:31.43852121782062%}.row-fluid .span3{width:22.92817679558011%;*width:22.87498530621841%}.row-fluid .span2{width:14.3646408839779%;*width:14.311449394616199%}.row-fluid .span1{width:5.801104972375691%;*width:5.747913483013988%}.row-fluid .offset12{margin-left:105.52486187845304%;*margin-left:105.41847889972962%}.row-fluid .offset12:first-child{margin-left:102.76243093922652%;*margin-left:102.6560479605031%}.row-fluid .offset11{margin-left:96.96132596685082%;*margin-left:96.8549429881274%}.row-fluid .offset11:first-child{margin-left:94.1988950276243%;*margin-left:94.09251204890089%}.row-fluid .offset10{margin-left:88.39779005524862%;*margin-left:88.2914070765252%}.row-fluid .offset10:first-child{margin-left:85.6353591160221%;*margin-left:85.52897613729868%}.row-fluid .offset9{margin-left:79.8342541436464%;*margin-left:79.72787116492299%}.row-fluid .offset9:first-child{margin-left:77.07182320441989%;*margin-left:76.96544022569647%}.row-fluid .offset8{margin-left:71.2707182320442%;*margin-left:71.16433525332079%}.row-fluid .offset8:first-child{margin-left:68.50828729281768%;*margin-left:68.40190431409427%}.row-fluid .offset7{margin-left:62.70718232044199%;*margin-left:62.600799341718584%}.row-fluid .offset7:first-child{margin-left:59.94475138121547%;*margin-left:59.838368402492065%}.row-fluid .offset6{margin-left:54.14364640883978%;*margin-left:54.037263430116376%}.row-fluid .offset6:first-child{margin-left:51.38121546961326%;*margin-left:51.27483249088986%}.row-fluid .offset5{margin-left:45.58011049723757%;*margin-left:45.47372751851417%}.row-fluid .offset5:first-child{margin-left:42.81767955801105%;*margin-left:42.71129657928765%}.row-fluid .offset4{margin-left:37.01657458563536%;*margin-left:36.91019160691196%}.row-fluid .offset4:first-child{margin-left:34.25414364640884%;*margin-left:34.14776066768544%}.row-fluid .offset3{margin-left:28.45303867403315%;*margin-left:28.346655695309746%}.row-fluid .offset3:first-child{margin-left:25.69060773480663%;*margin-left:25.584224756083227%}.row-fluid .offset2{margin-left:19.88950276243094%;*margin-left:19.783119783707537%}.row-fluid .offset2:first-child{margin-left:17.12707182320442%;*margin-left:17.02068884448102%}.row-fluid .offset1{margin-left:11.32596685082873%;*margin-left:11.219583872105325%}.row-fluid .offset1:first-child{margin-left:8.56353591160221%;*margin-left:8.457152932878806%}input,textarea,.uneditable-input{margin-left:0}.controls-row [class*="span"]+[class*="span"]{margin-left:20px}input.span12,textarea.span12,.uneditable-input.span12{width:710px}input.span11,textarea.span11,.uneditable-input.span11{width:648px}input.span10,textarea.span10,.uneditable-input.span10{width:586px}input.span9,textarea.span9,.uneditable-input.span9{width:524px}input.span8,textarea.span8,.uneditable-input.span8{width:462px}input.span7,textarea.span7,.uneditable-input.span7{width:400px}input.span6,textarea.span6,.uneditable-input.span6{width:338px}input.span5,textarea.span5,.uneditable-input.span5{width:276px}input.span4,textarea.span4,.uneditable-input.span4{width:214px}input.span3,textarea.span3,.uneditable-input.span3{width:152px}input.span2,textarea.span2,.uneditable-input.span2{width:90px}input.span1,textarea.span1,.uneditable-input.span1{width:28px}}@media(max-width:767px){body{padding-right:20px;padding-left:20px}.navbar-fixed-top,.navbar-fixed-bottom,.navbar-static-top{margin-right:-20px;margin-left:-20px}.container-fluid{padding:0}.dl-horizontal dt{float:none;width:auto;clear:none;text-align:left}.dl-horizontal dd{margin-left:0}.container{width:auto}.row-fluid{width:100%}.row,.thumbnails{margin-left:0}.thumbnails>li{float:none;margin-left:0}[class*="span"],.uneditable-input[class*="span"],.row-fluid [class*="span"]{display:block;float:none;width:100%;margin-left:0;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.span12,.row-fluid .span12{width:100%;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.row-fluid [class*="offset"]:first-child{margin-left:0}.input-large,.input-xlarge,.input-xxlarge,input[class*="span"],select[class*="span"],textarea[class*="span"],.uneditable-input{display:block;width:100%;min-height:30px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.input-prepend input,.input-append input,.input-prepend input[class*="span"],.input-append input[class*="span"]{display:inline-block;width:auto}.controls-row [class*="span"]+[class*="span"]{margin-left:0}.modal{position:fixed;top:20px;right:20px;left:20px;width:auto;margin:0}.modal.fade{top:-100px}.modal.fade.in{top:20px}}@media(max-width:480px){.nav-collapse{-webkit-transform:translate3d(0,0,0)}.page-header h1 small{display:block;line-height:20px}input[type="checkbox"],input[type="radio"]{border:1px solid #ccc}.form-horizontal .control-label{float:none;width:auto;padding-top:0;text-align:left}.form-horizontal .controls{margin-left:0}.form-horizontal .control-list{padding-top:0}.form-horizontal .form-actions{padding-right:10px;padding-left:10px}.media .pull-left,.media .pull-right{display:block;float:none;margin-bottom:10px}.media-object{margin-right:0;margin-left:0}.modal{top:10px;right:10px;left:10px}.modal-header .close{padding:10px;margin:-10px}.carousel-caption{position:static}}@media(max-width:979px){body{padding-top:0}.navbar-fixed-top,.navbar-fixed-bottom{position:static}.navbar-fixed-top{margin-bottom:20px}.navbar-fixed-bottom{margin-top:20px}.navbar-fixed-top .navbar-inner,.navbar-fixed-bottom .navbar-inner{padding:5px}.navbar .container{width:auto;padding:0}.navbar .brand{padding-right:10px;padding-left:10px;margin:0 0 0 -5px}.nav-collapse{clear:both}.nav-collapse .nav{float:none;margin:0 0 10px}.nav-collapse .nav>li{float:none}.nav-collapse .nav>li>a{margin-bottom:2px}.nav-collapse .nav>.divider-vertical{display:none}.nav-collapse .nav .nav-header{color:#777;text-shadow:none}.nav-collapse .nav>li>a,.nav-collapse .dropdown-menu a{padding:9px 15px;font-weight:bold;color:#777;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}.nav-collapse .btn{padding:4px 10px 4px;font-weight:normal;-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px}.nav-collapse .dropdown-menu li+li a{margin-bottom:2px}.nav-collapse .nav>li>a:hover,.nav-collapse .nav>li>a:focus,.nav-collapse .dropdown-menu a:hover,.nav-collapse .dropdown-menu a:focus{background-color:#f2f2f2}.navbar-inverse .nav-collapse .nav>li>a,.navbar-inverse .nav-collapse .dropdown-menu a{color:#999}.navbar-inverse .nav-collapse .nav>li>a:hover,.navbar-inverse .nav-collapse .nav>li>a:focus,.navbar-inverse .nav-collapse .dropdown-menu a:hover,.navbar-inverse .nav-collapse .dropdown-menu a:focus{background-color:#111}.nav-collapse.in .btn-group{padding:0;margin-top:5px}.nav-collapse .dropdown-menu{position:static;top:auto;left:auto;display:none;float:none;max-width:none;padding:0;margin:0 15px;background-color:transparent;border:0;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.nav-collapse .open>.dropdown-menu{display:block}.nav-collapse .dropdown-menu:before,.nav-collapse .dropdown-menu:after{display:none}.nav-collapse .dropdown-menu .divider{display:none}.nav-collapse .nav>li>.dropdown-menu:before,.nav-collapse .nav>li>.dropdown-menu:after{display:none}.nav-collapse .navbar-form,.nav-collapse .navbar-search{float:none;padding:10px 15px;margin:10px 0;border-top:1px solid #f2f2f2;border-bottom:1px solid #f2f2f2;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);-moz-box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 0 rgba(255,255,255,0.1),0 1px 0 rgba(255,255,255,0.1)}.navbar-inverse .nav-collapse .navbar-form,.navbar-inverse .nav-collapse .navbar-search{border-top-color:#111;border-bottom-color:#111}.navbar .nav-collapse .nav.pull-right{float:none;margin-left:0}.nav-collapse,.nav-collapse.collapse{height:0;overflow:hidden}.navbar .btn-navbar{display:block}.navbar-static .navbar-inner{padding-right:10px;padding-left:10px}}@media(min-width:980px){.nav-collapse.collapse{height:auto!important;overflow:visible!important}}
diff --git a/website/package-lock.json b/website/package-lock.json
new file mode 100644
index 0000000..c178ada
--- /dev/null
+++ b/website/package-lock.json
@@ -0,0 +1,9269 @@
+{
+  "name": "website",
+  "lockfileVersion": 2,
+  "requires": true,
+  "packages": {
+    "": {
+      "devDependencies": {
+        "clean-css": "^5.1.1",
+        "event-stream": "4.0.1",
+        "gulp": "^4.0.2",
+        "monaco-editor": "^0.23.0",
+        "rimraf": "^3.0.2",
+        "uncss": "^0.17.3",
+        "yaserver": "^0.3.0"
+      }
+    },
+    "node_modules/abab": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz",
+      "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==",
+      "dev": true
+    },
+    "node_modules/acorn": {
+      "version": "6.4.2",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz",
+      "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==",
+      "dev": true,
+      "bin": {
+        "acorn": "bin/acorn"
+      },
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/acorn-globals": {
+      "version": "4.3.4",
+      "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.4.tgz",
+      "integrity": "sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==",
+      "dev": true,
+      "dependencies": {
+        "acorn": "^6.0.1",
+        "acorn-walk": "^6.0.1"
+      }
+    },
+    "node_modules/acorn-walk": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz",
+      "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/ajv": {
+      "version": "6.12.6",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+      "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+      "dev": true,
+      "dependencies": {
+        "fast-deep-equal": "^3.1.1",
+        "fast-json-stable-stringify": "^2.0.0",
+        "json-schema-traverse": "^0.4.1",
+        "uri-js": "^4.2.2"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/epoberezkin"
+      }
+    },
+    "node_modules/ansi-colors": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz",
+      "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==",
+      "dev": true,
+      "dependencies": {
+        "ansi-wrap": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/ansi-gray": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz",
+      "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=",
+      "dev": true,
+      "dependencies": {
+        "ansi-wrap": "0.1.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/ansi-regex": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+      "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/ansi-wrap": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz",
+      "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/anymatch": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
+      "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
+      "dev": true,
+      "dependencies": {
+        "micromatch": "^3.1.4",
+        "normalize-path": "^2.1.1"
+      }
+    },
+    "node_modules/anymatch/node_modules/normalize-path": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
+      "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
+      "dev": true,
+      "dependencies": {
+        "remove-trailing-separator": "^1.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/append-buffer": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz",
+      "integrity": "sha1-2CIM9GYIFSXv6lBhTz3mUU36WPE=",
+      "dev": true,
+      "dependencies": {
+        "buffer-equal": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/archy": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz",
+      "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=",
+      "dev": true
+    },
+    "node_modules/arr-diff": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
+      "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/arr-filter": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/arr-filter/-/arr-filter-1.1.2.tgz",
+      "integrity": "sha1-Q/3d0JHo7xGqTEXZzcGOLf8XEe4=",
+      "dev": true,
+      "dependencies": {
+        "make-iterator": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/arr-flatten": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
+      "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/arr-map": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/arr-map/-/arr-map-2.0.2.tgz",
+      "integrity": "sha1-Onc0X/wc814qkYJWAfnljy4kysQ=",
+      "dev": true,
+      "dependencies": {
+        "make-iterator": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/arr-union": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
+      "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/array-each": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz",
+      "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/array-equal": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz",
+      "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=",
+      "dev": true
+    },
+    "node_modules/array-initial": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz",
+      "integrity": "sha1-L6dLJnOTccOUe9enrcc74zSz15U=",
+      "dev": true,
+      "dependencies": {
+        "array-slice": "^1.0.0",
+        "is-number": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/array-initial/node_modules/is-number": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz",
+      "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/array-last": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/array-last/-/array-last-1.3.0.tgz",
+      "integrity": "sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg==",
+      "dev": true,
+      "dependencies": {
+        "is-number": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/array-last/node_modules/is-number": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz",
+      "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/array-slice": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz",
+      "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/array-sort": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-1.0.0.tgz",
+      "integrity": "sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg==",
+      "dev": true,
+      "dependencies": {
+        "default-compare": "^1.0.0",
+        "get-value": "^2.0.6",
+        "kind-of": "^5.0.2"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/array-unique": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
+      "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/asn1": {
+      "version": "0.2.4",
+      "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
+      "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
+      "dev": true,
+      "dependencies": {
+        "safer-buffer": "~2.1.0"
+      }
+    },
+    "node_modules/assert-plus": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+      "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/assign-symbols": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
+      "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/async-done": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz",
+      "integrity": "sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw==",
+      "dev": true,
+      "dependencies": {
+        "end-of-stream": "^1.1.0",
+        "once": "^1.3.2",
+        "process-nextick-args": "^2.0.0",
+        "stream-exhaust": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/async-each": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz",
+      "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==",
+      "dev": true
+    },
+    "node_modules/async-limiter": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
+      "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==",
+      "dev": true
+    },
+    "node_modules/async-settle": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz",
+      "integrity": "sha1-HQqRS7Aldb7IqPOnTlCA9yssDGs=",
+      "dev": true,
+      "dependencies": {
+        "async-done": "^1.2.2"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
+      "dev": true
+    },
+    "node_modules/atob": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
+      "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
+      "dev": true,
+      "bin": {
+        "atob": "bin/atob.js"
+      },
+      "engines": {
+        "node": ">= 4.5.0"
+      }
+    },
+    "node_modules/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,
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/aws4": {
+      "version": "1.11.0",
+      "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz",
+      "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==",
+      "dev": true
+    },
+    "node_modules/bach": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz",
+      "integrity": "sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA=",
+      "dev": true,
+      "dependencies": {
+        "arr-filter": "^1.1.1",
+        "arr-flatten": "^1.0.1",
+        "arr-map": "^2.0.0",
+        "array-each": "^1.0.0",
+        "array-initial": "^1.0.0",
+        "array-last": "^1.1.1",
+        "async-done": "^1.2.2",
+        "async-settle": "^1.0.0",
+        "now-and-later": "^2.0.0"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/balanced-match": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+      "dev": true
+    },
+    "node_modules/base": {
+      "version": "0.11.2",
+      "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
+      "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
+      "dev": true,
+      "dependencies": {
+        "cache-base": "^1.0.1",
+        "class-utils": "^0.3.5",
+        "component-emitter": "^1.2.1",
+        "define-property": "^1.0.0",
+        "isobject": "^3.0.1",
+        "mixin-deep": "^1.2.0",
+        "pascalcase": "^0.1.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/base/node_modules/define-property": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+      "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+      "dev": true,
+      "dependencies": {
+        "is-descriptor": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/bcrypt-pbkdf": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
+      "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
+      "dev": true,
+      "dependencies": {
+        "tweetnacl": "^0.14.3"
+      }
+    },
+    "node_modules/binary-extensions": {
+      "version": "1.13.1",
+      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
+      "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/bindings": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
+      "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
+      "dev": true,
+      "optional": true,
+      "dependencies": {
+        "file-uri-to-path": "1.0.0"
+      }
+    },
+    "node_modules/brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dev": true,
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/braces": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+      "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+      "dev": true,
+      "dependencies": {
+        "arr-flatten": "^1.1.0",
+        "array-unique": "^0.3.2",
+        "extend-shallow": "^2.0.1",
+        "fill-range": "^4.0.0",
+        "isobject": "^3.0.1",
+        "repeat-element": "^1.1.2",
+        "snapdragon": "^0.8.1",
+        "snapdragon-node": "^2.0.1",
+        "split-string": "^3.0.2",
+        "to-regex": "^3.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/browser-process-hrtime": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz",
+      "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==",
+      "dev": true
+    },
+    "node_modules/buffer-equal": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.0.tgz",
+      "integrity": "sha1-WWFrSYME1Var1GaWayLu2j7KX74=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/buffer-from": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
+      "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
+      "dev": true
+    },
+    "node_modules/cache-base": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
+      "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
+      "dev": true,
+      "dependencies": {
+        "collection-visit": "^1.0.0",
+        "component-emitter": "^1.2.1",
+        "get-value": "^2.0.6",
+        "has-value": "^1.0.0",
+        "isobject": "^3.0.1",
+        "set-value": "^2.0.0",
+        "to-object-path": "^0.3.0",
+        "union-value": "^1.0.0",
+        "unset-value": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/call-bind": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
+      "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+      "dev": true,
+      "dependencies": {
+        "function-bind": "^1.1.1",
+        "get-intrinsic": "^1.0.2"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/camelcase": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
+      "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/caseless": {
+      "version": "0.12.0",
+      "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+      "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
+      "dev": true
+    },
+    "node_modules/chokidar": {
+      "version": "2.1.8",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
+      "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
+      "deprecated": "Chokidar 2 will break on node v14+. Upgrade to chokidar 3 with 15x less dependencies.",
+      "dev": true,
+      "dependencies": {
+        "anymatch": "^2.0.0",
+        "async-each": "^1.0.1",
+        "braces": "^2.3.2",
+        "glob-parent": "^3.1.0",
+        "inherits": "^2.0.3",
+        "is-binary-path": "^1.0.0",
+        "is-glob": "^4.0.0",
+        "normalize-path": "^3.0.0",
+        "path-is-absolute": "^1.0.0",
+        "readdirp": "^2.2.1",
+        "upath": "^1.1.1"
+      },
+      "optionalDependencies": {
+        "fsevents": "^1.2.7"
+      }
+    },
+    "node_modules/class-utils": {
+      "version": "0.3.6",
+      "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
+      "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
+      "dev": true,
+      "dependencies": {
+        "arr-union": "^3.1.0",
+        "define-property": "^0.2.5",
+        "isobject": "^3.0.0",
+        "static-extend": "^0.1.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/class-utils/node_modules/define-property": {
+      "version": "0.2.5",
+      "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+      "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+      "dev": true,
+      "dependencies": {
+        "is-descriptor": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/class-utils/node_modules/is-accessor-descriptor": {
+      "version": "0.1.6",
+      "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+      "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+      "dev": true,
+      "dependencies": {
+        "kind-of": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/class-utils/node_modules/is-accessor-descriptor/node_modules/kind-of": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+      "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+      "dev": true,
+      "dependencies": {
+        "is-buffer": "^1.1.5"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/class-utils/node_modules/is-data-descriptor": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+      "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+      "dev": true,
+      "dependencies": {
+        "kind-of": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/class-utils/node_modules/is-data-descriptor/node_modules/kind-of": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+      "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+      "dev": true,
+      "dependencies": {
+        "is-buffer": "^1.1.5"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/class-utils/node_modules/is-descriptor": {
+      "version": "0.1.6",
+      "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+      "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+      "dev": true,
+      "dependencies": {
+        "is-accessor-descriptor": "^0.1.6",
+        "is-data-descriptor": "^0.1.4",
+        "kind-of": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/clean-css": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.1.2.tgz",
+      "integrity": "sha512-QcaGg9OuMo+0Ds933yLOY+gHPWbxhxqF0HDexmToPf8pczvmvZGYzd+QqWp9/mkucAOKViI+dSFOqoZIvXbeBw==",
+      "dev": true,
+      "dependencies": {
+        "source-map": "~0.6.0"
+      },
+      "engines": {
+        "node": ">= 10.0"
+      }
+    },
+    "node_modules/cliui": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
+      "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
+      "dev": true,
+      "dependencies": {
+        "string-width": "^1.0.1",
+        "strip-ansi": "^3.0.1",
+        "wrap-ansi": "^2.0.0"
+      }
+    },
+    "node_modules/clone": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
+      "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/clone-buffer": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz",
+      "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/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
+    },
+    "node_modules/cloneable-readable": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz",
+      "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==",
+      "dev": true,
+      "dependencies": {
+        "inherits": "^2.0.1",
+        "process-nextick-args": "^2.0.0",
+        "readable-stream": "^2.3.5"
+      }
+    },
+    "node_modules/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,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/collection-map": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/collection-map/-/collection-map-1.0.0.tgz",
+      "integrity": "sha1-rqDwb40mx4DCt1SUOFVEsiVa8Yw=",
+      "dev": true,
+      "dependencies": {
+        "arr-map": "^2.0.2",
+        "for-own": "^1.0.0",
+        "make-iterator": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/collection-visit": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
+      "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=",
+      "dev": true,
+      "dependencies": {
+        "map-visit": "^1.0.0",
+        "object-visit": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/color-support": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
+      "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
+      "dev": true,
+      "bin": {
+        "color-support": "bin.js"
+      }
+    },
+    "node_modules/combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "dev": true,
+      "dependencies": {
+        "delayed-stream": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/commander": {
+      "version": "2.20.3",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+      "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+      "dev": true
+    },
+    "node_modules/component-emitter": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
+      "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
+      "dev": true
+    },
+    "node_modules/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
+    },
+    "node_modules/concat-stream": {
+      "version": "1.6.2",
+      "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
+      "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+      "dev": true,
+      "engines": [
+        "node >= 0.8"
+      ],
+      "dependencies": {
+        "buffer-from": "^1.0.0",
+        "inherits": "^2.0.3",
+        "readable-stream": "^2.2.2",
+        "typedarray": "^0.0.6"
+      }
+    },
+    "node_modules/convert-source-map": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz",
+      "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==",
+      "dev": true,
+      "dependencies": {
+        "safe-buffer": "~5.1.1"
+      }
+    },
+    "node_modules/copy-descriptor": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
+      "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/copy-props": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-2.0.5.tgz",
+      "integrity": "sha512-XBlx8HSqrT0ObQwmSzM7WE5k8FxTV75h1DX1Z3n6NhQ/UYYAvInWYmG06vFt7hQZArE2fuO62aihiWIVQwh1sw==",
+      "dev": true,
+      "dependencies": {
+        "each-props": "^1.3.2",
+        "is-plain-object": "^5.0.0"
+      }
+    },
+    "node_modules/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
+    },
+    "node_modules/cssesc": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+      "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+      "dev": true,
+      "bin": {
+        "cssesc": "bin/cssesc"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/cssom": {
+      "version": "0.3.8",
+      "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz",
+      "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==",
+      "dev": true
+    },
+    "node_modules/cssstyle": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.4.0.tgz",
+      "integrity": "sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==",
+      "dev": true,
+      "dependencies": {
+        "cssom": "0.3.x"
+      }
+    },
+    "node_modules/d": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
+      "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
+      "dev": true,
+      "dependencies": {
+        "es5-ext": "^0.10.50",
+        "type": "^1.0.1"
+      }
+    },
+    "node_modules/dashdash": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+      "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
+      "dev": true,
+      "dependencies": {
+        "assert-plus": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/data-urls": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz",
+      "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==",
+      "dev": true,
+      "dependencies": {
+        "abab": "^2.0.0",
+        "whatwg-mimetype": "^2.2.0",
+        "whatwg-url": "^7.0.0"
+      }
+    },
+    "node_modules/debug": {
+      "version": "2.6.9",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+      "dev": true,
+      "dependencies": {
+        "ms": "2.0.0"
+      }
+    },
+    "node_modules/decamelize": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+      "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/decode-uri-component": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
+      "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/deep-is": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
+      "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
+      "dev": true
+    },
+    "node_modules/default-compare": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz",
+      "integrity": "sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==",
+      "dev": true,
+      "dependencies": {
+        "kind-of": "^5.0.2"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/default-resolution": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/default-resolution/-/default-resolution-2.0.0.tgz",
+      "integrity": "sha1-vLgrqnKtebQmp2cy8aga1t8m1oQ=",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/define-properties": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+      "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+      "dev": true,
+      "dependencies": {
+        "object-keys": "^1.0.12"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/define-property": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
+      "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
+      "dev": true,
+      "dependencies": {
+        "is-descriptor": "^1.0.2",
+        "isobject": "^3.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/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,
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/detect-file": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz",
+      "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/domexception": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz",
+      "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==",
+      "dev": true,
+      "dependencies": {
+        "webidl-conversions": "^4.0.2"
+      }
+    },
+    "node_modules/duplexer": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
+      "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==",
+      "dev": true
+    },
+    "node_modules/duplexify": {
+      "version": "3.7.1",
+      "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
+      "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==",
+      "dev": true,
+      "dependencies": {
+        "end-of-stream": "^1.0.0",
+        "inherits": "^2.0.1",
+        "readable-stream": "^2.0.0",
+        "stream-shift": "^1.0.0"
+      }
+    },
+    "node_modules/each-props": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.2.tgz",
+      "integrity": "sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA==",
+      "dev": true,
+      "dependencies": {
+        "is-plain-object": "^2.0.1",
+        "object.defaults": "^1.1.0"
+      }
+    },
+    "node_modules/each-props/node_modules/is-plain-object": {
+      "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,
+      "dependencies": {
+        "isobject": "^3.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/ecc-jsbn": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
+      "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
+      "dev": true,
+      "dependencies": {
+        "jsbn": "~0.1.0",
+        "safer-buffer": "^2.1.0"
+      }
+    },
+    "node_modules/end-of-stream": {
+      "version": "1.4.4",
+      "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+      "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+      "dev": true,
+      "dependencies": {
+        "once": "^1.4.0"
+      }
+    },
+    "node_modules/error-ex": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+      "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+      "dev": true,
+      "dependencies": {
+        "is-arrayish": "^0.2.1"
+      }
+    },
+    "node_modules/es5-ext": {
+      "version": "0.10.53",
+      "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz",
+      "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==",
+      "dev": true,
+      "dependencies": {
+        "es6-iterator": "~2.0.3",
+        "es6-symbol": "~3.1.3",
+        "next-tick": "~1.0.0"
+      }
+    },
+    "node_modules/es6-iterator": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
+      "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=",
+      "dev": true,
+      "dependencies": {
+        "d": "1",
+        "es5-ext": "^0.10.35",
+        "es6-symbol": "^3.1.1"
+      }
+    },
+    "node_modules/es6-symbol": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz",
+      "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
+      "dev": true,
+      "dependencies": {
+        "d": "^1.0.1",
+        "ext": "^1.1.2"
+      }
+    },
+    "node_modules/es6-weak-map": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz",
+      "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==",
+      "dev": true,
+      "dependencies": {
+        "d": "1",
+        "es5-ext": "^0.10.46",
+        "es6-iterator": "^2.0.3",
+        "es6-symbol": "^3.1.1"
+      }
+    },
+    "node_modules/escodegen": {
+      "version": "1.14.3",
+      "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz",
+      "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==",
+      "dev": true,
+      "dependencies": {
+        "esprima": "^4.0.1",
+        "estraverse": "^4.2.0",
+        "esutils": "^2.0.2",
+        "optionator": "^0.8.1"
+      },
+      "bin": {
+        "escodegen": "bin/escodegen.js",
+        "esgenerate": "bin/esgenerate.js"
+      },
+      "engines": {
+        "node": ">=4.0"
+      },
+      "optionalDependencies": {
+        "source-map": "~0.6.1"
+      }
+    },
+    "node_modules/esprima": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+      "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+      "dev": true,
+      "bin": {
+        "esparse": "bin/esparse.js",
+        "esvalidate": "bin/esvalidate.js"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/estraverse": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+      "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+      "dev": true,
+      "engines": {
+        "node": ">=4.0"
+      }
+    },
+    "node_modules/esutils": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+      "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/event-stream": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-4.0.1.tgz",
+      "integrity": "sha512-qACXdu/9VHPBzcyhdOWR5/IahhGMf0roTeZJfzz077GwylcDd90yOHLouhmv7GJ5XzPi6ekaQWd8AvPP2nOvpA==",
+      "dev": true,
+      "dependencies": {
+        "duplexer": "^0.1.1",
+        "from": "^0.1.7",
+        "map-stream": "0.0.7",
+        "pause-stream": "^0.0.11",
+        "split": "^1.0.1",
+        "stream-combiner": "^0.2.2",
+        "through": "^2.3.8"
+      }
+    },
+    "node_modules/expand-brackets": {
+      "version": "2.1.4",
+      "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
+      "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
+      "dev": true,
+      "dependencies": {
+        "debug": "^2.3.3",
+        "define-property": "^0.2.5",
+        "extend-shallow": "^2.0.1",
+        "posix-character-classes": "^0.1.0",
+        "regex-not": "^1.0.0",
+        "snapdragon": "^0.8.1",
+        "to-regex": "^3.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/expand-brackets/node_modules/define-property": {
+      "version": "0.2.5",
+      "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+      "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+      "dev": true,
+      "dependencies": {
+        "is-descriptor": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/expand-brackets/node_modules/is-accessor-descriptor": {
+      "version": "0.1.6",
+      "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+      "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+      "dev": true,
+      "dependencies": {
+        "kind-of": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/expand-brackets/node_modules/is-accessor-descriptor/node_modules/kind-of": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+      "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+      "dev": true,
+      "dependencies": {
+        "is-buffer": "^1.1.5"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/expand-brackets/node_modules/is-data-descriptor": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+      "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+      "dev": true,
+      "dependencies": {
+        "kind-of": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/expand-brackets/node_modules/is-data-descriptor/node_modules/kind-of": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+      "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+      "dev": true,
+      "dependencies": {
+        "is-buffer": "^1.1.5"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/expand-brackets/node_modules/is-descriptor": {
+      "version": "0.1.6",
+      "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+      "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+      "dev": true,
+      "dependencies": {
+        "is-accessor-descriptor": "^0.1.6",
+        "is-data-descriptor": "^0.1.4",
+        "kind-of": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/expand-tilde": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz",
+      "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=",
+      "dev": true,
+      "dependencies": {
+        "homedir-polyfill": "^1.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/ext": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz",
+      "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==",
+      "dev": true,
+      "dependencies": {
+        "type": "^2.0.0"
+      }
+    },
+    "node_modules/ext/node_modules/type": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/type/-/type-2.5.0.tgz",
+      "integrity": "sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==",
+      "dev": true
+    },
+    "node_modules/extend": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+      "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+      "dev": true
+    },
+    "node_modules/extend-shallow": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+      "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+      "dev": true,
+      "dependencies": {
+        "is-extendable": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/extglob": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
+      "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
+      "dev": true,
+      "dependencies": {
+        "array-unique": "^0.3.2",
+        "define-property": "^1.0.0",
+        "expand-brackets": "^2.1.4",
+        "extend-shallow": "^2.0.1",
+        "fragment-cache": "^0.2.1",
+        "regex-not": "^1.0.0",
+        "snapdragon": "^0.8.1",
+        "to-regex": "^3.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/extglob/node_modules/define-property": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+      "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+      "dev": true,
+      "dependencies": {
+        "is-descriptor": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/extsprintf": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+      "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
+      "dev": true,
+      "engines": [
+        "node >=0.6.0"
+      ]
+    },
+    "node_modules/fancy-log": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz",
+      "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==",
+      "dev": true,
+      "dependencies": {
+        "ansi-gray": "^0.1.1",
+        "color-support": "^1.1.3",
+        "parse-node-version": "^1.0.0",
+        "time-stamp": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/fast-deep-equal": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+      "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+      "dev": true
+    },
+    "node_modules/fast-json-stable-stringify": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+      "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+      "dev": true
+    },
+    "node_modules/fast-levenshtein": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+      "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+      "dev": true
+    },
+    "node_modules/file-uri-to-path": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
+      "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
+      "dev": true,
+      "optional": true
+    },
+    "node_modules/fill-range": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+      "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+      "dev": true,
+      "dependencies": {
+        "extend-shallow": "^2.0.1",
+        "is-number": "^3.0.0",
+        "repeat-string": "^1.6.1",
+        "to-regex-range": "^2.1.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/find-up": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
+      "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
+      "dev": true,
+      "dependencies": {
+        "path-exists": "^2.0.0",
+        "pinkie-promise": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/findup-sync": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz",
+      "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==",
+      "dev": true,
+      "dependencies": {
+        "detect-file": "^1.0.0",
+        "is-glob": "^4.0.0",
+        "micromatch": "^3.0.4",
+        "resolve-dir": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/fined": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz",
+      "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==",
+      "dev": true,
+      "dependencies": {
+        "expand-tilde": "^2.0.2",
+        "is-plain-object": "^2.0.3",
+        "object.defaults": "^1.1.0",
+        "object.pick": "^1.2.0",
+        "parse-filepath": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/fined/node_modules/is-plain-object": {
+      "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,
+      "dependencies": {
+        "isobject": "^3.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/flagged-respawn": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz",
+      "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/flush-write-stream": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz",
+      "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==",
+      "dev": true,
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "readable-stream": "^2.3.6"
+      }
+    },
+    "node_modules/for-in": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
+      "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/for-own": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz",
+      "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=",
+      "dev": true,
+      "dependencies": {
+        "for-in": "^1.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/forever-agent": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+      "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
+      "dev": true,
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/form-data": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
+      "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
+      "dev": true,
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.6",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 0.12"
+      }
+    },
+    "node_modules/fragment-cache": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
+      "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
+      "dev": true,
+      "dependencies": {
+        "map-cache": "^0.2.2"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/from": {
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz",
+      "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=",
+      "dev": true
+    },
+    "node_modules/fs-mkdirp-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz",
+      "integrity": "sha1-C3gV/DIBxqaeFNuYzgmMFpNSWes=",
+      "dev": true,
+      "dependencies": {
+        "graceful-fs": "^4.1.11",
+        "through2": "^2.0.3"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+      "dev": true
+    },
+    "node_modules/fsevents": {
+      "version": "1.2.13",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
+      "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
+      "deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.",
+      "dev": true,
+      "hasInstallScript": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "dependencies": {
+        "bindings": "^1.5.0",
+        "nan": "^2.12.1"
+      },
+      "engines": {
+        "node": ">= 4.0"
+      }
+    },
+    "node_modules/function-bind": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+      "dev": true
+    },
+    "node_modules/get-caller-file": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
+      "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==",
+      "dev": true
+    },
+    "node_modules/get-intrinsic": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
+      "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
+      "dev": true,
+      "dependencies": {
+        "function-bind": "^1.1.1",
+        "has": "^1.0.3",
+        "has-symbols": "^1.0.1"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/get-value": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
+      "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/getpass": {
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
+      "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
+      "dev": true,
+      "dependencies": {
+        "assert-plus": "^1.0.0"
+      }
+    },
+    "node_modules/glob": {
+      "version": "7.1.6",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+      "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+      "dev": true,
+      "dependencies": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.0.4",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      },
+      "engines": {
+        "node": "*"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/glob-parent": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
+      "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
+      "dev": true,
+      "dependencies": {
+        "is-glob": "^3.1.0",
+        "path-dirname": "^1.0.0"
+      }
+    },
+    "node_modules/glob-parent/node_modules/is-glob": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+      "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
+      "dev": true,
+      "dependencies": {
+        "is-extglob": "^2.1.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/glob-stream": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz",
+      "integrity": "sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ=",
+      "dev": true,
+      "dependencies": {
+        "extend": "^3.0.0",
+        "glob": "^7.1.1",
+        "glob-parent": "^3.1.0",
+        "is-negated-glob": "^1.0.0",
+        "ordered-read-streams": "^1.0.0",
+        "pumpify": "^1.3.5",
+        "readable-stream": "^2.1.5",
+        "remove-trailing-separator": "^1.0.1",
+        "to-absolute-glob": "^2.0.0",
+        "unique-stream": "^2.0.2"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/glob-watcher": {
+      "version": "5.0.5",
+      "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.5.tgz",
+      "integrity": "sha512-zOZgGGEHPklZNjZQaZ9f41i7F2YwE+tS5ZHrDhbBCk3stwahn5vQxnFmBJZHoYdusR6R1bLSXeGUy/BhctwKzw==",
+      "dev": true,
+      "dependencies": {
+        "anymatch": "^2.0.0",
+        "async-done": "^1.2.0",
+        "chokidar": "^2.0.0",
+        "is-negated-glob": "^1.0.0",
+        "just-debounce": "^1.0.0",
+        "normalize-path": "^3.0.0",
+        "object.defaults": "^1.1.0"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/global-modules": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz",
+      "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==",
+      "dev": true,
+      "dependencies": {
+        "global-prefix": "^1.0.1",
+        "is-windows": "^1.0.1",
+        "resolve-dir": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/global-prefix": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz",
+      "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=",
+      "dev": true,
+      "dependencies": {
+        "expand-tilde": "^2.0.2",
+        "homedir-polyfill": "^1.0.1",
+        "ini": "^1.3.4",
+        "is-windows": "^1.0.1",
+        "which": "^1.2.14"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/glogg": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz",
+      "integrity": "sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA==",
+      "dev": true,
+      "dependencies": {
+        "sparkles": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/graceful-fs": {
+      "version": "4.2.6",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz",
+      "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==",
+      "dev": true
+    },
+    "node_modules/gulp": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.2.tgz",
+      "integrity": "sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA==",
+      "dev": true,
+      "dependencies": {
+        "glob-watcher": "^5.0.3",
+        "gulp-cli": "^2.2.0",
+        "undertaker": "^1.2.1",
+        "vinyl-fs": "^3.0.0"
+      },
+      "bin": {
+        "gulp": "bin/gulp.js"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/gulp-cli": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.3.0.tgz",
+      "integrity": "sha512-zzGBl5fHo0EKSXsHzjspp3y5CONegCm8ErO5Qh0UzFzk2y4tMvzLWhoDokADbarfZRL2pGpRp7yt6gfJX4ph7A==",
+      "dev": true,
+      "dependencies": {
+        "ansi-colors": "^1.0.1",
+        "archy": "^1.0.0",
+        "array-sort": "^1.0.0",
+        "color-support": "^1.1.3",
+        "concat-stream": "^1.6.0",
+        "copy-props": "^2.0.1",
+        "fancy-log": "^1.3.2",
+        "gulplog": "^1.0.0",
+        "interpret": "^1.4.0",
+        "isobject": "^3.0.1",
+        "liftoff": "^3.1.0",
+        "matchdep": "^2.0.0",
+        "mute-stdout": "^1.0.0",
+        "pretty-hrtime": "^1.0.0",
+        "replace-homedir": "^1.0.0",
+        "semver-greatest-satisfied-range": "^1.1.0",
+        "v8flags": "^3.2.0",
+        "yargs": "^7.1.0"
+      },
+      "bin": {
+        "gulp": "bin/gulp.js"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/gulplog": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz",
+      "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=",
+      "dev": true,
+      "dependencies": {
+        "glogg": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/har-schema": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
+      "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/har-validator": {
+      "version": "5.1.5",
+      "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
+      "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
+      "deprecated": "this library is no longer supported",
+      "dev": true,
+      "dependencies": {
+        "ajv": "^6.12.3",
+        "har-schema": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/has": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+      "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+      "dev": true,
+      "dependencies": {
+        "function-bind": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 0.4.0"
+      }
+    },
+    "node_modules/has-symbols": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
+      "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-value": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
+      "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=",
+      "dev": true,
+      "dependencies": {
+        "get-value": "^2.0.6",
+        "has-values": "^1.0.0",
+        "isobject": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/has-values": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
+      "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=",
+      "dev": true,
+      "dependencies": {
+        "is-number": "^3.0.0",
+        "kind-of": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/has-values/node_modules/kind-of": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
+      "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
+      "dev": true,
+      "dependencies": {
+        "is-buffer": "^1.1.5"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/homedir-polyfill": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz",
+      "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==",
+      "dev": true,
+      "dependencies": {
+        "parse-passwd": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/hosted-git-info": {
+      "version": "2.8.9",
+      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
+      "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
+      "dev": true
+    },
+    "node_modules/html-encoding-sniffer": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz",
+      "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==",
+      "dev": true,
+      "dependencies": {
+        "whatwg-encoding": "^1.0.1"
+      }
+    },
+    "node_modules/html-tags": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-1.2.0.tgz",
+      "integrity": "sha1-x43mW1Zjqll5id0rerSSANfk25g=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/http-signature": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
+      "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
+      "dev": true,
+      "dependencies": {
+        "assert-plus": "^1.0.0",
+        "jsprim": "^1.2.2",
+        "sshpk": "^1.7.0"
+      },
+      "engines": {
+        "node": ">=0.8",
+        "npm": ">=1.3.7"
+      }
+    },
+    "node_modules/iconv-lite": {
+      "version": "0.4.24",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+      "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+      "dev": true,
+      "dependencies": {
+        "safer-buffer": ">= 2.1.2 < 3"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/indexes-of": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz",
+      "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=",
+      "dev": true
+    },
+    "node_modules/inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+      "dev": true,
+      "dependencies": {
+        "once": "^1.3.0",
+        "wrappy": "1"
+      }
+    },
+    "node_modules/inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+      "dev": true
+    },
+    "node_modules/ini": {
+      "version": "1.3.8",
+      "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+      "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+      "dev": true
+    },
+    "node_modules/interpret": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz",
+      "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/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,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-absolute": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz",
+      "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==",
+      "dev": true,
+      "dependencies": {
+        "is-relative": "^1.0.0",
+        "is-windows": "^1.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-absolute-url": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz",
+      "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-accessor-descriptor": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+      "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+      "dev": true,
+      "dependencies": {
+        "kind-of": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-accessor-descriptor/node_modules/kind-of": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+      "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-arrayish": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+      "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
+      "dev": true
+    },
+    "node_modules/is-binary-path": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
+      "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
+      "dev": true,
+      "dependencies": {
+        "binary-extensions": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-buffer": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+      "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
+      "dev": true
+    },
+    "node_modules/is-core-module": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz",
+      "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==",
+      "dev": true,
+      "dependencies": {
+        "has": "^1.0.3"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-data-descriptor": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+      "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+      "dev": true,
+      "dependencies": {
+        "kind-of": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-data-descriptor/node_modules/kind-of": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+      "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-descriptor": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+      "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+      "dev": true,
+      "dependencies": {
+        "is-accessor-descriptor": "^1.0.0",
+        "is-data-descriptor": "^1.0.0",
+        "kind-of": "^6.0.2"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-descriptor/node_modules/kind-of": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+      "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-extendable": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+      "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/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,
+      "dependencies": {
+        "number-is-nan": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-glob": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+      "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+      "dev": true,
+      "dependencies": {
+        "is-extglob": "^2.1.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-html": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/is-html/-/is-html-1.1.0.tgz",
+      "integrity": "sha1-4E8cGNOUhRETlvmgJz6rUa8hhGQ=",
+      "dev": true,
+      "dependencies": {
+        "html-tags": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-negated-glob": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz",
+      "integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-number": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+      "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+      "dev": true,
+      "dependencies": {
+        "kind-of": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-number/node_modules/kind-of": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+      "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+      "dev": true,
+      "dependencies": {
+        "is-buffer": "^1.1.5"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-plain-object": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
+      "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-relative": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz",
+      "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==",
+      "dev": true,
+      "dependencies": {
+        "is-unc-path": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-typedarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+      "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
+      "dev": true
+    },
+    "node_modules/is-unc-path": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz",
+      "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==",
+      "dev": true,
+      "dependencies": {
+        "unc-path-regex": "^0.1.2"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-utf8": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
+      "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=",
+      "dev": true
+    },
+    "node_modules/is-valid-glob": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-1.0.0.tgz",
+      "integrity": "sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-windows": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
+      "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/isarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+      "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+      "dev": true
+    },
+    "node_modules/isexe": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+      "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+      "dev": true
+    },
+    "node_modules/isobject": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+      "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/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
+    },
+    "node_modules/jsbn": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+      "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
+      "dev": true
+    },
+    "node_modules/jsdom": {
+      "version": "14.1.0",
+      "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-14.1.0.tgz",
+      "integrity": "sha512-O901mfJSuTdwU2w3Sn+74T+RnDVP+FuV5fH8tcPWyqrseRAb0s5xOtPgCFiPOtLcyK7CLIJwPyD83ZqQWvA5ng==",
+      "dev": true,
+      "dependencies": {
+        "abab": "^2.0.0",
+        "acorn": "^6.0.4",
+        "acorn-globals": "^4.3.0",
+        "array-equal": "^1.0.0",
+        "cssom": "^0.3.4",
+        "cssstyle": "^1.1.1",
+        "data-urls": "^1.1.0",
+        "domexception": "^1.0.1",
+        "escodegen": "^1.11.0",
+        "html-encoding-sniffer": "^1.0.2",
+        "nwsapi": "^2.1.3",
+        "parse5": "5.1.0",
+        "pn": "^1.1.0",
+        "request": "^2.88.0",
+        "request-promise-native": "^1.0.5",
+        "saxes": "^3.1.9",
+        "symbol-tree": "^3.2.2",
+        "tough-cookie": "^2.5.0",
+        "w3c-hr-time": "^1.0.1",
+        "w3c-xmlserializer": "^1.1.2",
+        "webidl-conversions": "^4.0.2",
+        "whatwg-encoding": "^1.0.5",
+        "whatwg-mimetype": "^2.3.0",
+        "whatwg-url": "^7.0.0",
+        "ws": "^6.1.2",
+        "xml-name-validator": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/json-schema": {
+      "version": "0.2.3",
+      "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
+      "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
+      "dev": true
+    },
+    "node_modules/json-schema-traverse": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+      "dev": true
+    },
+    "node_modules/json-stable-stringify-without-jsonify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+      "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
+      "dev": true
+    },
+    "node_modules/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
+    },
+    "node_modules/jsprim": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
+      "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
+      "dev": true,
+      "engines": [
+        "node >=0.6.0"
+      ],
+      "dependencies": {
+        "assert-plus": "1.0.0",
+        "extsprintf": "1.3.0",
+        "json-schema": "0.2.3",
+        "verror": "1.10.0"
+      }
+    },
+    "node_modules/just-debounce": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.1.0.tgz",
+      "integrity": "sha512-qpcRocdkUmf+UTNBYx5w6dexX5J31AKK1OmPwH630a83DdVVUIngk55RSAiIGpQyoH0dlr872VHfPjnQnK1qDQ==",
+      "dev": true
+    },
+    "node_modules/kind-of": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+      "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/last-run": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/last-run/-/last-run-1.1.1.tgz",
+      "integrity": "sha1-RblpQsF7HHnHchmCWbqUO+v4yls=",
+      "dev": true,
+      "dependencies": {
+        "default-resolution": "^2.0.0",
+        "es6-weak-map": "^2.0.1"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/lazystream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz",
+      "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=",
+      "dev": true,
+      "dependencies": {
+        "readable-stream": "^2.0.5"
+      },
+      "engines": {
+        "node": ">= 0.6.3"
+      }
+    },
+    "node_modules/lcid": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
+      "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
+      "dev": true,
+      "dependencies": {
+        "invert-kv": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/lead": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/lead/-/lead-1.0.0.tgz",
+      "integrity": "sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI=",
+      "dev": true,
+      "dependencies": {
+        "flush-write-stream": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/levn": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+      "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
+      "dev": true,
+      "dependencies": {
+        "prelude-ls": "~1.1.2",
+        "type-check": "~0.3.2"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/liftoff": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz",
+      "integrity": "sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog==",
+      "dev": true,
+      "dependencies": {
+        "extend": "^3.0.0",
+        "findup-sync": "^3.0.0",
+        "fined": "^1.0.1",
+        "flagged-respawn": "^1.0.0",
+        "is-plain-object": "^2.0.4",
+        "object.map": "^1.0.0",
+        "rechoir": "^0.6.2",
+        "resolve": "^1.1.7"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/liftoff/node_modules/is-plain-object": {
+      "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,
+      "dependencies": {
+        "isobject": "^3.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/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,
+      "dependencies": {
+        "graceful-fs": "^4.1.2",
+        "parse-json": "^2.2.0",
+        "pify": "^2.0.0",
+        "pinkie-promise": "^2.0.0",
+        "strip-bom": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/lodash": {
+      "version": "4.17.21",
+      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+      "dev": true
+    },
+    "node_modules/lodash.sortby": {
+      "version": "4.7.0",
+      "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
+      "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=",
+      "dev": true
+    },
+    "node_modules/make-iterator": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz",
+      "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==",
+      "dev": true,
+      "dependencies": {
+        "kind-of": "^6.0.2"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/make-iterator/node_modules/kind-of": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+      "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/map-cache": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
+      "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/map-stream": {
+      "version": "0.0.7",
+      "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz",
+      "integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=",
+      "dev": true
+    },
+    "node_modules/map-visit": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
+      "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=",
+      "dev": true,
+      "dependencies": {
+        "object-visit": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/matchdep": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz",
+      "integrity": "sha1-xvNINKDY28OzfCfui7yyfHd1WC4=",
+      "dev": true,
+      "dependencies": {
+        "findup-sync": "^2.0.0",
+        "micromatch": "^3.0.4",
+        "resolve": "^1.4.0",
+        "stack-trace": "0.0.10"
+      },
+      "engines": {
+        "node": ">= 0.10.0"
+      }
+    },
+    "node_modules/matchdep/node_modules/findup-sync": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz",
+      "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=",
+      "dev": true,
+      "dependencies": {
+        "detect-file": "^1.0.0",
+        "is-glob": "^3.1.0",
+        "micromatch": "^3.0.4",
+        "resolve-dir": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/matchdep/node_modules/is-glob": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+      "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
+      "dev": true,
+      "dependencies": {
+        "is-extglob": "^2.1.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/micromatch": {
+      "version": "3.1.10",
+      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+      "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+      "dev": true,
+      "dependencies": {
+        "arr-diff": "^4.0.0",
+        "array-unique": "^0.3.2",
+        "braces": "^2.3.1",
+        "define-property": "^2.0.2",
+        "extend-shallow": "^3.0.2",
+        "extglob": "^2.0.4",
+        "fragment-cache": "^0.2.1",
+        "kind-of": "^6.0.2",
+        "nanomatch": "^1.2.9",
+        "object.pick": "^1.3.0",
+        "regex-not": "^1.0.0",
+        "snapdragon": "^0.8.1",
+        "to-regex": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/micromatch/node_modules/extend-shallow": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+      "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+      "dev": true,
+      "dependencies": {
+        "assign-symbols": "^1.0.0",
+        "is-extendable": "^1.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/micromatch/node_modules/is-extendable": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+      "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+      "dev": true,
+      "dependencies": {
+        "is-plain-object": "^2.0.4"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/micromatch/node_modules/is-plain-object": {
+      "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,
+      "dependencies": {
+        "isobject": "^3.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/micromatch/node_modules/kind-of": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+      "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/mime-db": {
+      "version": "1.46.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.46.0.tgz",
+      "integrity": "sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mime-types": {
+      "version": "2.1.29",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.29.tgz",
+      "integrity": "sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==",
+      "dev": true,
+      "dependencies": {
+        "mime-db": "1.46.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/minimatch": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+      "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+      "dev": true,
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/mixin-deep": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
+      "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
+      "dev": true,
+      "dependencies": {
+        "for-in": "^1.0.2",
+        "is-extendable": "^1.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/mixin-deep/node_modules/is-extendable": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+      "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+      "dev": true,
+      "dependencies": {
+        "is-plain-object": "^2.0.4"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/mixin-deep/node_modules/is-plain-object": {
+      "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,
+      "dependencies": {
+        "isobject": "^3.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/monaco-editor": {
+      "version": "0.23.0",
+      "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.23.0.tgz",
+      "integrity": "sha512-q+CP5zMR/aFiMTE9QlIavGyGicKnG2v/H8qVvybLzeFsARM8f6G9fL0sMST2tyVYCwDKkGamZUI6647A0jR/Lg==",
+      "dev": true
+    },
+    "node_modules/ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+      "dev": true
+    },
+    "node_modules/mute-stdout": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz",
+      "integrity": "sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/nan": {
+      "version": "2.14.2",
+      "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz",
+      "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==",
+      "dev": true,
+      "optional": true
+    },
+    "node_modules/nanomatch": {
+      "version": "1.2.13",
+      "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
+      "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
+      "dev": true,
+      "dependencies": {
+        "arr-diff": "^4.0.0",
+        "array-unique": "^0.3.2",
+        "define-property": "^2.0.2",
+        "extend-shallow": "^3.0.2",
+        "fragment-cache": "^0.2.1",
+        "is-windows": "^1.0.2",
+        "kind-of": "^6.0.2",
+        "object.pick": "^1.3.0",
+        "regex-not": "^1.0.0",
+        "snapdragon": "^0.8.1",
+        "to-regex": "^3.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/nanomatch/node_modules/extend-shallow": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+      "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+      "dev": true,
+      "dependencies": {
+        "assign-symbols": "^1.0.0",
+        "is-extendable": "^1.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/nanomatch/node_modules/is-extendable": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+      "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+      "dev": true,
+      "dependencies": {
+        "is-plain-object": "^2.0.4"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/nanomatch/node_modules/is-plain-object": {
+      "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,
+      "dependencies": {
+        "isobject": "^3.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/nanomatch/node_modules/kind-of": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+      "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/next-tick": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
+      "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=",
+      "dev": true
+    },
+    "node_modules/normalize-package-data": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+      "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
+      "dev": true,
+      "dependencies": {
+        "hosted-git-info": "^2.1.4",
+        "resolve": "^1.10.0",
+        "semver": "2 || 3 || 4 || 5",
+        "validate-npm-package-license": "^3.0.1"
+      }
+    },
+    "node_modules/normalize-path": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/now-and-later": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.1.tgz",
+      "integrity": "sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ==",
+      "dev": true,
+      "dependencies": {
+        "once": "^1.3.2"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/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,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/nwsapi": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz",
+      "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==",
+      "dev": true
+    },
+    "node_modules/oauth-sign": {
+      "version": "0.9.0",
+      "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
+      "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
+      "dev": true,
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/object-copy": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
+      "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=",
+      "dev": true,
+      "dependencies": {
+        "copy-descriptor": "^0.1.0",
+        "define-property": "^0.2.5",
+        "kind-of": "^3.0.3"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/object-copy/node_modules/define-property": {
+      "version": "0.2.5",
+      "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+      "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+      "dev": true,
+      "dependencies": {
+        "is-descriptor": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/object-copy/node_modules/is-accessor-descriptor": {
+      "version": "0.1.6",
+      "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+      "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+      "dev": true,
+      "dependencies": {
+        "kind-of": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/object-copy/node_modules/is-data-descriptor": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+      "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+      "dev": true,
+      "dependencies": {
+        "kind-of": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/object-copy/node_modules/is-descriptor": {
+      "version": "0.1.6",
+      "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+      "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+      "dev": true,
+      "dependencies": {
+        "is-accessor-descriptor": "^0.1.6",
+        "is-data-descriptor": "^0.1.4",
+        "kind-of": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/object-copy/node_modules/is-descriptor/node_modules/kind-of": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+      "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/object-copy/node_modules/kind-of": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+      "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+      "dev": true,
+      "dependencies": {
+        "is-buffer": "^1.1.5"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/object-keys": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+      "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/object-visit": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
+      "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=",
+      "dev": true,
+      "dependencies": {
+        "isobject": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/object.assign": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
+      "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
+      "dev": true,
+      "dependencies": {
+        "call-bind": "^1.0.0",
+        "define-properties": "^1.1.3",
+        "has-symbols": "^1.0.1",
+        "object-keys": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/object.defaults": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz",
+      "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=",
+      "dev": true,
+      "dependencies": {
+        "array-each": "^1.0.1",
+        "array-slice": "^1.0.0",
+        "for-own": "^1.0.0",
+        "isobject": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/object.map": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz",
+      "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=",
+      "dev": true,
+      "dependencies": {
+        "for-own": "^1.0.0",
+        "make-iterator": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/object.pick": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
+      "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=",
+      "dev": true,
+      "dependencies": {
+        "isobject": "^3.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/object.reduce": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/object.reduce/-/object.reduce-1.0.1.tgz",
+      "integrity": "sha1-b+NI8qx/oPlcpiEiZZkJaCW7A60=",
+      "dev": true,
+      "dependencies": {
+        "for-own": "^1.0.0",
+        "make-iterator": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+      "dev": true,
+      "dependencies": {
+        "wrappy": "1"
+      }
+    },
+    "node_modules/optionator": {
+      "version": "0.8.3",
+      "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
+      "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
+      "dev": true,
+      "dependencies": {
+        "deep-is": "~0.1.3",
+        "fast-levenshtein": "~2.0.6",
+        "levn": "~0.3.0",
+        "prelude-ls": "~1.1.2",
+        "type-check": "~0.3.2",
+        "word-wrap": "~1.2.3"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/ordered-read-streams": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz",
+      "integrity": "sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4=",
+      "dev": true,
+      "dependencies": {
+        "readable-stream": "^2.0.1"
+      }
+    },
+    "node_modules/os-locale": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
+      "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
+      "dev": true,
+      "dependencies": {
+        "lcid": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/parse-filepath": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz",
+      "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=",
+      "dev": true,
+      "dependencies": {
+        "is-absolute": "^1.0.0",
+        "map-cache": "^0.2.0",
+        "path-root": "^0.1.1"
+      },
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/parse-json": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
+      "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
+      "dev": true,
+      "dependencies": {
+        "error-ex": "^1.2.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/parse-node-version": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz",
+      "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/parse-passwd": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz",
+      "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/parse5": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz",
+      "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==",
+      "dev": true
+    },
+    "node_modules/pascalcase": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
+      "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/path-dirname": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
+      "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=",
+      "dev": true
+    },
+    "node_modules/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,
+      "dependencies": {
+        "pinkie-promise": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/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,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/path-parse": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+      "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+      "dev": true
+    },
+    "node_modules/path-root": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz",
+      "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=",
+      "dev": true,
+      "dependencies": {
+        "path-root-regex": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/path-root-regex": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz",
+      "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/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,
+      "dependencies": {
+        "graceful-fs": "^4.1.2",
+        "pify": "^2.0.0",
+        "pinkie-promise": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/pause-stream": {
+      "version": "0.0.11",
+      "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz",
+      "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=",
+      "dev": true,
+      "dependencies": {
+        "through": "~2.3"
+      }
+    },
+    "node_modules/performance-now": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+      "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
+      "dev": true
+    },
+    "node_modules/picocolors": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
+      "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
+      "dev": true
+    },
+    "node_modules/pify": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+      "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/pinkie": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+      "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/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,
+      "dependencies": {
+        "pinkie": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/pn": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz",
+      "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==",
+      "dev": true
+    },
+    "node_modules/posix-character-classes": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
+      "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/postcss": {
+      "version": "7.0.39",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
+      "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
+      "dev": true,
+      "dependencies": {
+        "picocolors": "^0.2.1",
+        "source-map": "^0.6.1"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/postcss/"
+      }
+    },
+    "node_modules/postcss-selector-parser": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz",
+      "integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==",
+      "dev": true,
+      "dependencies": {
+        "cssesc": "^3.0.0",
+        "indexes-of": "^1.0.1",
+        "uniq": "^1.0.1"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/prelude-ls": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+      "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/pretty-hrtime": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz",
+      "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/process-nextick-args": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+      "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+      "dev": true
+    },
+    "node_modules/psl": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
+      "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==",
+      "dev": true
+    },
+    "node_modules/pump": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
+      "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
+      "dev": true,
+      "dependencies": {
+        "end-of-stream": "^1.1.0",
+        "once": "^1.3.1"
+      }
+    },
+    "node_modules/pumpify": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz",
+      "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==",
+      "dev": true,
+      "dependencies": {
+        "duplexify": "^3.6.0",
+        "inherits": "^2.0.3",
+        "pump": "^2.0.0"
+      }
+    },
+    "node_modules/punycode": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+      "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/qs": {
+      "version": "6.5.2",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
+      "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.6"
+      }
+    },
+    "node_modules/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,
+      "dependencies": {
+        "load-json-file": "^1.0.0",
+        "normalize-package-data": "^2.3.2",
+        "path-type": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/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,
+      "dependencies": {
+        "find-up": "^1.0.0",
+        "read-pkg": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/readable-stream": {
+      "version": "2.3.7",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+      "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+      "dev": true,
+      "dependencies": {
+        "core-util-is": "~1.0.0",
+        "inherits": "~2.0.3",
+        "isarray": "~1.0.0",
+        "process-nextick-args": "~2.0.0",
+        "safe-buffer": "~5.1.1",
+        "string_decoder": "~1.1.1",
+        "util-deprecate": "~1.0.1"
+      }
+    },
+    "node_modules/readdirp": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
+      "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
+      "dev": true,
+      "dependencies": {
+        "graceful-fs": "^4.1.11",
+        "micromatch": "^3.1.10",
+        "readable-stream": "^2.0.2"
+      },
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/rechoir": {
+      "version": "0.6.2",
+      "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
+      "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=",
+      "dev": true,
+      "dependencies": {
+        "resolve": "^1.1.6"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/regex-not": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
+      "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
+      "dev": true,
+      "dependencies": {
+        "extend-shallow": "^3.0.2",
+        "safe-regex": "^1.1.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/regex-not/node_modules/extend-shallow": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+      "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+      "dev": true,
+      "dependencies": {
+        "assign-symbols": "^1.0.0",
+        "is-extendable": "^1.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/regex-not/node_modules/is-extendable": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+      "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+      "dev": true,
+      "dependencies": {
+        "is-plain-object": "^2.0.4"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/regex-not/node_modules/is-plain-object": {
+      "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,
+      "dependencies": {
+        "isobject": "^3.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/remove-bom-buffer": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz",
+      "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==",
+      "dev": true,
+      "dependencies": {
+        "is-buffer": "^1.1.5",
+        "is-utf8": "^0.2.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/remove-bom-stream": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz",
+      "integrity": "sha1-BfGlk/FuQuH7kOv1nejlaVJflSM=",
+      "dev": true,
+      "dependencies": {
+        "remove-bom-buffer": "^3.0.0",
+        "safe-buffer": "^5.1.0",
+        "through2": "^2.0.3"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/remove-trailing-separator": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
+      "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=",
+      "dev": true
+    },
+    "node_modules/repeat-element": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz",
+      "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/repeat-string": {
+      "version": "1.6.1",
+      "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
+      "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/replace-ext": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz",
+      "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/replace-homedir": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/replace-homedir/-/replace-homedir-1.0.0.tgz",
+      "integrity": "sha1-6H9tUTuSjd6AgmDBK+f+xv9ueYw=",
+      "dev": true,
+      "dependencies": {
+        "homedir-polyfill": "^1.0.1",
+        "is-absolute": "^1.0.0",
+        "remove-trailing-separator": "^1.1.0"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/request": {
+      "version": "2.88.2",
+      "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
+      "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
+      "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142",
+      "dev": true,
+      "dependencies": {
+        "aws-sign2": "~0.7.0",
+        "aws4": "^1.8.0",
+        "caseless": "~0.12.0",
+        "combined-stream": "~1.0.6",
+        "extend": "~3.0.2",
+        "forever-agent": "~0.6.1",
+        "form-data": "~2.3.2",
+        "har-validator": "~5.1.3",
+        "http-signature": "~1.2.0",
+        "is-typedarray": "~1.0.0",
+        "isstream": "~0.1.2",
+        "json-stringify-safe": "~5.0.1",
+        "mime-types": "~2.1.19",
+        "oauth-sign": "~0.9.0",
+        "performance-now": "^2.1.0",
+        "qs": "~6.5.2",
+        "safe-buffer": "^5.1.2",
+        "tough-cookie": "~2.5.0",
+        "tunnel-agent": "^0.6.0",
+        "uuid": "^3.3.2"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/request-promise-core": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz",
+      "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==",
+      "dev": true,
+      "dependencies": {
+        "lodash": "^4.17.19"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      },
+      "peerDependencies": {
+        "request": "^2.34"
+      }
+    },
+    "node_modules/request-promise-native": {
+      "version": "1.0.9",
+      "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz",
+      "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==",
+      "deprecated": "request-promise-native has been deprecated because it extends the now deprecated request package, see https://github.com/request/request/issues/3142",
+      "dev": true,
+      "dependencies": {
+        "request-promise-core": "1.1.4",
+        "stealthy-require": "^1.1.1",
+        "tough-cookie": "^2.3.3"
+      },
+      "engines": {
+        "node": ">=0.12.0"
+      },
+      "peerDependencies": {
+        "request": "^2.34"
+      }
+    },
+    "node_modules/require-directory": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+      "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/require-main-filename": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
+      "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=",
+      "dev": true
+    },
+    "node_modules/resolve": {
+      "version": "1.20.0",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
+      "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
+      "dev": true,
+      "dependencies": {
+        "is-core-module": "^2.2.0",
+        "path-parse": "^1.0.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/resolve-dir": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz",
+      "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=",
+      "dev": true,
+      "dependencies": {
+        "expand-tilde": "^2.0.0",
+        "global-modules": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/resolve-options": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-1.1.0.tgz",
+      "integrity": "sha1-MrueOcBtZzONyTeMDW1gdFZq0TE=",
+      "dev": true,
+      "dependencies": {
+        "value-or-function": "^3.0.0"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/resolve-url": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
+      "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=",
+      "deprecated": "https://github.com/lydell/resolve-url#deprecated",
+      "dev": true
+    },
+    "node_modules/ret": {
+      "version": "0.1.15",
+      "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
+      "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.12"
+      }
+    },
+    "node_modules/rimraf": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+      "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+      "dev": true,
+      "dependencies": {
+        "glob": "^7.1.3"
+      },
+      "bin": {
+        "rimraf": "bin.js"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/safe-buffer": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+      "dev": true
+    },
+    "node_modules/safe-regex": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
+      "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
+      "dev": true,
+      "dependencies": {
+        "ret": "~0.1.10"
+      }
+    },
+    "node_modules/safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+      "dev": true
+    },
+    "node_modules/saxes": {
+      "version": "3.1.11",
+      "resolved": "https://registry.npmjs.org/saxes/-/saxes-3.1.11.tgz",
+      "integrity": "sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g==",
+      "dev": true,
+      "dependencies": {
+        "xmlchars": "^2.1.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/semver": {
+      "version": "5.7.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+      "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+      "dev": true,
+      "bin": {
+        "semver": "bin/semver"
+      }
+    },
+    "node_modules/semver-greatest-satisfied-range": {
+      "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,
+      "dependencies": {
+        "sver-compat": "^1.5.0"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/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
+    },
+    "node_modules/set-value": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
+      "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
+      "dev": true,
+      "dependencies": {
+        "extend-shallow": "^2.0.1",
+        "is-extendable": "^0.1.1",
+        "is-plain-object": "^2.0.3",
+        "split-string": "^3.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/set-value/node_modules/is-plain-object": {
+      "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,
+      "dependencies": {
+        "isobject": "^3.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/snapdragon": {
+      "version": "0.8.2",
+      "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
+      "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
+      "dev": true,
+      "dependencies": {
+        "base": "^0.11.1",
+        "debug": "^2.2.0",
+        "define-property": "^0.2.5",
+        "extend-shallow": "^2.0.1",
+        "map-cache": "^0.2.2",
+        "source-map": "^0.5.6",
+        "source-map-resolve": "^0.5.0",
+        "use": "^3.1.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/snapdragon-node": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
+      "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
+      "dev": true,
+      "dependencies": {
+        "define-property": "^1.0.0",
+        "isobject": "^3.0.0",
+        "snapdragon-util": "^3.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/snapdragon-node/node_modules/define-property": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+      "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+      "dev": true,
+      "dependencies": {
+        "is-descriptor": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/snapdragon-util": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
+      "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
+      "dev": true,
+      "dependencies": {
+        "kind-of": "^3.2.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/snapdragon-util/node_modules/kind-of": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+      "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+      "dev": true,
+      "dependencies": {
+        "is-buffer": "^1.1.5"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/snapdragon/node_modules/define-property": {
+      "version": "0.2.5",
+      "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+      "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+      "dev": true,
+      "dependencies": {
+        "is-descriptor": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/snapdragon/node_modules/is-accessor-descriptor": {
+      "version": "0.1.6",
+      "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+      "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+      "dev": true,
+      "dependencies": {
+        "kind-of": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/snapdragon/node_modules/is-accessor-descriptor/node_modules/kind-of": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+      "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+      "dev": true,
+      "dependencies": {
+        "is-buffer": "^1.1.5"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/snapdragon/node_modules/is-data-descriptor": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+      "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+      "dev": true,
+      "dependencies": {
+        "kind-of": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/snapdragon/node_modules/is-data-descriptor/node_modules/kind-of": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+      "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+      "dev": true,
+      "dependencies": {
+        "is-buffer": "^1.1.5"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/snapdragon/node_modules/is-descriptor": {
+      "version": "0.1.6",
+      "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+      "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+      "dev": true,
+      "dependencies": {
+        "is-accessor-descriptor": "^0.1.6",
+        "is-data-descriptor": "^0.1.4",
+        "kind-of": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/snapdragon/node_modules/source-map": {
+      "version": "0.5.7",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+      "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/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,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/source-map-resolve": {
+      "version": "0.5.3",
+      "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz",
+      "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==",
+      "dev": true,
+      "dependencies": {
+        "atob": "^2.1.2",
+        "decode-uri-component": "^0.2.0",
+        "resolve-url": "^0.2.1",
+        "source-map-url": "^0.4.0",
+        "urix": "^0.1.0"
+      }
+    },
+    "node_modules/source-map-url": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz",
+      "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==",
+      "dev": true
+    },
+    "node_modules/sparkles": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz",
+      "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/spdx-correct": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
+      "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
+      "dev": true,
+      "dependencies": {
+        "spdx-expression-parse": "^3.0.0",
+        "spdx-license-ids": "^3.0.0"
+      }
+    },
+    "node_modules/spdx-exceptions": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
+      "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
+      "dev": true
+    },
+    "node_modules/spdx-expression-parse": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+      "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
+      "dev": true,
+      "dependencies": {
+        "spdx-exceptions": "^2.1.0",
+        "spdx-license-ids": "^3.0.0"
+      }
+    },
+    "node_modules/spdx-license-ids": {
+      "version": "3.0.7",
+      "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz",
+      "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==",
+      "dev": true
+    },
+    "node_modules/split": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz",
+      "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==",
+      "dev": true,
+      "dependencies": {
+        "through": "2"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/split-string": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
+      "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
+      "dev": true,
+      "dependencies": {
+        "extend-shallow": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/split-string/node_modules/extend-shallow": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+      "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+      "dev": true,
+      "dependencies": {
+        "assign-symbols": "^1.0.0",
+        "is-extendable": "^1.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/split-string/node_modules/is-extendable": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+      "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+      "dev": true,
+      "dependencies": {
+        "is-plain-object": "^2.0.4"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/split-string/node_modules/is-plain-object": {
+      "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,
+      "dependencies": {
+        "isobject": "^3.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/sshpk": {
+      "version": "1.16.1",
+      "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
+      "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
+      "dev": true,
+      "dependencies": {
+        "asn1": "~0.2.3",
+        "assert-plus": "^1.0.0",
+        "bcrypt-pbkdf": "^1.0.0",
+        "dashdash": "^1.12.0",
+        "ecc-jsbn": "~0.1.1",
+        "getpass": "^0.1.1",
+        "jsbn": "~0.1.0",
+        "safer-buffer": "^2.0.2",
+        "tweetnacl": "~0.14.0"
+      },
+      "bin": {
+        "sshpk-conv": "bin/sshpk-conv",
+        "sshpk-sign": "bin/sshpk-sign",
+        "sshpk-verify": "bin/sshpk-verify"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/stack-trace": {
+      "version": "0.0.10",
+      "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
+      "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=",
+      "dev": true,
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/static-extend": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
+      "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=",
+      "dev": true,
+      "dependencies": {
+        "define-property": "^0.2.5",
+        "object-copy": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/static-extend/node_modules/define-property": {
+      "version": "0.2.5",
+      "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+      "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+      "dev": true,
+      "dependencies": {
+        "is-descriptor": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/static-extend/node_modules/is-accessor-descriptor": {
+      "version": "0.1.6",
+      "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+      "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+      "dev": true,
+      "dependencies": {
+        "kind-of": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/static-extend/node_modules/is-accessor-descriptor/node_modules/kind-of": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+      "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+      "dev": true,
+      "dependencies": {
+        "is-buffer": "^1.1.5"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/static-extend/node_modules/is-data-descriptor": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+      "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+      "dev": true,
+      "dependencies": {
+        "kind-of": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/static-extend/node_modules/is-data-descriptor/node_modules/kind-of": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+      "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+      "dev": true,
+      "dependencies": {
+        "is-buffer": "^1.1.5"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/static-extend/node_modules/is-descriptor": {
+      "version": "0.1.6",
+      "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+      "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+      "dev": true,
+      "dependencies": {
+        "is-accessor-descriptor": "^0.1.6",
+        "is-data-descriptor": "^0.1.4",
+        "kind-of": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/stealthy-require": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
+      "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/stream-combiner": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz",
+      "integrity": "sha1-rsjLrBd7Vrb0+kec7YwZEs7lKFg=",
+      "dev": true,
+      "dependencies": {
+        "duplexer": "~0.1.1",
+        "through": "~2.3.4"
+      }
+    },
+    "node_modules/stream-exhaust": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz",
+      "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==",
+      "dev": true
+    },
+    "node_modules/stream-shift": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz",
+      "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==",
+      "dev": true
+    },
+    "node_modules/string_decoder": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+      "dev": true,
+      "dependencies": {
+        "safe-buffer": "~5.1.0"
+      }
+    },
+    "node_modules/string-width": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+      "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+      "dev": true,
+      "dependencies": {
+        "code-point-at": "^1.0.0",
+        "is-fullwidth-code-point": "^1.0.0",
+        "strip-ansi": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/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,
+      "dependencies": {
+        "ansi-regex": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/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,
+      "dependencies": {
+        "is-utf8": "^0.2.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/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,
+      "dependencies": {
+        "es6-iterator": "^2.0.1",
+        "es6-symbol": "^3.1.1"
+      }
+    },
+    "node_modules/symbol-tree": {
+      "version": "3.2.4",
+      "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
+      "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
+      "dev": true
+    },
+    "node_modules/through": {
+      "version": "2.3.8",
+      "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+      "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
+      "dev": true
+    },
+    "node_modules/through2": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
+      "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
+      "dev": true,
+      "dependencies": {
+        "readable-stream": "~2.3.6",
+        "xtend": "~4.0.1"
+      }
+    },
+    "node_modules/through2-filter": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz",
+      "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==",
+      "dev": true,
+      "dependencies": {
+        "through2": "~2.0.0",
+        "xtend": "~4.0.0"
+      }
+    },
+    "node_modules/time-stamp": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz",
+      "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/to-absolute-glob": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz",
+      "integrity": "sha1-GGX0PZ50sIItufFFt4z/fQ98hJs=",
+      "dev": true,
+      "dependencies": {
+        "is-absolute": "^1.0.0",
+        "is-negated-glob": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/to-object-path": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
+      "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=",
+      "dev": true,
+      "dependencies": {
+        "kind-of": "^3.0.2"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/to-object-path/node_modules/kind-of": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+      "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+      "dev": true,
+      "dependencies": {
+        "is-buffer": "^1.1.5"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/to-regex": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
+      "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
+      "dev": true,
+      "dependencies": {
+        "define-property": "^2.0.2",
+        "extend-shallow": "^3.0.2",
+        "regex-not": "^1.0.2",
+        "safe-regex": "^1.1.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/to-regex-range": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+      "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+      "dev": true,
+      "dependencies": {
+        "is-number": "^3.0.0",
+        "repeat-string": "^1.6.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/to-regex/node_modules/extend-shallow": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+      "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+      "dev": true,
+      "dependencies": {
+        "assign-symbols": "^1.0.0",
+        "is-extendable": "^1.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/to-regex/node_modules/is-extendable": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+      "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+      "dev": true,
+      "dependencies": {
+        "is-plain-object": "^2.0.4"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/to-regex/node_modules/is-plain-object": {
+      "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,
+      "dependencies": {
+        "isobject": "^3.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/to-through": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/to-through/-/to-through-2.0.0.tgz",
+      "integrity": "sha1-/JKtq6ByZHvAtn1rA2ZKoZUJOvY=",
+      "dev": true,
+      "dependencies": {
+        "through2": "^2.0.3"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/tough-cookie": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
+      "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
+      "dev": true,
+      "dependencies": {
+        "psl": "^1.1.28",
+        "punycode": "^2.1.1"
+      },
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/tr46": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
+      "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=",
+      "dev": true,
+      "dependencies": {
+        "punycode": "^2.1.0"
+      }
+    },
+    "node_modules/tunnel-agent": {
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+      "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
+      "dev": true,
+      "dependencies": {
+        "safe-buffer": "^5.0.1"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/tweetnacl": {
+      "version": "0.14.5",
+      "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+      "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
+      "dev": true
+    },
+    "node_modules/type": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
+      "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
+      "dev": true
+    },
+    "node_modules/type-check": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+      "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
+      "dev": true,
+      "dependencies": {
+        "prelude-ls": "~1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.8.0"
+      }
+    },
+    "node_modules/typedarray": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+      "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
+      "dev": true
+    },
+    "node_modules/unc-path-regex": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz",
+      "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/uncss": {
+      "version": "0.17.3",
+      "resolved": "https://registry.npmjs.org/uncss/-/uncss-0.17.3.tgz",
+      "integrity": "sha512-ksdDWl81YWvF/X14fOSw4iu8tESDHFIeyKIeDrK6GEVTQvqJc1WlOEXqostNwOCi3qAj++4EaLsdAgPmUbEyog==",
+      "dev": true,
+      "dependencies": {
+        "commander": "^2.20.0",
+        "glob": "^7.1.4",
+        "is-absolute-url": "^3.0.1",
+        "is-html": "^1.1.0",
+        "jsdom": "^14.1.0",
+        "lodash": "^4.17.15",
+        "postcss": "^7.0.17",
+        "postcss-selector-parser": "6.0.2",
+        "request": "^2.88.0"
+      },
+      "bin": {
+        "uncss": "bin/uncss"
+      },
+      "engines": {
+        "node": ">=6.0"
+      }
+    },
+    "node_modules/undertaker": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.3.0.tgz",
+      "integrity": "sha512-/RXwi5m/Mu3H6IHQGww3GNt1PNXlbeCuclF2QYR14L/2CHPz3DFZkvB5hZ0N/QUkiXWCACML2jXViIQEQc2MLg==",
+      "dev": true,
+      "dependencies": {
+        "arr-flatten": "^1.0.1",
+        "arr-map": "^2.0.0",
+        "bach": "^1.0.0",
+        "collection-map": "^1.0.0",
+        "es6-weak-map": "^2.0.1",
+        "fast-levenshtein": "^1.0.0",
+        "last-run": "^1.1.0",
+        "object.defaults": "^1.0.0",
+        "object.reduce": "^1.0.0",
+        "undertaker-registry": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/undertaker-registry": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-1.0.1.tgz",
+      "integrity": "sha1-XkvaMI5KiirlhPm5pDWaSZglzFA=",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/undertaker/node_modules/fast-levenshtein": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-1.1.4.tgz",
+      "integrity": "sha1-5qdUzI8V5YmHqpy9J69m/W9OWvk=",
+      "dev": true
+    },
+    "node_modules/union-value": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
+      "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
+      "dev": true,
+      "dependencies": {
+        "arr-union": "^3.1.0",
+        "get-value": "^2.0.6",
+        "is-extendable": "^0.1.1",
+        "set-value": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/uniq": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz",
+      "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=",
+      "dev": true
+    },
+    "node_modules/unique-stream": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz",
+      "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==",
+      "dev": true,
+      "dependencies": {
+        "json-stable-stringify-without-jsonify": "^1.0.1",
+        "through2-filter": "^3.0.0"
+      }
+    },
+    "node_modules/unset-value": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
+      "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=",
+      "dev": true,
+      "dependencies": {
+        "has-value": "^0.3.1",
+        "isobject": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/unset-value/node_modules/has-value": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
+      "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=",
+      "dev": true,
+      "dependencies": {
+        "get-value": "^2.0.3",
+        "has-values": "^0.1.4",
+        "isobject": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/unset-value/node_modules/has-value/node_modules/isobject": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
+      "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
+      "dev": true,
+      "dependencies": {
+        "isarray": "1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/unset-value/node_modules/has-values": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
+      "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/upath": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",
+      "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==",
+      "dev": true,
+      "engines": {
+        "node": ">=4",
+        "yarn": "*"
+      }
+    },
+    "node_modules/uri-js": {
+      "version": "4.4.1",
+      "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+      "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+      "dev": true,
+      "dependencies": {
+        "punycode": "^2.1.0"
+      }
+    },
+    "node_modules/urix": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
+      "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=",
+      "deprecated": "Please see https://github.com/lydell/urix#deprecated",
+      "dev": true
+    },
+    "node_modules/use": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
+      "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+      "dev": true
+    },
+    "node_modules/uuid": {
+      "version": "3.4.0",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+      "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+      "dev": true,
+      "bin": {
+        "uuid": "bin/uuid"
+      }
+    },
+    "node_modules/v8flags": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz",
+      "integrity": "sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg==",
+      "dev": true,
+      "dependencies": {
+        "homedir-polyfill": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/validate-npm-package-license": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+      "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+      "dev": true,
+      "dependencies": {
+        "spdx-correct": "^3.0.0",
+        "spdx-expression-parse": "^3.0.0"
+      }
+    },
+    "node_modules/value-or-function": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-3.0.0.tgz",
+      "integrity": "sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM=",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/verror": {
+      "version": "1.10.0",
+      "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+      "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
+      "dev": true,
+      "engines": [
+        "node >=0.6.0"
+      ],
+      "dependencies": {
+        "assert-plus": "^1.0.0",
+        "core-util-is": "1.0.2",
+        "extsprintf": "^1.2.0"
+      }
+    },
+    "node_modules/vinyl": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz",
+      "integrity": "sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==",
+      "dev": true,
+      "dependencies": {
+        "clone": "^2.1.1",
+        "clone-buffer": "^1.0.0",
+        "clone-stats": "^1.0.0",
+        "cloneable-readable": "^1.0.0",
+        "remove-trailing-separator": "^1.0.1",
+        "replace-ext": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/vinyl-fs": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-3.0.3.tgz",
+      "integrity": "sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng==",
+      "dev": true,
+      "dependencies": {
+        "fs-mkdirp-stream": "^1.0.0",
+        "glob-stream": "^6.1.0",
+        "graceful-fs": "^4.0.0",
+        "is-valid-glob": "^1.0.0",
+        "lazystream": "^1.0.0",
+        "lead": "^1.0.0",
+        "object.assign": "^4.0.4",
+        "pumpify": "^1.3.5",
+        "readable-stream": "^2.3.3",
+        "remove-bom-buffer": "^3.0.0",
+        "remove-bom-stream": "^1.2.0",
+        "resolve-options": "^1.1.0",
+        "through2": "^2.0.0",
+        "to-through": "^2.0.0",
+        "value-or-function": "^3.0.0",
+        "vinyl": "^2.0.0",
+        "vinyl-sourcemap": "^1.1.0"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/vinyl-sourcemap": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz",
+      "integrity": "sha1-kqgAWTo4cDqM2xHYswCtS+Y7PhY=",
+      "dev": true,
+      "dependencies": {
+        "append-buffer": "^1.0.2",
+        "convert-source-map": "^1.5.0",
+        "graceful-fs": "^4.1.6",
+        "normalize-path": "^2.1.1",
+        "now-and-later": "^2.0.0",
+        "remove-bom-buffer": "^3.0.0",
+        "vinyl": "^2.0.0"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/vinyl-sourcemap/node_modules/normalize-path": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
+      "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
+      "dev": true,
+      "dependencies": {
+        "remove-trailing-separator": "^1.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/w3c-hr-time": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
+      "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==",
+      "dev": true,
+      "dependencies": {
+        "browser-process-hrtime": "^1.0.0"
+      }
+    },
+    "node_modules/w3c-xmlserializer": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz",
+      "integrity": "sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg==",
+      "dev": true,
+      "dependencies": {
+        "domexception": "^1.0.1",
+        "webidl-conversions": "^4.0.2",
+        "xml-name-validator": "^3.0.0"
+      }
+    },
+    "node_modules/webidl-conversions": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
+      "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==",
+      "dev": true
+    },
+    "node_modules/whatwg-encoding": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz",
+      "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==",
+      "dev": true,
+      "dependencies": {
+        "iconv-lite": "0.4.24"
+      }
+    },
+    "node_modules/whatwg-mimetype": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz",
+      "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==",
+      "dev": true
+    },
+    "node_modules/whatwg-url": {
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz",
+      "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==",
+      "dev": true,
+      "dependencies": {
+        "lodash.sortby": "^4.7.0",
+        "tr46": "^1.0.1",
+        "webidl-conversions": "^4.0.2"
+      }
+    },
+    "node_modules/which": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+      "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+      "dev": true,
+      "dependencies": {
+        "isexe": "^2.0.0"
+      },
+      "bin": {
+        "which": "bin/which"
+      }
+    },
+    "node_modules/which-module": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz",
+      "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=",
+      "dev": true
+    },
+    "node_modules/word-wrap": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
+      "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/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,
+      "dependencies": {
+        "string-width": "^1.0.1",
+        "strip-ansi": "^3.0.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+      "dev": true
+    },
+    "node_modules/ws": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz",
+      "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==",
+      "dev": true,
+      "dependencies": {
+        "async-limiter": "~1.0.0"
+      }
+    },
+    "node_modules/xml-name-validator": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz",
+      "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==",
+      "dev": true
+    },
+    "node_modules/xmlchars": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
+      "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
+      "dev": true
+    },
+    "node_modules/xtend": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+      "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.4"
+      }
+    },
+    "node_modules/y18n": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz",
+      "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==",
+      "dev": true
+    },
+    "node_modules/yargs": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.1.tgz",
+      "integrity": "sha512-huO4Fr1f9PmiJJdll5kwoS2e4GqzGSsMT3PPMpOwoVkOK8ckqAewMTZyA6LXVQWflleb/Z8oPBEvNsMft0XE+g==",
+      "dev": true,
+      "dependencies": {
+        "camelcase": "^3.0.0",
+        "cliui": "^3.2.0",
+        "decamelize": "^1.1.1",
+        "get-caller-file": "^1.0.1",
+        "os-locale": "^1.4.0",
+        "read-pkg-up": "^1.0.1",
+        "require-directory": "^2.1.1",
+        "require-main-filename": "^1.0.1",
+        "set-blocking": "^2.0.0",
+        "string-width": "^1.0.2",
+        "which-module": "^1.0.0",
+        "y18n": "^3.2.1",
+        "yargs-parser": "5.0.0-security.0"
+      }
+    },
+    "node_modules/yargs-parser": {
+      "version": "5.0.0-security.0",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0-security.0.tgz",
+      "integrity": "sha512-T69y4Ps64LNesYxeYGYPvfoMTt/7y1XtfpIslUeK4um+9Hu7hlGoRtaDLvdXb7+/tfq4opVa2HRY5xGip022rQ==",
+      "dev": true,
+      "dependencies": {
+        "camelcase": "^3.0.0",
+        "object.assign": "^4.1.0"
+      }
+    },
+    "node_modules/yaserver": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/yaserver/-/yaserver-0.3.0.tgz",
+      "integrity": "sha512-q7O/gz6B46jA0QZptLxPDBWwojnbpxXwjHGHwWKghsPQBNDcTwOmJ2wTbFAoU6me1M/Z995eATE5THYmGzGE6A==",
+      "dev": true,
+      "bin": {
+        "yaserver": "bin/yaserver"
+      }
+    }
+  },
+  "dependencies": {
+    "abab": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz",
+      "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==",
+      "dev": true
+    },
+    "acorn": {
+      "version": "6.4.2",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz",
+      "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==",
+      "dev": true
+    },
+    "acorn-globals": {
+      "version": "4.3.4",
+      "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.4.tgz",
+      "integrity": "sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==",
+      "dev": true,
+      "requires": {
+        "acorn": "^6.0.1",
+        "acorn-walk": "^6.0.1"
+      }
+    },
+    "acorn-walk": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz",
+      "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==",
+      "dev": true
+    },
+    "ajv": {
+      "version": "6.12.6",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+      "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+      "dev": true,
+      "requires": {
+        "fast-deep-equal": "^3.1.1",
+        "fast-json-stable-stringify": "^2.0.0",
+        "json-schema-traverse": "^0.4.1",
+        "uri-js": "^4.2.2"
+      }
+    },
+    "ansi-colors": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz",
+      "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==",
+      "dev": true,
+      "requires": {
+        "ansi-wrap": "^0.1.0"
+      }
+    },
+    "ansi-gray": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz",
+      "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE=",
+      "dev": true,
+      "requires": {
+        "ansi-wrap": "0.1.0"
+      }
+    },
+    "ansi-regex": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+      "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+      "dev": true
+    },
+    "ansi-wrap": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/ansi-wrap/-/ansi-wrap-0.1.0.tgz",
+      "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=",
+      "dev": true
+    },
+    "anymatch": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
+      "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
+      "dev": true,
+      "requires": {
+        "micromatch": "^3.1.4",
+        "normalize-path": "^2.1.1"
+      },
+      "dependencies": {
+        "normalize-path": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
+          "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
+          "dev": true,
+          "requires": {
+            "remove-trailing-separator": "^1.0.1"
+          }
+        }
+      }
+    },
+    "append-buffer": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/append-buffer/-/append-buffer-1.0.2.tgz",
+      "integrity": "sha1-2CIM9GYIFSXv6lBhTz3mUU36WPE=",
+      "dev": true,
+      "requires": {
+        "buffer-equal": "^1.0.0"
+      }
+    },
+    "archy": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz",
+      "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=",
+      "dev": true
+    },
+    "arr-diff": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
+      "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
+      "dev": true
+    },
+    "arr-filter": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/arr-filter/-/arr-filter-1.1.2.tgz",
+      "integrity": "sha1-Q/3d0JHo7xGqTEXZzcGOLf8XEe4=",
+      "dev": true,
+      "requires": {
+        "make-iterator": "^1.0.0"
+      }
+    },
+    "arr-flatten": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
+      "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
+      "dev": true
+    },
+    "arr-map": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/arr-map/-/arr-map-2.0.2.tgz",
+      "integrity": "sha1-Onc0X/wc814qkYJWAfnljy4kysQ=",
+      "dev": true,
+      "requires": {
+        "make-iterator": "^1.0.0"
+      }
+    },
+    "arr-union": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
+      "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=",
+      "dev": true
+    },
+    "array-each": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz",
+      "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8=",
+      "dev": true
+    },
+    "array-equal": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz",
+      "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=",
+      "dev": true
+    },
+    "array-initial": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz",
+      "integrity": "sha1-L6dLJnOTccOUe9enrcc74zSz15U=",
+      "dev": true,
+      "requires": {
+        "array-slice": "^1.0.0",
+        "is-number": "^4.0.0"
+      },
+      "dependencies": {
+        "is-number": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz",
+          "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==",
+          "dev": true
+        }
+      }
+    },
+    "array-last": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/array-last/-/array-last-1.3.0.tgz",
+      "integrity": "sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg==",
+      "dev": true,
+      "requires": {
+        "is-number": "^4.0.0"
+      },
+      "dependencies": {
+        "is-number": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz",
+          "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==",
+          "dev": true
+        }
+      }
+    },
+    "array-slice": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz",
+      "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==",
+      "dev": true
+    },
+    "array-sort": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-1.0.0.tgz",
+      "integrity": "sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg==",
+      "dev": true,
+      "requires": {
+        "default-compare": "^1.0.0",
+        "get-value": "^2.0.6",
+        "kind-of": "^5.0.2"
+      }
+    },
+    "array-unique": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
+      "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
+      "dev": true
+    },
+    "asn1": {
+      "version": "0.2.4",
+      "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
+      "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
+      "dev": true,
+      "requires": {
+        "safer-buffer": "~2.1.0"
+      }
+    },
+    "assert-plus": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+      "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
+      "dev": true
+    },
+    "assign-symbols": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
+      "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
+      "dev": true
+    },
+    "async-done": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz",
+      "integrity": "sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw==",
+      "dev": true,
+      "requires": {
+        "end-of-stream": "^1.1.0",
+        "once": "^1.3.2",
+        "process-nextick-args": "^2.0.0",
+        "stream-exhaust": "^1.0.1"
+      }
+    },
+    "async-each": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz",
+      "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==",
+      "dev": true
+    },
+    "async-limiter": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
+      "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==",
+      "dev": true
+    },
+    "async-settle": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz",
+      "integrity": "sha1-HQqRS7Aldb7IqPOnTlCA9yssDGs=",
+      "dev": true,
+      "requires": {
+        "async-done": "^1.2.2"
+      }
+    },
+    "asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
+      "dev": true
+    },
+    "atob": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
+      "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
+      "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.11.0",
+      "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz",
+      "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==",
+      "dev": true
+    },
+    "bach": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz",
+      "integrity": "sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA=",
+      "dev": true,
+      "requires": {
+        "arr-filter": "^1.1.1",
+        "arr-flatten": "^1.0.1",
+        "arr-map": "^2.0.0",
+        "array-each": "^1.0.0",
+        "array-initial": "^1.0.0",
+        "array-last": "^1.1.1",
+        "async-done": "^1.2.2",
+        "async-settle": "^1.0.0",
+        "now-and-later": "^2.0.0"
+      }
+    },
+    "balanced-match": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+      "dev": true
+    },
+    "base": {
+      "version": "0.11.2",
+      "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
+      "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
+      "dev": true,
+      "requires": {
+        "cache-base": "^1.0.1",
+        "class-utils": "^0.3.5",
+        "component-emitter": "^1.2.1",
+        "define-property": "^1.0.0",
+        "isobject": "^3.0.1",
+        "mixin-deep": "^1.2.0",
+        "pascalcase": "^0.1.1"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^1.0.0"
+          }
+        }
+      }
+    },
+    "bcrypt-pbkdf": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
+      "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
+      "dev": true,
+      "requires": {
+        "tweetnacl": "^0.14.3"
+      }
+    },
+    "binary-extensions": {
+      "version": "1.13.1",
+      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
+      "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
+      "dev": true
+    },
+    "bindings": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
+      "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
+      "dev": true,
+      "optional": true,
+      "requires": {
+        "file-uri-to-path": "1.0.0"
+      }
+    },
+    "brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dev": true,
+      "requires": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "braces": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+      "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+      "dev": true,
+      "requires": {
+        "arr-flatten": "^1.1.0",
+        "array-unique": "^0.3.2",
+        "extend-shallow": "^2.0.1",
+        "fill-range": "^4.0.0",
+        "isobject": "^3.0.1",
+        "repeat-element": "^1.1.2",
+        "snapdragon": "^0.8.1",
+        "snapdragon-node": "^2.0.1",
+        "split-string": "^3.0.2",
+        "to-regex": "^3.0.1"
+      }
+    },
+    "browser-process-hrtime": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz",
+      "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==",
+      "dev": true
+    },
+    "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-from": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
+      "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
+      "dev": true
+    },
+    "cache-base": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
+      "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
+      "dev": true,
+      "requires": {
+        "collection-visit": "^1.0.0",
+        "component-emitter": "^1.2.1",
+        "get-value": "^2.0.6",
+        "has-value": "^1.0.0",
+        "isobject": "^3.0.1",
+        "set-value": "^2.0.0",
+        "to-object-path": "^0.3.0",
+        "union-value": "^1.0.0",
+        "unset-value": "^1.0.0"
+      }
+    },
+    "call-bind": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
+      "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
+      "dev": true,
+      "requires": {
+        "function-bind": "^1.1.1",
+        "get-intrinsic": "^1.0.2"
+      }
+    },
+    "camelcase": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
+      "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=",
+      "dev": true
+    },
+    "caseless": {
+      "version": "0.12.0",
+      "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+      "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
+      "dev": true
+    },
+    "chokidar": {
+      "version": "2.1.8",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
+      "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
+      "dev": true,
+      "requires": {
+        "anymatch": "^2.0.0",
+        "async-each": "^1.0.1",
+        "braces": "^2.3.2",
+        "fsevents": "^1.2.7",
+        "glob-parent": "^3.1.0",
+        "inherits": "^2.0.3",
+        "is-binary-path": "^1.0.0",
+        "is-glob": "^4.0.0",
+        "normalize-path": "^3.0.0",
+        "path-is-absolute": "^1.0.0",
+        "readdirp": "^2.2.1",
+        "upath": "^1.1.1"
+      }
+    },
+    "class-utils": {
+      "version": "0.3.6",
+      "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
+      "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
+      "dev": true,
+      "requires": {
+        "arr-union": "^3.1.0",
+        "define-property": "^0.2.5",
+        "isobject": "^3.0.0",
+        "static-extend": "^0.1.1"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^0.1.0"
+          }
+        },
+        "is-accessor-descriptor": {
+          "version": "0.1.6",
+          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+          "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+          "dev": true,
+          "requires": {
+            "kind-of": "^3.0.2"
+          },
+          "dependencies": {
+            "kind-of": {
+              "version": "3.2.2",
+              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+              "dev": true,
+              "requires": {
+                "is-buffer": "^1.1.5"
+              }
+            }
+          }
+        },
+        "is-data-descriptor": {
+          "version": "0.1.4",
+          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+          "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+          "dev": true,
+          "requires": {
+            "kind-of": "^3.0.2"
+          },
+          "dependencies": {
+            "kind-of": {
+              "version": "3.2.2",
+              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+              "dev": true,
+              "requires": {
+                "is-buffer": "^1.1.5"
+              }
+            }
+          }
+        },
+        "is-descriptor": {
+          "version": "0.1.6",
+          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+          "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+          "dev": true,
+          "requires": {
+            "is-accessor-descriptor": "^0.1.6",
+            "is-data-descriptor": "^0.1.4",
+            "kind-of": "^5.0.0"
+          }
+        }
+      }
+    },
+    "clean-css": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.1.2.tgz",
+      "integrity": "sha512-QcaGg9OuMo+0Ds933yLOY+gHPWbxhxqF0HDexmToPf8pczvmvZGYzd+QqWp9/mkucAOKViI+dSFOqoZIvXbeBw==",
+      "dev": true,
+      "requires": {
+        "source-map": "~0.6.0"
+      }
+    },
+    "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.1",
+        "strip-ansi": "^3.0.1",
+        "wrap-ansi": "^2.0.0"
+      }
+    },
+    "clone": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
+      "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=",
+      "dev": true
+    },
+    "clone-buffer": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz",
+      "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg=",
+      "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
+    },
+    "cloneable-readable": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz",
+      "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==",
+      "dev": true,
+      "requires": {
+        "inherits": "^2.0.1",
+        "process-nextick-args": "^2.0.0",
+        "readable-stream": "^2.3.5"
+      }
+    },
+    "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
+    },
+    "collection-map": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/collection-map/-/collection-map-1.0.0.tgz",
+      "integrity": "sha1-rqDwb40mx4DCt1SUOFVEsiVa8Yw=",
+      "dev": true,
+      "requires": {
+        "arr-map": "^2.0.2",
+        "for-own": "^1.0.0",
+        "make-iterator": "^1.0.0"
+      }
+    },
+    "collection-visit": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
+      "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=",
+      "dev": true,
+      "requires": {
+        "map-visit": "^1.0.0",
+        "object-visit": "^1.0.0"
+      }
+    },
+    "color-support": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
+      "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
+      "dev": true
+    },
+    "combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "dev": true,
+      "requires": {
+        "delayed-stream": "~1.0.0"
+      }
+    },
+    "commander": {
+      "version": "2.20.3",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+      "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+      "dev": true
+    },
+    "component-emitter": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
+      "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
+      "dev": true
+    },
+    "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.2",
+      "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
+      "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+      "dev": true,
+      "requires": {
+        "buffer-from": "^1.0.0",
+        "inherits": "^2.0.3",
+        "readable-stream": "^2.2.2",
+        "typedarray": "^0.0.6"
+      }
+    },
+    "convert-source-map": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz",
+      "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==",
+      "dev": true,
+      "requires": {
+        "safe-buffer": "~5.1.1"
+      }
+    },
+    "copy-descriptor": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
+      "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
+      "dev": true
+    },
+    "copy-props": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-2.0.5.tgz",
+      "integrity": "sha512-XBlx8HSqrT0ObQwmSzM7WE5k8FxTV75h1DX1Z3n6NhQ/UYYAvInWYmG06vFt7hQZArE2fuO62aihiWIVQwh1sw==",
+      "dev": true,
+      "requires": {
+        "each-props": "^1.3.2",
+        "is-plain-object": "^5.0.0"
+      }
+    },
+    "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
+    },
+    "cssesc": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+      "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+      "dev": true
+    },
+    "cssom": {
+      "version": "0.3.8",
+      "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz",
+      "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==",
+      "dev": true
+    },
+    "cssstyle": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.4.0.tgz",
+      "integrity": "sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==",
+      "dev": true,
+      "requires": {
+        "cssom": "0.3.x"
+      }
+    },
+    "d": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
+      "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
+      "dev": true,
+      "requires": {
+        "es5-ext": "^0.10.50",
+        "type": "^1.0.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"
+      }
+    },
+    "data-urls": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz",
+      "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==",
+      "dev": true,
+      "requires": {
+        "abab": "^2.0.0",
+        "whatwg-mimetype": "^2.2.0",
+        "whatwg-url": "^7.0.0"
+      }
+    },
+    "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"
+      }
+    },
+    "decamelize": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+      "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
+      "dev": true
+    },
+    "decode-uri-component": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
+      "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
+      "dev": true
+    },
+    "deep-is": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
+      "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
+      "dev": true
+    },
+    "default-compare": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz",
+      "integrity": "sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==",
+      "dev": true,
+      "requires": {
+        "kind-of": "^5.0.2"
+      }
+    },
+    "default-resolution": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/default-resolution/-/default-resolution-2.0.0.tgz",
+      "integrity": "sha1-vLgrqnKtebQmp2cy8aga1t8m1oQ=",
+      "dev": true
+    },
+    "define-properties": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+      "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+      "dev": true,
+      "requires": {
+        "object-keys": "^1.0.12"
+      }
+    },
+    "define-property": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
+      "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
+      "dev": true,
+      "requires": {
+        "is-descriptor": "^1.0.2",
+        "isobject": "^3.0.1"
+      }
+    },
+    "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
+    },
+    "detect-file": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz",
+      "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=",
+      "dev": true
+    },
+    "domexception": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz",
+      "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==",
+      "dev": true,
+      "requires": {
+        "webidl-conversions": "^4.0.2"
+      }
+    },
+    "duplexer": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz",
+      "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==",
+      "dev": true
+    },
+    "duplexify": {
+      "version": "3.7.1",
+      "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
+      "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==",
+      "dev": true,
+      "requires": {
+        "end-of-stream": "^1.0.0",
+        "inherits": "^2.0.1",
+        "readable-stream": "^2.0.0",
+        "stream-shift": "^1.0.0"
+      }
+    },
+    "each-props": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.2.tgz",
+      "integrity": "sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA==",
+      "dev": true,
+      "requires": {
+        "is-plain-object": "^2.0.1",
+        "object.defaults": "^1.1.0"
+      },
+      "dependencies": {
+        "is-plain-object": {
+          "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"
+          }
+        }
+      }
+    },
+    "ecc-jsbn": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
+      "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
+      "dev": true,
+      "requires": {
+        "jsbn": "~0.1.0",
+        "safer-buffer": "^2.1.0"
+      }
+    },
+    "end-of-stream": {
+      "version": "1.4.4",
+      "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+      "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+      "dev": true,
+      "requires": {
+        "once": "^1.4.0"
+      }
+    },
+    "error-ex": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+      "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+      "dev": true,
+      "requires": {
+        "is-arrayish": "^0.2.1"
+      }
+    },
+    "es5-ext": {
+      "version": "0.10.53",
+      "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz",
+      "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==",
+      "dev": true,
+      "requires": {
+        "es6-iterator": "~2.0.3",
+        "es6-symbol": "~3.1.3",
+        "next-tick": "~1.0.0"
+      }
+    },
+    "es6-iterator": {
+      "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",
+        "es5-ext": "^0.10.35",
+        "es6-symbol": "^3.1.1"
+      }
+    },
+    "es6-symbol": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz",
+      "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
+      "dev": true,
+      "requires": {
+        "d": "^1.0.1",
+        "ext": "^1.1.2"
+      }
+    },
+    "es6-weak-map": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.3.tgz",
+      "integrity": "sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==",
+      "dev": true,
+      "requires": {
+        "d": "1",
+        "es5-ext": "^0.10.46",
+        "es6-iterator": "^2.0.3",
+        "es6-symbol": "^3.1.1"
+      }
+    },
+    "escodegen": {
+      "version": "1.14.3",
+      "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz",
+      "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==",
+      "dev": true,
+      "requires": {
+        "esprima": "^4.0.1",
+        "estraverse": "^4.2.0",
+        "esutils": "^2.0.2",
+        "optionator": "^0.8.1",
+        "source-map": "~0.6.1"
+      }
+    },
+    "esprima": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+      "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+      "dev": true
+    },
+    "estraverse": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+      "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+      "dev": true
+    },
+    "esutils": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+      "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+      "dev": true
+    },
+    "event-stream": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-4.0.1.tgz",
+      "integrity": "sha512-qACXdu/9VHPBzcyhdOWR5/IahhGMf0roTeZJfzz077GwylcDd90yOHLouhmv7GJ5XzPi6ekaQWd8AvPP2nOvpA==",
+      "dev": true,
+      "requires": {
+        "duplexer": "^0.1.1",
+        "from": "^0.1.7",
+        "map-stream": "0.0.7",
+        "pause-stream": "^0.0.11",
+        "split": "^1.0.1",
+        "stream-combiner": "^0.2.2",
+        "through": "^2.3.8"
+      }
+    },
+    "expand-brackets": {
+      "version": "2.1.4",
+      "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
+      "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
+      "dev": true,
+      "requires": {
+        "debug": "^2.3.3",
+        "define-property": "^0.2.5",
+        "extend-shallow": "^2.0.1",
+        "posix-character-classes": "^0.1.0",
+        "regex-not": "^1.0.0",
+        "snapdragon": "^0.8.1",
+        "to-regex": "^3.0.1"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^0.1.0"
+          }
+        },
+        "is-accessor-descriptor": {
+          "version": "0.1.6",
+          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+          "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+          "dev": true,
+          "requires": {
+            "kind-of": "^3.0.2"
+          },
+          "dependencies": {
+            "kind-of": {
+              "version": "3.2.2",
+              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+              "dev": true,
+              "requires": {
+                "is-buffer": "^1.1.5"
+              }
+            }
+          }
+        },
+        "is-data-descriptor": {
+          "version": "0.1.4",
+          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+          "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+          "dev": true,
+          "requires": {
+            "kind-of": "^3.0.2"
+          },
+          "dependencies": {
+            "kind-of": {
+              "version": "3.2.2",
+              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+              "dev": true,
+              "requires": {
+                "is-buffer": "^1.1.5"
+              }
+            }
+          }
+        },
+        "is-descriptor": {
+          "version": "0.1.6",
+          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+          "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+          "dev": true,
+          "requires": {
+            "is-accessor-descriptor": "^0.1.6",
+            "is-data-descriptor": "^0.1.4",
+            "kind-of": "^5.0.0"
+          }
+        }
+      }
+    },
+    "expand-tilde": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz",
+      "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=",
+      "dev": true,
+      "requires": {
+        "homedir-polyfill": "^1.0.1"
+      }
+    },
+    "ext": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz",
+      "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==",
+      "dev": true,
+      "requires": {
+        "type": "^2.0.0"
+      },
+      "dependencies": {
+        "type": {
+          "version": "2.5.0",
+          "resolved": "https://registry.npmjs.org/type/-/type-2.5.0.tgz",
+          "integrity": "sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==",
+          "dev": true
+        }
+      }
+    },
+    "extend": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+      "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+      "dev": true
+    },
+    "extend-shallow": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+      "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+      "dev": true,
+      "requires": {
+        "is-extendable": "^0.1.0"
+      }
+    },
+    "extglob": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
+      "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
+      "dev": true,
+      "requires": {
+        "array-unique": "^0.3.2",
+        "define-property": "^1.0.0",
+        "expand-brackets": "^2.1.4",
+        "extend-shallow": "^2.0.1",
+        "fragment-cache": "^0.2.1",
+        "regex-not": "^1.0.0",
+        "snapdragon": "^0.8.1",
+        "to-regex": "^3.0.1"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^1.0.0"
+          }
+        }
+      }
+    },
+    "extsprintf": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+      "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
+      "dev": true
+    },
+    "fancy-log": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz",
+      "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==",
+      "dev": true,
+      "requires": {
+        "ansi-gray": "^0.1.1",
+        "color-support": "^1.1.3",
+        "parse-node-version": "^1.0.0",
+        "time-stamp": "^1.0.0"
+      }
+    },
+    "fast-deep-equal": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+      "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+      "dev": true
+    },
+    "fast-json-stable-stringify": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+      "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+      "dev": true
+    },
+    "fast-levenshtein": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+      "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+      "dev": true
+    },
+    "file-uri-to-path": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
+      "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
+      "dev": true,
+      "optional": true
+    },
+    "fill-range": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+      "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+      "dev": true,
+      "requires": {
+        "extend-shallow": "^2.0.1",
+        "is-number": "^3.0.0",
+        "repeat-string": "^1.6.1",
+        "to-regex-range": "^2.1.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.0.0",
+        "pinkie-promise": "^2.0.0"
+      }
+    },
+    "findup-sync": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz",
+      "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==",
+      "dev": true,
+      "requires": {
+        "detect-file": "^1.0.0",
+        "is-glob": "^4.0.0",
+        "micromatch": "^3.0.4",
+        "resolve-dir": "^1.0.1"
+      }
+    },
+    "fined": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz",
+      "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==",
+      "dev": true,
+      "requires": {
+        "expand-tilde": "^2.0.2",
+        "is-plain-object": "^2.0.3",
+        "object.defaults": "^1.1.0",
+        "object.pick": "^1.2.0",
+        "parse-filepath": "^1.0.1"
+      },
+      "dependencies": {
+        "is-plain-object": {
+          "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"
+          }
+        }
+      }
+    },
+    "flagged-respawn": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz",
+      "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==",
+      "dev": true
+    },
+    "flush-write-stream": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz",
+      "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==",
+      "dev": true,
+      "requires": {
+        "inherits": "^2.0.3",
+        "readable-stream": "^2.3.6"
+      }
+    },
+    "for-in": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
+      "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
+      "dev": true
+    },
+    "for-own": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz",
+      "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=",
+      "dev": true,
+      "requires": {
+        "for-in": "^1.0.1"
+      }
+    },
+    "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.3",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
+      "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
+      "dev": true,
+      "requires": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.6",
+        "mime-types": "^2.1.12"
+      }
+    },
+    "fragment-cache": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
+      "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
+      "dev": true,
+      "requires": {
+        "map-cache": "^0.2.2"
+      }
+    },
+    "from": {
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/from/-/from-0.1.7.tgz",
+      "integrity": "sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=",
+      "dev": true
+    },
+    "fs-mkdirp-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz",
+      "integrity": "sha1-C3gV/DIBxqaeFNuYzgmMFpNSWes=",
+      "dev": true,
+      "requires": {
+        "graceful-fs": "^4.1.11",
+        "through2": "^2.0.3"
+      }
+    },
+    "fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+      "dev": true
+    },
+    "fsevents": {
+      "version": "1.2.13",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
+      "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
+      "dev": true,
+      "optional": true,
+      "requires": {
+        "bindings": "^1.5.0",
+        "nan": "^2.12.1"
+      }
+    },
+    "function-bind": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+      "dev": true
+    },
+    "get-caller-file": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
+      "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==",
+      "dev": true
+    },
+    "get-intrinsic": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
+      "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
+      "dev": true,
+      "requires": {
+        "function-bind": "^1.1.1",
+        "has": "^1.0.3",
+        "has-symbols": "^1.0.1"
+      }
+    },
+    "get-value": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
+      "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=",
+      "dev": true
+    },
+    "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.6",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+      "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+      "dev": true,
+      "requires": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.0.4",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      }
+    },
+    "glob-parent": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
+      "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
+      "dev": true,
+      "requires": {
+        "is-glob": "^3.1.0",
+        "path-dirname": "^1.0.0"
+      },
+      "dependencies": {
+        "is-glob": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+          "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
+          "dev": true,
+          "requires": {
+            "is-extglob": "^2.1.0"
+          }
+        }
+      }
+    },
+    "glob-stream": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-6.1.0.tgz",
+      "integrity": "sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ=",
+      "dev": true,
+      "requires": {
+        "extend": "^3.0.0",
+        "glob": "^7.1.1",
+        "glob-parent": "^3.1.0",
+        "is-negated-glob": "^1.0.0",
+        "ordered-read-streams": "^1.0.0",
+        "pumpify": "^1.3.5",
+        "readable-stream": "^2.1.5",
+        "remove-trailing-separator": "^1.0.1",
+        "to-absolute-glob": "^2.0.0",
+        "unique-stream": "^2.0.2"
+      }
+    },
+    "glob-watcher": {
+      "version": "5.0.5",
+      "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.5.tgz",
+      "integrity": "sha512-zOZgGGEHPklZNjZQaZ9f41i7F2YwE+tS5ZHrDhbBCk3stwahn5vQxnFmBJZHoYdusR6R1bLSXeGUy/BhctwKzw==",
+      "dev": true,
+      "requires": {
+        "anymatch": "^2.0.0",
+        "async-done": "^1.2.0",
+        "chokidar": "^2.0.0",
+        "is-negated-glob": "^1.0.0",
+        "just-debounce": "^1.0.0",
+        "normalize-path": "^3.0.0",
+        "object.defaults": "^1.1.0"
+      }
+    },
+    "global-modules": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz",
+      "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==",
+      "dev": true,
+      "requires": {
+        "global-prefix": "^1.0.1",
+        "is-windows": "^1.0.1",
+        "resolve-dir": "^1.0.0"
+      }
+    },
+    "global-prefix": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz",
+      "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=",
+      "dev": true,
+      "requires": {
+        "expand-tilde": "^2.0.2",
+        "homedir-polyfill": "^1.0.1",
+        "ini": "^1.3.4",
+        "is-windows": "^1.0.1",
+        "which": "^1.2.14"
+      }
+    },
+    "glogg": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz",
+      "integrity": "sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA==",
+      "dev": true,
+      "requires": {
+        "sparkles": "^1.0.0"
+      }
+    },
+    "graceful-fs": {
+      "version": "4.2.6",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz",
+      "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==",
+      "dev": true
+    },
+    "gulp": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.2.tgz",
+      "integrity": "sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA==",
+      "dev": true,
+      "requires": {
+        "glob-watcher": "^5.0.3",
+        "gulp-cli": "^2.2.0",
+        "undertaker": "^1.2.1",
+        "vinyl-fs": "^3.0.0"
+      }
+    },
+    "gulp-cli": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.3.0.tgz",
+      "integrity": "sha512-zzGBl5fHo0EKSXsHzjspp3y5CONegCm8ErO5Qh0UzFzk2y4tMvzLWhoDokADbarfZRL2pGpRp7yt6gfJX4ph7A==",
+      "dev": true,
+      "requires": {
+        "ansi-colors": "^1.0.1",
+        "archy": "^1.0.0",
+        "array-sort": "^1.0.0",
+        "color-support": "^1.1.3",
+        "concat-stream": "^1.6.0",
+        "copy-props": "^2.0.1",
+        "fancy-log": "^1.3.2",
+        "gulplog": "^1.0.0",
+        "interpret": "^1.4.0",
+        "isobject": "^3.0.1",
+        "liftoff": "^3.1.0",
+        "matchdep": "^2.0.0",
+        "mute-stdout": "^1.0.0",
+        "pretty-hrtime": "^1.0.0",
+        "replace-homedir": "^1.0.0",
+        "semver-greatest-satisfied-range": "^1.1.0",
+        "v8flags": "^3.2.0",
+        "yargs": "^7.1.0"
+      }
+    },
+    "gulplog": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz",
+      "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=",
+      "dev": true,
+      "requires": {
+        "glogg": "^1.0.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.1.5",
+      "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
+      "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
+      "dev": true,
+      "requires": {
+        "ajv": "^6.12.3",
+        "har-schema": "^2.0.0"
+      }
+    },
+    "has": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+      "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+      "dev": true,
+      "requires": {
+        "function-bind": "^1.1.1"
+      }
+    },
+    "has-symbols": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
+      "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==",
+      "dev": true
+    },
+    "has-value": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
+      "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=",
+      "dev": true,
+      "requires": {
+        "get-value": "^2.0.6",
+        "has-values": "^1.0.0",
+        "isobject": "^3.0.0"
+      }
+    },
+    "has-values": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
+      "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=",
+      "dev": true,
+      "requires": {
+        "is-number": "^3.0.0",
+        "kind-of": "^4.0.0"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
+          "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
+    },
+    "homedir-polyfill": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz",
+      "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==",
+      "dev": true,
+      "requires": {
+        "parse-passwd": "^1.0.0"
+      }
+    },
+    "hosted-git-info": {
+      "version": "2.8.9",
+      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
+      "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
+      "dev": true
+    },
+    "html-encoding-sniffer": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz",
+      "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==",
+      "dev": true,
+      "requires": {
+        "whatwg-encoding": "^1.0.1"
+      }
+    },
+    "html-tags": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-1.2.0.tgz",
+      "integrity": "sha1-x43mW1Zjqll5id0rerSSANfk25g=",
+      "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.2.2",
+        "sshpk": "^1.7.0"
+      }
+    },
+    "iconv-lite": {
+      "version": "0.4.24",
+      "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+      "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+      "dev": true,
+      "requires": {
+        "safer-buffer": ">= 2.1.2 < 3"
+      }
+    },
+    "indexes-of": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz",
+      "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=",
+      "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.3.0",
+        "wrappy": "1"
+      }
+    },
+    "inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+      "dev": true
+    },
+    "ini": {
+      "version": "1.3.8",
+      "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+      "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+      "dev": true
+    },
+    "interpret": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz",
+      "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==",
+      "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-absolute": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz",
+      "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==",
+      "dev": true,
+      "requires": {
+        "is-relative": "^1.0.0",
+        "is-windows": "^1.0.1"
+      }
+    },
+    "is-absolute-url": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz",
+      "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==",
+      "dev": true
+    },
+    "is-accessor-descriptor": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+      "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+      "dev": true,
+      "requires": {
+        "kind-of": "^6.0.0"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "6.0.3",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+          "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+          "dev": true
+        }
+      }
+    },
+    "is-arrayish": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+      "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
+      "dev": true
+    },
+    "is-binary-path": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
+      "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
+      "dev": true,
+      "requires": {
+        "binary-extensions": "^1.0.0"
+      }
+    },
+    "is-buffer": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+      "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
+      "dev": true
+    },
+    "is-core-module": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz",
+      "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==",
+      "dev": true,
+      "requires": {
+        "has": "^1.0.3"
+      }
+    },
+    "is-data-descriptor": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+      "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+      "dev": true,
+      "requires": {
+        "kind-of": "^6.0.0"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "6.0.3",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+          "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+          "dev": true
+        }
+      }
+    },
+    "is-descriptor": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+      "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+      "dev": true,
+      "requires": {
+        "is-accessor-descriptor": "^1.0.0",
+        "is-data-descriptor": "^1.0.0",
+        "kind-of": "^6.0.2"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "6.0.3",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+          "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+          "dev": true
+        }
+      }
+    },
+    "is-extendable": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+      "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
+      "dev": true
+    },
+    "is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+      "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.0"
+      }
+    },
+    "is-glob": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+      "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+      "dev": true,
+      "requires": {
+        "is-extglob": "^2.1.1"
+      }
+    },
+    "is-html": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/is-html/-/is-html-1.1.0.tgz",
+      "integrity": "sha1-4E8cGNOUhRETlvmgJz6rUa8hhGQ=",
+      "dev": true,
+      "requires": {
+        "html-tags": "^1.0.0"
+      }
+    },
+    "is-negated-glob": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz",
+      "integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI=",
+      "dev": true
+    },
+    "is-number": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+      "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+      "dev": true,
+      "requires": {
+        "kind-of": "^3.0.2"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
+    },
+    "is-plain-object": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
+      "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
+      "dev": true
+    },
+    "is-relative": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz",
+      "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==",
+      "dev": true,
+      "requires": {
+        "is-unc-path": "^1.0.0"
+      }
+    },
+    "is-typedarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+      "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
+      "dev": true
+    },
+    "is-unc-path": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz",
+      "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==",
+      "dev": true,
+      "requires": {
+        "unc-path-regex": "^0.1.2"
+      }
+    },
+    "is-utf8": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
+      "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=",
+      "dev": true
+    },
+    "is-valid-glob": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-valid-glob/-/is-valid-glob-1.0.0.tgz",
+      "integrity": "sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao=",
+      "dev": true
+    },
+    "is-windows": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
+      "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
+      "dev": true
+    },
+    "isarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+      "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+      "dev": true
+    },
+    "isexe": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+      "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+      "dev": true
+    },
+    "isobject": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+      "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+      "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
+    },
+    "jsdom": {
+      "version": "14.1.0",
+      "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-14.1.0.tgz",
+      "integrity": "sha512-O901mfJSuTdwU2w3Sn+74T+RnDVP+FuV5fH8tcPWyqrseRAb0s5xOtPgCFiPOtLcyK7CLIJwPyD83ZqQWvA5ng==",
+      "dev": true,
+      "requires": {
+        "abab": "^2.0.0",
+        "acorn": "^6.0.4",
+        "acorn-globals": "^4.3.0",
+        "array-equal": "^1.0.0",
+        "cssom": "^0.3.4",
+        "cssstyle": "^1.1.1",
+        "data-urls": "^1.1.0",
+        "domexception": "^1.0.1",
+        "escodegen": "^1.11.0",
+        "html-encoding-sniffer": "^1.0.2",
+        "nwsapi": "^2.1.3",
+        "parse5": "5.1.0",
+        "pn": "^1.1.0",
+        "request": "^2.88.0",
+        "request-promise-native": "^1.0.5",
+        "saxes": "^3.1.9",
+        "symbol-tree": "^3.2.2",
+        "tough-cookie": "^2.5.0",
+        "w3c-hr-time": "^1.0.1",
+        "w3c-xmlserializer": "^1.1.2",
+        "webidl-conversions": "^4.0.2",
+        "whatwg-encoding": "^1.0.5",
+        "whatwg-mimetype": "^2.3.0",
+        "whatwg-url": "^7.0.0",
+        "ws": "^6.1.2",
+        "xml-name-validator": "^3.0.0"
+      }
+    },
+    "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.4.1",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+      "dev": true
+    },
+    "json-stable-stringify-without-jsonify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+      "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
+      "dev": true
+    },
+    "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
+    },
+    "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"
+      }
+    },
+    "just-debounce": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.1.0.tgz",
+      "integrity": "sha512-qpcRocdkUmf+UTNBYx5w6dexX5J31AKK1OmPwH630a83DdVVUIngk55RSAiIGpQyoH0dlr872VHfPjnQnK1qDQ==",
+      "dev": true
+    },
+    "kind-of": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+      "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+      "dev": true
+    },
+    "last-run": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/last-run/-/last-run-1.1.1.tgz",
+      "integrity": "sha1-RblpQsF7HHnHchmCWbqUO+v4yls=",
+      "dev": true,
+      "requires": {
+        "default-resolution": "^2.0.0",
+        "es6-weak-map": "^2.0.1"
+      }
+    },
+    "lazystream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz",
+      "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=",
+      "dev": true,
+      "requires": {
+        "readable-stream": "^2.0.5"
+      }
+    },
+    "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"
+      }
+    },
+    "lead": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/lead/-/lead-1.0.0.tgz",
+      "integrity": "sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI=",
+      "dev": true,
+      "requires": {
+        "flush-write-stream": "^1.0.2"
+      }
+    },
+    "levn": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+      "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
+      "dev": true,
+      "requires": {
+        "prelude-ls": "~1.1.2",
+        "type-check": "~0.3.2"
+      }
+    },
+    "liftoff": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz",
+      "integrity": "sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog==",
+      "dev": true,
+      "requires": {
+        "extend": "^3.0.0",
+        "findup-sync": "^3.0.0",
+        "fined": "^1.0.1",
+        "flagged-respawn": "^1.0.0",
+        "is-plain-object": "^2.0.4",
+        "object.map": "^1.0.0",
+        "rechoir": "^0.6.2",
+        "resolve": "^1.1.7"
+      },
+      "dependencies": {
+        "is-plain-object": {
+          "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"
+          }
+        }
+      }
+    },
+    "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.2",
+        "parse-json": "^2.2.0",
+        "pify": "^2.0.0",
+        "pinkie-promise": "^2.0.0",
+        "strip-bom": "^2.0.0"
+      }
+    },
+    "lodash": {
+      "version": "4.17.21",
+      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+      "dev": true
+    },
+    "lodash.sortby": {
+      "version": "4.7.0",
+      "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
+      "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=",
+      "dev": true
+    },
+    "make-iterator": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz",
+      "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==",
+      "dev": true,
+      "requires": {
+        "kind-of": "^6.0.2"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "6.0.3",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+          "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+          "dev": true
+        }
+      }
+    },
+    "map-cache": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
+      "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=",
+      "dev": true
+    },
+    "map-stream": {
+      "version": "0.0.7",
+      "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz",
+      "integrity": "sha1-ih8HiW2CsQkmvTdEokIACfiJdKg=",
+      "dev": true
+    },
+    "map-visit": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
+      "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=",
+      "dev": true,
+      "requires": {
+        "object-visit": "^1.0.0"
+      }
+    },
+    "matchdep": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz",
+      "integrity": "sha1-xvNINKDY28OzfCfui7yyfHd1WC4=",
+      "dev": true,
+      "requires": {
+        "findup-sync": "^2.0.0",
+        "micromatch": "^3.0.4",
+        "resolve": "^1.4.0",
+        "stack-trace": "0.0.10"
+      },
+      "dependencies": {
+        "findup-sync": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz",
+          "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=",
+          "dev": true,
+          "requires": {
+            "detect-file": "^1.0.0",
+            "is-glob": "^3.1.0",
+            "micromatch": "^3.0.4",
+            "resolve-dir": "^1.0.1"
+          }
+        },
+        "is-glob": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+          "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
+          "dev": true,
+          "requires": {
+            "is-extglob": "^2.1.0"
+          }
+        }
+      }
+    },
+    "micromatch": {
+      "version": "3.1.10",
+      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+      "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+      "dev": true,
+      "requires": {
+        "arr-diff": "^4.0.0",
+        "array-unique": "^0.3.2",
+        "braces": "^2.3.1",
+        "define-property": "^2.0.2",
+        "extend-shallow": "^3.0.2",
+        "extglob": "^2.0.4",
+        "fragment-cache": "^0.2.1",
+        "kind-of": "^6.0.2",
+        "nanomatch": "^1.2.9",
+        "object.pick": "^1.3.0",
+        "regex-not": "^1.0.0",
+        "snapdragon": "^0.8.1",
+        "to-regex": "^3.0.2"
+      },
+      "dependencies": {
+        "extend-shallow": {
+          "version": "3.0.2",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+          "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+          "dev": true,
+          "requires": {
+            "assign-symbols": "^1.0.0",
+            "is-extendable": "^1.0.1"
+          }
+        },
+        "is-extendable": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+          "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+          "dev": true,
+          "requires": {
+            "is-plain-object": "^2.0.4"
+          }
+        },
+        "is-plain-object": {
+          "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"
+          }
+        },
+        "kind-of": {
+          "version": "6.0.3",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+          "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+          "dev": true
+        }
+      }
+    },
+    "mime-db": {
+      "version": "1.46.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.46.0.tgz",
+      "integrity": "sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ==",
+      "dev": true
+    },
+    "mime-types": {
+      "version": "2.1.29",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.29.tgz",
+      "integrity": "sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ==",
+      "dev": true,
+      "requires": {
+        "mime-db": "1.46.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.7"
+      }
+    },
+    "mixin-deep": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
+      "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
+      "dev": true,
+      "requires": {
+        "for-in": "^1.0.2",
+        "is-extendable": "^1.0.1"
+      },
+      "dependencies": {
+        "is-extendable": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+          "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+          "dev": true,
+          "requires": {
+            "is-plain-object": "^2.0.4"
+          }
+        },
+        "is-plain-object": {
+          "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"
+          }
+        }
+      }
+    },
+    "monaco-editor": {
+      "version": "0.23.0",
+      "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.23.0.tgz",
+      "integrity": "sha512-q+CP5zMR/aFiMTE9QlIavGyGicKnG2v/H8qVvybLzeFsARM8f6G9fL0sMST2tyVYCwDKkGamZUI6647A0jR/Lg==",
+      "dev": true
+    },
+    "ms": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+      "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+      "dev": true
+    },
+    "mute-stdout": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz",
+      "integrity": "sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg==",
+      "dev": true
+    },
+    "nan": {
+      "version": "2.14.2",
+      "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz",
+      "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==",
+      "dev": true,
+      "optional": true
+    },
+    "nanomatch": {
+      "version": "1.2.13",
+      "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
+      "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
+      "dev": true,
+      "requires": {
+        "arr-diff": "^4.0.0",
+        "array-unique": "^0.3.2",
+        "define-property": "^2.0.2",
+        "extend-shallow": "^3.0.2",
+        "fragment-cache": "^0.2.1",
+        "is-windows": "^1.0.2",
+        "kind-of": "^6.0.2",
+        "object.pick": "^1.3.0",
+        "regex-not": "^1.0.0",
+        "snapdragon": "^0.8.1",
+        "to-regex": "^3.0.1"
+      },
+      "dependencies": {
+        "extend-shallow": {
+          "version": "3.0.2",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+          "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+          "dev": true,
+          "requires": {
+            "assign-symbols": "^1.0.0",
+            "is-extendable": "^1.0.1"
+          }
+        },
+        "is-extendable": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+          "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+          "dev": true,
+          "requires": {
+            "is-plain-object": "^2.0.4"
+          }
+        },
+        "is-plain-object": {
+          "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"
+          }
+        },
+        "kind-of": {
+          "version": "6.0.3",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+          "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+          "dev": true
+        }
+      }
+    },
+    "next-tick": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
+      "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=",
+      "dev": true
+    },
+    "normalize-package-data": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+      "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
+      "dev": true,
+      "requires": {
+        "hosted-git-info": "^2.1.4",
+        "resolve": "^1.10.0",
+        "semver": "2 || 3 || 4 || 5",
+        "validate-npm-package-license": "^3.0.1"
+      }
+    },
+    "normalize-path": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+      "dev": true
+    },
+    "now-and-later": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-2.0.1.tgz",
+      "integrity": "sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ==",
+      "dev": true,
+      "requires": {
+        "once": "^1.3.2"
+      }
+    },
+    "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
+    },
+    "nwsapi": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz",
+      "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==",
+      "dev": true
+    },
+    "oauth-sign": {
+      "version": "0.9.0",
+      "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
+      "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
+      "dev": true
+    },
+    "object-copy": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
+      "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=",
+      "dev": true,
+      "requires": {
+        "copy-descriptor": "^0.1.0",
+        "define-property": "^0.2.5",
+        "kind-of": "^3.0.3"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^0.1.0"
+          }
+        },
+        "is-accessor-descriptor": {
+          "version": "0.1.6",
+          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+          "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+          "dev": true,
+          "requires": {
+            "kind-of": "^3.0.2"
+          }
+        },
+        "is-data-descriptor": {
+          "version": "0.1.4",
+          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+          "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+          "dev": true,
+          "requires": {
+            "kind-of": "^3.0.2"
+          }
+        },
+        "is-descriptor": {
+          "version": "0.1.6",
+          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+          "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+          "dev": true,
+          "requires": {
+            "is-accessor-descriptor": "^0.1.6",
+            "is-data-descriptor": "^0.1.4",
+            "kind-of": "^5.0.0"
+          },
+          "dependencies": {
+            "kind-of": {
+              "version": "5.1.0",
+              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+              "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+              "dev": true
+            }
+          }
+        },
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
+    },
+    "object-keys": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+      "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+      "dev": true
+    },
+    "object-visit": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
+      "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=",
+      "dev": true,
+      "requires": {
+        "isobject": "^3.0.0"
+      }
+    },
+    "object.assign": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
+      "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
+      "dev": true,
+      "requires": {
+        "call-bind": "^1.0.0",
+        "define-properties": "^1.1.3",
+        "has-symbols": "^1.0.1",
+        "object-keys": "^1.1.1"
+      }
+    },
+    "object.defaults": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz",
+      "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=",
+      "dev": true,
+      "requires": {
+        "array-each": "^1.0.1",
+        "array-slice": "^1.0.0",
+        "for-own": "^1.0.0",
+        "isobject": "^3.0.0"
+      }
+    },
+    "object.map": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz",
+      "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=",
+      "dev": true,
+      "requires": {
+        "for-own": "^1.0.0",
+        "make-iterator": "^1.0.0"
+      }
+    },
+    "object.pick": {
+      "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": "^3.0.1"
+      }
+    },
+    "object.reduce": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/object.reduce/-/object.reduce-1.0.1.tgz",
+      "integrity": "sha1-b+NI8qx/oPlcpiEiZZkJaCW7A60=",
+      "dev": true,
+      "requires": {
+        "for-own": "^1.0.0",
+        "make-iterator": "^1.0.0"
+      }
+    },
+    "once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+      "dev": true,
+      "requires": {
+        "wrappy": "1"
+      }
+    },
+    "optionator": {
+      "version": "0.8.3",
+      "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
+      "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
+      "dev": true,
+      "requires": {
+        "deep-is": "~0.1.3",
+        "fast-levenshtein": "~2.0.6",
+        "levn": "~0.3.0",
+        "prelude-ls": "~1.1.2",
+        "type-check": "~0.3.2",
+        "word-wrap": "~1.2.3"
+      }
+    },
+    "ordered-read-streams": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz",
+      "integrity": "sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4=",
+      "dev": true,
+      "requires": {
+        "readable-stream": "^2.0.1"
+      }
+    },
+    "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"
+      }
+    },
+    "parse-filepath": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz",
+      "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=",
+      "dev": true,
+      "requires": {
+        "is-absolute": "^1.0.0",
+        "map-cache": "^0.2.0",
+        "path-root": "^0.1.1"
+      }
+    },
+    "parse-json": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
+      "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
+      "dev": true,
+      "requires": {
+        "error-ex": "^1.2.0"
+      }
+    },
+    "parse-node-version": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz",
+      "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==",
+      "dev": true
+    },
+    "parse-passwd": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz",
+      "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=",
+      "dev": true
+    },
+    "parse5": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz",
+      "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==",
+      "dev": true
+    },
+    "pascalcase": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
+      "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=",
+      "dev": true
+    },
+    "path-dirname": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
+      "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=",
+      "dev": true
+    },
+    "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.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
+    },
+    "path-parse": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+      "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+      "dev": true
+    },
+    "path-root": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz",
+      "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=",
+      "dev": true,
+      "requires": {
+        "path-root-regex": "^0.1.0"
+      }
+    },
+    "path-root-regex": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz",
+      "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0=",
+      "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=",
+      "dev": true,
+      "requires": {
+        "graceful-fs": "^4.1.2",
+        "pify": "^2.0.0",
+        "pinkie-promise": "^2.0.0"
+      }
+    },
+    "pause-stream": {
+      "version": "0.0.11",
+      "resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz",
+      "integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=",
+      "dev": true,
+      "requires": {
+        "through": "~2.3"
+      }
+    },
+    "performance-now": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+      "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
+      "dev": true
+    },
+    "picocolors": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
+      "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
+      "dev": true
+    },
+    "pify": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+      "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+      "dev": true
+    },
+    "pinkie": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+      "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
+      "dev": true
+    },
+    "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.0"
+      }
+    },
+    "pn": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz",
+      "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==",
+      "dev": true
+    },
+    "posix-character-classes": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
+      "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=",
+      "dev": true
+    },
+    "postcss": {
+      "version": "7.0.39",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
+      "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
+      "dev": true,
+      "requires": {
+        "picocolors": "^0.2.1",
+        "source-map": "^0.6.1"
+      }
+    },
+    "postcss-selector-parser": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz",
+      "integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==",
+      "dev": true,
+      "requires": {
+        "cssesc": "^3.0.0",
+        "indexes-of": "^1.0.1",
+        "uniq": "^1.0.1"
+      }
+    },
+    "prelude-ls": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+      "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
+      "dev": true
+    },
+    "pretty-hrtime": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz",
+      "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=",
+      "dev": true
+    },
+    "process-nextick-args": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+      "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+      "dev": true
+    },
+    "psl": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
+      "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==",
+      "dev": true
+    },
+    "pump": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
+      "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
+      "dev": true,
+      "requires": {
+        "end-of-stream": "^1.1.0",
+        "once": "^1.3.1"
+      }
+    },
+    "pumpify": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz",
+      "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==",
+      "dev": true,
+      "requires": {
+        "duplexify": "^3.6.0",
+        "inherits": "^2.0.3",
+        "pump": "^2.0.0"
+      }
+    },
+    "punycode": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+      "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+      "dev": true
+    },
+    "qs": {
+      "version": "6.5.2",
+      "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
+      "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
+      "dev": true
+    },
+    "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.0.0",
+        "normalize-package-data": "^2.3.2",
+        "path-type": "^1.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=",
+      "dev": true,
+      "requires": {
+        "find-up": "^1.0.0",
+        "read-pkg": "^1.0.0"
+      }
+    },
+    "readable-stream": {
+      "version": "2.3.7",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+      "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+      "dev": true,
+      "requires": {
+        "core-util-is": "~1.0.0",
+        "inherits": "~2.0.3",
+        "isarray": "~1.0.0",
+        "process-nextick-args": "~2.0.0",
+        "safe-buffer": "~5.1.1",
+        "string_decoder": "~1.1.1",
+        "util-deprecate": "~1.0.1"
+      }
+    },
+    "readdirp": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
+      "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
+      "dev": true,
+      "requires": {
+        "graceful-fs": "^4.1.11",
+        "micromatch": "^3.1.10",
+        "readable-stream": "^2.0.2"
+      }
+    },
+    "rechoir": {
+      "version": "0.6.2",
+      "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
+      "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=",
+      "dev": true,
+      "requires": {
+        "resolve": "^1.1.6"
+      }
+    },
+    "regex-not": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
+      "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
+      "dev": true,
+      "requires": {
+        "extend-shallow": "^3.0.2",
+        "safe-regex": "^1.1.0"
+      },
+      "dependencies": {
+        "extend-shallow": {
+          "version": "3.0.2",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+          "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+          "dev": true,
+          "requires": {
+            "assign-symbols": "^1.0.0",
+            "is-extendable": "^1.0.1"
+          }
+        },
+        "is-extendable": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+          "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+          "dev": true,
+          "requires": {
+            "is-plain-object": "^2.0.4"
+          }
+        },
+        "is-plain-object": {
+          "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"
+          }
+        }
+      }
+    },
+    "remove-bom-buffer": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz",
+      "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==",
+      "dev": true,
+      "requires": {
+        "is-buffer": "^1.1.5",
+        "is-utf8": "^0.2.1"
+      }
+    },
+    "remove-bom-stream": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz",
+      "integrity": "sha1-BfGlk/FuQuH7kOv1nejlaVJflSM=",
+      "dev": true,
+      "requires": {
+        "remove-bom-buffer": "^3.0.0",
+        "safe-buffer": "^5.1.0",
+        "through2": "^2.0.3"
+      }
+    },
+    "remove-trailing-separator": {
+      "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": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz",
+      "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==",
+      "dev": true
+    },
+    "repeat-string": {
+      "version": "1.6.1",
+      "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
+      "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
+      "dev": true
+    },
+    "replace-ext": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz",
+      "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==",
+      "dev": true
+    },
+    "replace-homedir": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/replace-homedir/-/replace-homedir-1.0.0.tgz",
+      "integrity": "sha1-6H9tUTuSjd6AgmDBK+f+xv9ueYw=",
+      "dev": true,
+      "requires": {
+        "homedir-polyfill": "^1.0.1",
+        "is-absolute": "^1.0.0",
+        "remove-trailing-separator": "^1.1.0"
+      }
+    },
+    "request": {
+      "version": "2.88.2",
+      "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
+      "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
+      "dev": true,
+      "requires": {
+        "aws-sign2": "~0.7.0",
+        "aws4": "^1.8.0",
+        "caseless": "~0.12.0",
+        "combined-stream": "~1.0.6",
+        "extend": "~3.0.2",
+        "forever-agent": "~0.6.1",
+        "form-data": "~2.3.2",
+        "har-validator": "~5.1.3",
+        "http-signature": "~1.2.0",
+        "is-typedarray": "~1.0.0",
+        "isstream": "~0.1.2",
+        "json-stringify-safe": "~5.0.1",
+        "mime-types": "~2.1.19",
+        "oauth-sign": "~0.9.0",
+        "performance-now": "^2.1.0",
+        "qs": "~6.5.2",
+        "safe-buffer": "^5.1.2",
+        "tough-cookie": "~2.5.0",
+        "tunnel-agent": "^0.6.0",
+        "uuid": "^3.3.2"
+      }
+    },
+    "request-promise-core": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.4.tgz",
+      "integrity": "sha512-TTbAfBBRdWD7aNNOoVOBH4pN/KigV6LyapYNNlAPA8JwbovRti1E88m3sYAwsLi5ryhPKsE9APwnjFTgdUjTpw==",
+      "dev": true,
+      "requires": {
+        "lodash": "^4.17.19"
+      }
+    },
+    "request-promise-native": {
+      "version": "1.0.9",
+      "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.9.tgz",
+      "integrity": "sha512-wcW+sIUiWnKgNY0dqCpOZkUbF/I+YPi+f09JZIDa39Ec+q82CpSYniDp+ISgTTbKmnpJWASeJBPZmoxH84wt3g==",
+      "dev": true,
+      "requires": {
+        "request-promise-core": "1.1.4",
+        "stealthy-require": "^1.1.1",
+        "tough-cookie": "^2.3.3"
+      }
+    },
+    "require-directory": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+      "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+      "dev": true
+    },
+    "require-main-filename": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
+      "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=",
+      "dev": true
+    },
+    "resolve": {
+      "version": "1.20.0",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
+      "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
+      "dev": true,
+      "requires": {
+        "is-core-module": "^2.2.0",
+        "path-parse": "^1.0.6"
+      }
+    },
+    "resolve-dir": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz",
+      "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=",
+      "dev": true,
+      "requires": {
+        "expand-tilde": "^2.0.0",
+        "global-modules": "^1.0.0"
+      }
+    },
+    "resolve-options": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-1.1.0.tgz",
+      "integrity": "sha1-MrueOcBtZzONyTeMDW1gdFZq0TE=",
+      "dev": true,
+      "requires": {
+        "value-or-function": "^3.0.0"
+      }
+    },
+    "resolve-url": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
+      "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=",
+      "dev": true
+    },
+    "ret": {
+      "version": "0.1.15",
+      "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
+      "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
+      "dev": true
+    },
+    "rimraf": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+      "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+      "dev": true,
+      "requires": {
+        "glob": "^7.1.3"
+      }
+    },
+    "safe-buffer": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+      "dev": true
+    },
+    "safe-regex": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
+      "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
+      "dev": true,
+      "requires": {
+        "ret": "~0.1.10"
+      }
+    },
+    "safer-buffer": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+      "dev": true
+    },
+    "saxes": {
+      "version": "3.1.11",
+      "resolved": "https://registry.npmjs.org/saxes/-/saxes-3.1.11.tgz",
+      "integrity": "sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g==",
+      "dev": true,
+      "requires": {
+        "xmlchars": "^2.1.1"
+      }
+    },
+    "semver": {
+      "version": "5.7.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+      "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+      "dev": true
+    },
+    "semver-greatest-satisfied-range": {
+      "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": {
+        "sver-compat": "^1.5.0"
+      }
+    },
+    "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
+    },
+    "set-value": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
+      "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
+      "dev": true,
+      "requires": {
+        "extend-shallow": "^2.0.1",
+        "is-extendable": "^0.1.1",
+        "is-plain-object": "^2.0.3",
+        "split-string": "^3.0.1"
+      },
+      "dependencies": {
+        "is-plain-object": {
+          "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"
+          }
+        }
+      }
+    },
+    "snapdragon": {
+      "version": "0.8.2",
+      "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
+      "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
+      "dev": true,
+      "requires": {
+        "base": "^0.11.1",
+        "debug": "^2.2.0",
+        "define-property": "^0.2.5",
+        "extend-shallow": "^2.0.1",
+        "map-cache": "^0.2.2",
+        "source-map": "^0.5.6",
+        "source-map-resolve": "^0.5.0",
+        "use": "^3.1.0"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^0.1.0"
+          }
+        },
+        "is-accessor-descriptor": {
+          "version": "0.1.6",
+          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+          "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+          "dev": true,
+          "requires": {
+            "kind-of": "^3.0.2"
+          },
+          "dependencies": {
+            "kind-of": {
+              "version": "3.2.2",
+              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+              "dev": true,
+              "requires": {
+                "is-buffer": "^1.1.5"
+              }
+            }
+          }
+        },
+        "is-data-descriptor": {
+          "version": "0.1.4",
+          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+          "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+          "dev": true,
+          "requires": {
+            "kind-of": "^3.0.2"
+          },
+          "dependencies": {
+            "kind-of": {
+              "version": "3.2.2",
+              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+              "dev": true,
+              "requires": {
+                "is-buffer": "^1.1.5"
+              }
+            }
+          }
+        },
+        "is-descriptor": {
+          "version": "0.1.6",
+          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+          "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+          "dev": true,
+          "requires": {
+            "is-accessor-descriptor": "^0.1.6",
+            "is-data-descriptor": "^0.1.4",
+            "kind-of": "^5.0.0"
+          }
+        },
+        "source-map": {
+          "version": "0.5.7",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+          "dev": true
+        }
+      }
+    },
+    "snapdragon-node": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
+      "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
+      "dev": true,
+      "requires": {
+        "define-property": "^1.0.0",
+        "isobject": "^3.0.0",
+        "snapdragon-util": "^3.0.1"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^1.0.0"
+          }
+        }
+      }
+    },
+    "snapdragon-util": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
+      "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
+      "dev": true,
+      "requires": {
+        "kind-of": "^3.2.0"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
+    },
+    "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
+    },
+    "source-map-resolve": {
+      "version": "0.5.3",
+      "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz",
+      "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==",
+      "dev": true,
+      "requires": {
+        "atob": "^2.1.2",
+        "decode-uri-component": "^0.2.0",
+        "resolve-url": "^0.2.1",
+        "source-map-url": "^0.4.0",
+        "urix": "^0.1.0"
+      }
+    },
+    "source-map-url": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.1.tgz",
+      "integrity": "sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==",
+      "dev": true
+    },
+    "sparkles": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz",
+      "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==",
+      "dev": true
+    },
+    "spdx-correct": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
+      "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
+      "dev": true,
+      "requires": {
+        "spdx-expression-parse": "^3.0.0",
+        "spdx-license-ids": "^3.0.0"
+      }
+    },
+    "spdx-exceptions": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
+      "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
+      "dev": true
+    },
+    "spdx-expression-parse": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+      "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
+      "dev": true,
+      "requires": {
+        "spdx-exceptions": "^2.1.0",
+        "spdx-license-ids": "^3.0.0"
+      }
+    },
+    "spdx-license-ids": {
+      "version": "3.0.7",
+      "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz",
+      "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==",
+      "dev": true
+    },
+    "split": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz",
+      "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==",
+      "dev": true,
+      "requires": {
+        "through": "2"
+      }
+    },
+    "split-string": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
+      "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
+      "dev": true,
+      "requires": {
+        "extend-shallow": "^3.0.0"
+      },
+      "dependencies": {
+        "extend-shallow": {
+          "version": "3.0.2",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+          "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+          "dev": true,
+          "requires": {
+            "assign-symbols": "^1.0.0",
+            "is-extendable": "^1.0.1"
+          }
+        },
+        "is-extendable": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+          "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+          "dev": true,
+          "requires": {
+            "is-plain-object": "^2.0.4"
+          }
+        },
+        "is-plain-object": {
+          "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"
+          }
+        }
+      }
+    },
+    "sshpk": {
+      "version": "1.16.1",
+      "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
+      "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
+      "dev": true,
+      "requires": {
+        "asn1": "~0.2.3",
+        "assert-plus": "^1.0.0",
+        "bcrypt-pbkdf": "^1.0.0",
+        "dashdash": "^1.12.0",
+        "ecc-jsbn": "~0.1.1",
+        "getpass": "^0.1.1",
+        "jsbn": "~0.1.0",
+        "safer-buffer": "^2.0.2",
+        "tweetnacl": "~0.14.0"
+      }
+    },
+    "stack-trace": {
+      "version": "0.0.10",
+      "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
+      "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=",
+      "dev": true
+    },
+    "static-extend": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
+      "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=",
+      "dev": true,
+      "requires": {
+        "define-property": "^0.2.5",
+        "object-copy": "^0.1.0"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+          "dev": true,
+          "requires": {
+            "is-descriptor": "^0.1.0"
+          }
+        },
+        "is-accessor-descriptor": {
+          "version": "0.1.6",
+          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+          "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+          "dev": true,
+          "requires": {
+            "kind-of": "^3.0.2"
+          },
+          "dependencies": {
+            "kind-of": {
+              "version": "3.2.2",
+              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+              "dev": true,
+              "requires": {
+                "is-buffer": "^1.1.5"
+              }
+            }
+          }
+        },
+        "is-data-descriptor": {
+          "version": "0.1.4",
+          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+          "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+          "dev": true,
+          "requires": {
+            "kind-of": "^3.0.2"
+          },
+          "dependencies": {
+            "kind-of": {
+              "version": "3.2.2",
+              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+              "dev": true,
+              "requires": {
+                "is-buffer": "^1.1.5"
+              }
+            }
+          }
+        },
+        "is-descriptor": {
+          "version": "0.1.6",
+          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+          "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+          "dev": true,
+          "requires": {
+            "is-accessor-descriptor": "^0.1.6",
+            "is-data-descriptor": "^0.1.4",
+            "kind-of": "^5.0.0"
+          }
+        }
+      }
+    },
+    "stealthy-require": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
+      "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=",
+      "dev": true
+    },
+    "stream-combiner": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz",
+      "integrity": "sha1-rsjLrBd7Vrb0+kec7YwZEs7lKFg=",
+      "dev": true,
+      "requires": {
+        "duplexer": "~0.1.1",
+        "through": "~2.3.4"
+      }
+    },
+    "stream-exhaust": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz",
+      "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==",
+      "dev": true
+    },
+    "stream-shift": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz",
+      "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==",
+      "dev": true
+    },
+    "string_decoder": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+      "dev": true,
+      "requires": {
+        "safe-buffer": "~5.1.0"
+      }
+    },
+    "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.0.0",
+        "is-fullwidth-code-point": "^1.0.0",
+        "strip-ansi": "^3.0.0"
+      }
+    },
+    "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.0.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.0"
+      }
+    },
+    "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.1",
+        "es6-symbol": "^3.1.1"
+      }
+    },
+    "symbol-tree": {
+      "version": "3.2.4",
+      "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
+      "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
+      "dev": true
+    },
+    "through": {
+      "version": "2.3.8",
+      "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+      "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
+      "dev": true
+    },
+    "through2": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
+      "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
+      "dev": true,
+      "requires": {
+        "readable-stream": "~2.3.6",
+        "xtend": "~4.0.1"
+      }
+    },
+    "through2-filter": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/through2-filter/-/through2-filter-3.0.0.tgz",
+      "integrity": "sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA==",
+      "dev": true,
+      "requires": {
+        "through2": "~2.0.0",
+        "xtend": "~4.0.0"
+      }
+    },
+    "time-stamp": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz",
+      "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=",
+      "dev": true
+    },
+    "to-absolute-glob": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz",
+      "integrity": "sha1-GGX0PZ50sIItufFFt4z/fQ98hJs=",
+      "dev": true,
+      "requires": {
+        "is-absolute": "^1.0.0",
+        "is-negated-glob": "^1.0.0"
+      }
+    },
+    "to-object-path": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
+      "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=",
+      "dev": true,
+      "requires": {
+        "kind-of": "^3.0.2"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
+      }
+    },
+    "to-regex": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
+      "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
+      "dev": true,
+      "requires": {
+        "define-property": "^2.0.2",
+        "extend-shallow": "^3.0.2",
+        "regex-not": "^1.0.2",
+        "safe-regex": "^1.1.0"
+      },
+      "dependencies": {
+        "extend-shallow": {
+          "version": "3.0.2",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+          "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+          "dev": true,
+          "requires": {
+            "assign-symbols": "^1.0.0",
+            "is-extendable": "^1.0.1"
+          }
+        },
+        "is-extendable": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+          "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+          "dev": true,
+          "requires": {
+            "is-plain-object": "^2.0.4"
+          }
+        },
+        "is-plain-object": {
+          "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"
+          }
+        }
+      }
+    },
+    "to-regex-range": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+      "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+      "dev": true,
+      "requires": {
+        "is-number": "^3.0.0",
+        "repeat-string": "^1.6.1"
+      }
+    },
+    "to-through": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/to-through/-/to-through-2.0.0.tgz",
+      "integrity": "sha1-/JKtq6ByZHvAtn1rA2ZKoZUJOvY=",
+      "dev": true,
+      "requires": {
+        "through2": "^2.0.3"
+      }
+    },
+    "tough-cookie": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
+      "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
+      "dev": true,
+      "requires": {
+        "psl": "^1.1.28",
+        "punycode": "^2.1.1"
+      }
+    },
+    "tr46": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
+      "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=",
+      "dev": true,
+      "requires": {
+        "punycode": "^2.1.0"
+      }
+    },
+    "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.0.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
+    },
+    "type": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
+      "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
+      "dev": true
+    },
+    "type-check": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+      "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
+      "dev": true,
+      "requires": {
+        "prelude-ls": "~1.1.2"
+      }
+    },
+    "typedarray": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+      "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
+      "dev": true
+    },
+    "unc-path-regex": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz",
+      "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=",
+      "dev": true
+    },
+    "uncss": {
+      "version": "0.17.3",
+      "resolved": "https://registry.npmjs.org/uncss/-/uncss-0.17.3.tgz",
+      "integrity": "sha512-ksdDWl81YWvF/X14fOSw4iu8tESDHFIeyKIeDrK6GEVTQvqJc1WlOEXqostNwOCi3qAj++4EaLsdAgPmUbEyog==",
+      "dev": true,
+      "requires": {
+        "commander": "^2.20.0",
+        "glob": "^7.1.4",
+        "is-absolute-url": "^3.0.1",
+        "is-html": "^1.1.0",
+        "jsdom": "^14.1.0",
+        "lodash": "^4.17.15",
+        "postcss": "^7.0.17",
+        "postcss-selector-parser": "6.0.2",
+        "request": "^2.88.0"
+      }
+    },
+    "undertaker": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.3.0.tgz",
+      "integrity": "sha512-/RXwi5m/Mu3H6IHQGww3GNt1PNXlbeCuclF2QYR14L/2CHPz3DFZkvB5hZ0N/QUkiXWCACML2jXViIQEQc2MLg==",
+      "dev": true,
+      "requires": {
+        "arr-flatten": "^1.0.1",
+        "arr-map": "^2.0.0",
+        "bach": "^1.0.0",
+        "collection-map": "^1.0.0",
+        "es6-weak-map": "^2.0.1",
+        "fast-levenshtein": "^1.0.0",
+        "last-run": "^1.1.0",
+        "object.defaults": "^1.0.0",
+        "object.reduce": "^1.0.0",
+        "undertaker-registry": "^1.0.0"
+      },
+      "dependencies": {
+        "fast-levenshtein": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-1.1.4.tgz",
+          "integrity": "sha1-5qdUzI8V5YmHqpy9J69m/W9OWvk=",
+          "dev": true
+        }
+      }
+    },
+    "undertaker-registry": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-1.0.1.tgz",
+      "integrity": "sha1-XkvaMI5KiirlhPm5pDWaSZglzFA=",
+      "dev": true
+    },
+    "union-value": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
+      "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
+      "dev": true,
+      "requires": {
+        "arr-union": "^3.1.0",
+        "get-value": "^2.0.6",
+        "is-extendable": "^0.1.1",
+        "set-value": "^2.0.1"
+      }
+    },
+    "uniq": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz",
+      "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=",
+      "dev": true
+    },
+    "unique-stream": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/unique-stream/-/unique-stream-2.3.1.tgz",
+      "integrity": "sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A==",
+      "dev": true,
+      "requires": {
+        "json-stable-stringify-without-jsonify": "^1.0.1",
+        "through2-filter": "^3.0.0"
+      }
+    },
+    "unset-value": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
+      "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=",
+      "dev": true,
+      "requires": {
+        "has-value": "^0.3.1",
+        "isobject": "^3.0.0"
+      },
+      "dependencies": {
+        "has-value": {
+          "version": "0.3.1",
+          "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
+          "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=",
+          "dev": true,
+          "requires": {
+            "get-value": "^2.0.3",
+            "has-values": "^0.1.4",
+            "isobject": "^2.0.0"
+          },
+          "dependencies": {
+            "isobject": {
+              "version": "2.1.0",
+              "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
+              "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
+              "dev": true,
+              "requires": {
+                "isarray": "1.0.0"
+              }
+            }
+          }
+        },
+        "has-values": {
+          "version": "0.1.4",
+          "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
+          "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=",
+          "dev": true
+        }
+      }
+    },
+    "upath": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",
+      "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==",
+      "dev": true
+    },
+    "uri-js": {
+      "version": "4.4.1",
+      "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+      "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+      "dev": true,
+      "requires": {
+        "punycode": "^2.1.0"
+      }
+    },
+    "urix": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
+      "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=",
+      "dev": true
+    },
+    "use": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
+      "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
+      "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.4.0",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+      "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+      "dev": true
+    },
+    "v8flags": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz",
+      "integrity": "sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg==",
+      "dev": true,
+      "requires": {
+        "homedir-polyfill": "^1.0.1"
+      }
+    },
+    "validate-npm-package-license": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+      "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+      "dev": true,
+      "requires": {
+        "spdx-correct": "^3.0.0",
+        "spdx-expression-parse": "^3.0.0"
+      }
+    },
+    "value-or-function": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-3.0.0.tgz",
+      "integrity": "sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM=",
+      "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.2.0"
+      }
+    },
+    "vinyl": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz",
+      "integrity": "sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==",
+      "dev": true,
+      "requires": {
+        "clone": "^2.1.1",
+        "clone-buffer": "^1.0.0",
+        "clone-stats": "^1.0.0",
+        "cloneable-readable": "^1.0.0",
+        "remove-trailing-separator": "^1.0.1",
+        "replace-ext": "^1.0.0"
+      }
+    },
+    "vinyl-fs": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-3.0.3.tgz",
+      "integrity": "sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng==",
+      "dev": true,
+      "requires": {
+        "fs-mkdirp-stream": "^1.0.0",
+        "glob-stream": "^6.1.0",
+        "graceful-fs": "^4.0.0",
+        "is-valid-glob": "^1.0.0",
+        "lazystream": "^1.0.0",
+        "lead": "^1.0.0",
+        "object.assign": "^4.0.4",
+        "pumpify": "^1.3.5",
+        "readable-stream": "^2.3.3",
+        "remove-bom-buffer": "^3.0.0",
+        "remove-bom-stream": "^1.2.0",
+        "resolve-options": "^1.1.0",
+        "through2": "^2.0.0",
+        "to-through": "^2.0.0",
+        "value-or-function": "^3.0.0",
+        "vinyl": "^2.0.0",
+        "vinyl-sourcemap": "^1.1.0"
+      }
+    },
+    "vinyl-sourcemap": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz",
+      "integrity": "sha1-kqgAWTo4cDqM2xHYswCtS+Y7PhY=",
+      "dev": true,
+      "requires": {
+        "append-buffer": "^1.0.2",
+        "convert-source-map": "^1.5.0",
+        "graceful-fs": "^4.1.6",
+        "normalize-path": "^2.1.1",
+        "now-and-later": "^2.0.0",
+        "remove-bom-buffer": "^3.0.0",
+        "vinyl": "^2.0.0"
+      },
+      "dependencies": {
+        "normalize-path": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
+          "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
+          "dev": true,
+          "requires": {
+            "remove-trailing-separator": "^1.0.1"
+          }
+        }
+      }
+    },
+    "w3c-hr-time": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
+      "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==",
+      "dev": true,
+      "requires": {
+        "browser-process-hrtime": "^1.0.0"
+      }
+    },
+    "w3c-xmlserializer": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz",
+      "integrity": "sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg==",
+      "dev": true,
+      "requires": {
+        "domexception": "^1.0.1",
+        "webidl-conversions": "^4.0.2",
+        "xml-name-validator": "^3.0.0"
+      }
+    },
+    "webidl-conversions": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
+      "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==",
+      "dev": true
+    },
+    "whatwg-encoding": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz",
+      "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==",
+      "dev": true,
+      "requires": {
+        "iconv-lite": "0.4.24"
+      }
+    },
+    "whatwg-mimetype": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz",
+      "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==",
+      "dev": true
+    },
+    "whatwg-url": {
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz",
+      "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==",
+      "dev": true,
+      "requires": {
+        "lodash.sortby": "^4.7.0",
+        "tr46": "^1.0.1",
+        "webidl-conversions": "^4.0.2"
+      }
+    },
+    "which": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+      "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+      "dev": true,
+      "requires": {
+        "isexe": "^2.0.0"
+      }
+    },
+    "which-module": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz",
+      "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=",
+      "dev": true
+    },
+    "word-wrap": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
+      "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==",
+      "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.1",
+        "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
+    },
+    "ws": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.2.tgz",
+      "integrity": "sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==",
+      "dev": true,
+      "requires": {
+        "async-limiter": "~1.0.0"
+      }
+    },
+    "xml-name-validator": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz",
+      "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==",
+      "dev": true
+    },
+    "xmlchars": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
+      "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
+      "dev": true
+    },
+    "xtend": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+      "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+      "dev": true
+    },
+    "y18n": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz",
+      "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==",
+      "dev": true
+    },
+    "yargs": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.1.tgz",
+      "integrity": "sha512-huO4Fr1f9PmiJJdll5kwoS2e4GqzGSsMT3PPMpOwoVkOK8ckqAewMTZyA6LXVQWflleb/Z8oPBEvNsMft0XE+g==",
+      "dev": true,
+      "requires": {
+        "camelcase": "^3.0.0",
+        "cliui": "^3.2.0",
+        "decamelize": "^1.1.1",
+        "get-caller-file": "^1.0.1",
+        "os-locale": "^1.4.0",
+        "read-pkg-up": "^1.0.1",
+        "require-directory": "^2.1.1",
+        "require-main-filename": "^1.0.1",
+        "set-blocking": "^2.0.0",
+        "string-width": "^1.0.2",
+        "which-module": "^1.0.0",
+        "y18n": "^3.2.1",
+        "yargs-parser": "5.0.0-security.0"
+      }
+    },
+    "yargs-parser": {
+      "version": "5.0.0-security.0",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0-security.0.tgz",
+      "integrity": "sha512-T69y4Ps64LNesYxeYGYPvfoMTt/7y1XtfpIslUeK4um+9Hu7hlGoRtaDLvdXb7+/tfq4opVa2HRY5xGip022rQ==",
+      "dev": true,
+      "requires": {
+        "camelcase": "^3.0.0",
+        "object.assign": "^4.1.0"
+      }
+    },
+    "yaserver": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/yaserver/-/yaserver-0.3.0.tgz",
+      "integrity": "sha512-q7O/gz6B46jA0QZptLxPDBWwojnbpxXwjHGHwWKghsPQBNDcTwOmJ2wTbFAoU6me1M/Z995eATE5THYmGzGE6A==",
+      "dev": true
+    }
+  }
+}
diff --git a/website/package.json b/website/package.json
new file mode 100644
index 0000000..5ef380f
--- /dev/null
+++ b/website/package.json
@@ -0,0 +1,15 @@
+{
+  "scripts": {
+    "prettier": "prettier --write .",
+    "simpleserver": "yaserver --root ./ --port 8888"
+  },
+  "devDependencies": {
+    "clean-css": "^5.1.1",
+    "event-stream": "4.0.1",
+    "gulp": "^4.0.2",
+    "monaco-editor": "^0.23.0",
+    "rimraf": "^3.0.2",
+    "uncss": "^0.17.3",
+    "yaserver": "^0.3.0"
+  }
+}