diff options
678 files changed, 78590 insertions, 17878 deletions
diff --git a/.github/workflows/gradle-test.pr.yml b/.github/workflows/gradle-test.pr.yml new file mode 100644 index 00000000..e0e7944e --- /dev/null +++ b/.github/workflows/gradle-test.pr.yml @@ -0,0 +1,16 @@ +name: CI + +on: pull_request + +jobs: + test: + strategy: + matrix: + os: [ubuntu-latest, windows-latest] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-java@v1 + with: + java-version: 11 + - run: ./gradlew clean test --stacktrace diff --git a/.github/workflows/s3-snapshots.yml b/.github/workflows/s3-snapshots.yml new file mode 100644 index 00000000..354ee20f --- /dev/null +++ b/.github/workflows/s3-snapshots.yml @@ -0,0 +1,149 @@ +name: S3-snapshots + +on: push + +env: + branch-name: ${GITHUB_REF#refs/heads/} + bucket-name: 'dokka-snapshots' + +jobs: + coroutines: + runs-on: ubuntu-latest + steps: + - name: Checkout dokka + uses: actions/checkout@v2 + with: + path: dokka + + - uses: actions/setup-java@v1 + with: + java-version: 11 + + - name: Publish dokka locally + run: ./gradlew clean publishToMavenLocal --stacktrace + working-directory: ./dokka + + - name: Get current dokka version + run: echo "::set-env name=DOKKA_VERSION::`./gradlew :properties | grep '^version:.*' | cut -d ' ' -f 2`" + working-directory: ./dokka + + - name: Checkout coroutines + uses: actions/checkout@v2 + with: + repository: 'kamildoleglo/kotlinx.coroutines' + ref: 'aws' + path: coroutines + + - name: Document coroutines + run: ./gradlew clean dokkaHtml :dokkaHtmlMultimodule -Pdokka_version=$DOKKA_VERSION --stacktrace + working-directory: ./coroutines + + - name: Configure AWS credentials for S3 access + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: eu-central-1 + + - name: Copy files to dokka's S3 bucket + run: | + aws s3 --recursive rm s3://${{ env.bucket-name }}/${{ env.branch-name }}/coroutines/prev + aws s3 --recursive mv s3://${{ env.bucket-name }}/${{ env.branch-name }}/coroutines/latest s3://${{ env.bucket-name }}/${{ env.branch-name }}/coroutines/prev + aws s3 sync ./coroutines/build/dokka s3://${{ env.bucket-name }}/${{ env.branch-name }}/coroutines/latest + - name: Print link + run: echo http://dokka-snapshots.s3.eu-central-1.amazonaws.com/${{ env.branch-name }}/coroutines/latest/kotlinx-coroutines-/index.html + + stdlib: + runs-on: ubuntu-latest + steps: + - name: Checkout dokka + uses: actions/checkout@v2 + with: + path: dokka + + - uses: actions/setup-java@v1 + with: + java-version: 11 + + - name: Publish dokka locally + run: ./gradlew clean publishToMavenLocal --stacktrace + working-directory: ./dokka + + - name: Get current dokka version + run: echo "::set-env name=DOKKA_VERSION::`./gradlew :properties | grep '^version:.*' | cut -d ' ' -f 2`" + working-directory: ./dokka + + - name: Checkout stdlib + uses: actions/checkout@v2 + with: + repository: 'kamildoleglo/kotlin-dokka-stdlib' + ref: 'aws' + path: stdlib + + - name: Document stdlib + run: ./gradlew clean callDokka -Pdokka_version=$DOKKA_VERSION --stacktrace + working-directory: ./stdlib + + - name: Configure AWS credentials for S3 access + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: eu-central-1 + + - name: Copy files to dokka's S3 bucket + run: | + aws s3 --recursive rm s3://${{ env.bucket-name }}/${{ env.branch-name }}/stdlib/prev + aws s3 --recursive mv s3://${{ env.bucket-name }}/${{ env.branch-name }}/stdlib/latest s3://${{ env.bucket-name }}/${{ env.branch-name }}/stdlib/prev + aws s3 sync ./stdlib/build/dokka s3://${{ env.bucket-name }}/${{ env.branch-name }}/stdlib/latest + + - name: Print link + run: echo http://dokka-snapshots.s3.eu-central-1.amazonaws.com/${{ env.branch-name }}/stdlib/latest/kotlin-stdlib/kotlin-stdlib/index.html + + biojava: + runs-on: ubuntu-latest + steps: + - name: Checkout dokka + uses: actions/checkout@v2 + with: + path: dokka + + - uses: actions/setup-java@v1 + with: + java-version: 11 + + - name: Publish dokka locally + run: ./gradlew clean publishToMavenLocal --stacktrace + working-directory: ./dokka + + - name: Get current dokka version + run: echo "::set-env name=DOKKA_VERSION::`./gradlew :properties | grep '^version:.*' | cut -d ' ' -f 2`" + working-directory: ./dokka + + - name: Checkout biojava + uses: actions/checkout@v2 + with: + repository: 'kamildoleglo/biojava' + ref: 'aws' + path: biojava + + - name: Document biojava-core + run: mvn site -pl biojava-core "-Ddokka-version=$DOKKA_VERSION" + working-directory: ./biojava + + - name: Configure AWS credentials for S3 access + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: eu-central-1 + + - name: Copy files to dokka's S3 bucket + run: | + aws s3 --recursive rm s3://${{ env.bucket-name }}/${{ env.branch-name }}/biojava/prev + aws s3 --recursive mv s3://${{ env.bucket-name }}/${{ env.branch-name }}/biojava/latest s3://${{ env.bucket-name }}/${{ env.branch-name }}/biojava/prev + aws s3 sync ./biojava/biojava-core/target/dokkaJavadoc s3://${{ env.bucket-name }}/${{ env.branch-name }}/biojava/latest + + - name: Print link + run: echo http://dokka-snapshots.s3.eu-central-1.amazonaws.com/${{ env.branch-name }}/biojava/latest/index.html + @@ -37,6 +37,8 @@ hs_err_pid* .idea/shelf .idea/jsLibraryMappings.xml .idea/modules.xml +.idea/misc.xml +.idea/compiler.xml # Sensitive or high-churn files: .idea/dataSources.ids @@ -94,4 +96,11 @@ gradle-app.setting local.properties android.local.properties -!runners/gradle-integration-tests/testData/basic/classDir/**/*.class
\ No newline at end of file +!integration-tests/gradle-integration-tests/testData/basic/classDir/**/*.class + +core/out/ +runners/**/out/ +.idea +plugins/base/src/main/resources/dokka/scripts/main.js +plugins/base/src/main/resources/dokka/scripts/main.js.map +/.idea/compiler.xml diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index ba49a93e..31d977a0 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -1,24 +1,26 @@ <component name="ProjectCodeStyleConfiguration"> <code_scheme name="Project" version="173"> <JetCodeStyleSettings> - <option name="CONTINUATION_INDENT_IN_PARAMETER_LISTS" value="false" /> - <option name="CONTINUATION_INDENT_IN_ARGUMENT_LISTS" value="false" /> - <option name="CONTINUATION_INDENT_FOR_EXPRESSION_BODIES" value="false" /> - <option name="CONTINUATION_INDENT_FOR_CHAINED_CALLS" value="false" /> - <option name="CONTINUATION_INDENT_IN_SUPERTYPE_LISTS" value="false" /> - <option name="CONTINUATION_INDENT_IN_IF_CONDITIONS" value="false" /> - <option name="WRAP_EXPRESSION_BODY_FUNCTIONS" value="1" /> + <option name="PACKAGES_TO_USE_STAR_IMPORTS"> + <value> + <package name="java.util" alias="false" withSubpackages="false" /> + <package name="kotlinx.android.synthetic" alias="false" withSubpackages="true" /> + <package name="io.ktor" alias="false" withSubpackages="true" /> + </value> + </option> + <option name="PACKAGES_IMPORT_LAYOUT"> + <value> + <package name="" alias="false" withSubpackages="true" /> + <package name="java" alias="false" withSubpackages="true" /> + <package name="javax" alias="false" withSubpackages="true" /> + <package name="kotlin" alias="false" withSubpackages="true" /> + <package name="" alias="true" withSubpackages="true" /> + </value> + </option> + <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> </JetCodeStyleSettings> <codeStyleSettings language="kotlin"> - <option name="CALL_PARAMETERS_WRAP" value="5" /> - <option name="CALL_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" /> - <option name="CALL_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" /> - <option name="METHOD_PARAMETERS_WRAP" value="5" /> - <option name="METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE" value="true" /> - <option name="METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE" value="true" /> - <option name="EXTENDS_LIST_WRAP" value="1" /> - <option name="METHOD_CALL_CHAIN_WRAP" value="1" /> - <option name="ASSIGNMENT_WRAP" value="1" /> + <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> </codeStyleSettings> </code_scheme> </component>
\ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml deleted file mode 100644 index 5d169de7..00000000 --- a/.idea/compiler.xml +++ /dev/null @@ -1,77 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project version="4"> - <component name="CompilerConfiguration"> - <wildcardResourcePatterns> - <entry name="!?*.java" /> - <entry name="!?*.form" /> - <entry name="!?*.class" /> - <entry name="!?*.groovy" /> - <entry name="!?*.scala" /> - <entry name="!?*.flex" /> - <entry name="!?*.kt" /> - <entry name="!?*.clj" /> - </wildcardResourcePatterns> - <bytecodeTargetLevel> - <module name="android-gradle-plugin_main" target="1.8" /> - <module name="android-gradle-plugin_test" target="1.8" /> - <module name="ant_main" target="1.8" /> - <module name="ant_test" target="1.8" /> - <module name="buildSrc_main" target="1.8" /> - <module name="buildSrc_test" target="1.8" /> - <module name="cli_main" target="1.8" /> - <module name="cli_test" target="1.8" /> - <module name="core_main" target="1.8" /> - <module name="core_test" target="1.8" /> - <module name="dokka.buildSrc.main" target="1.8" /> - <module name="dokka.buildSrc.test" target="1.8" /> - <module name="dokka.core.main" target="1.8" /> - <module name="dokka.core.test" target="1.8" /> - <module name="dokka.integration.main" target="1.8" /> - <module name="dokka.integration.test" target="1.8" /> - <module name="dokka.runners.android-gradle-plugin.main" target="1.8" /> - <module name="dokka.runners.android-gradle-plugin.test" target="1.8" /> - <module name="dokka.runners.ant.main" target="1.8" /> - <module name="dokka.runners.ant.test" target="1.8" /> - <module name="dokka.runners.cli.main" target="1.8" /> - <module name="dokka.runners.cli.test" target="1.8" /> - <module name="dokka.runners.fatjar.main" target="1.8" /> - <module name="dokka.runners.fatjar.test" target="1.8" /> - <module name="dokka.runners.gradle-integration-tests.main" target="1.8" /> - <module name="dokka.runners.gradle-integration-tests.test" target="1.8" /> - <module name="dokka.runners.gradle-plugin.main" target="1.8" /> - <module name="dokka.runners.gradle-plugin.test" target="1.8" /> - <module name="dokka.runners.maven-plugin.main" target="1.8" /> - <module name="dokka.runners.maven-plugin.test" target="1.8" /> - <module name="fatjar_main" target="1.8" /> - <module name="fatjar_test" target="1.8" /> - <module name="gradle-integration-tests_main" target="1.8" /> - <module name="gradle-integration-tests_test" target="1.8" /> - <module name="gradle-plugin_main" target="1.8" /> - <module name="gradle-plugin_test" target="1.8" /> - <module name="integration_main" target="1.8" /> - <module name="integration_test" target="1.8" /> - <module name="maven-plugin_main" target="1.8" /> - <module name="maven-plugin_test" target="1.8" /> - <module name="org.jetbrains.dokka.android-gradle-plugin.main" target="1.8" /> - <module name="org.jetbrains.dokka.android-gradle-plugin.test" target="1.8" /> - <module name="org.jetbrains.dokka.ant.main" target="1.8" /> - <module name="org.jetbrains.dokka.ant.test" target="1.8" /> - <module name="org.jetbrains.dokka.buildSrc.main" target="1.8" /> - <module name="org.jetbrains.dokka.buildSrc.test" target="1.8" /> - <module name="org.jetbrains.dokka.cli.main" target="1.8" /> - <module name="org.jetbrains.dokka.cli.test" target="1.8" /> - <module name="org.jetbrains.dokka.core.main" target="1.8" /> - <module name="org.jetbrains.dokka.core.test" target="1.8" /> - <module name="org.jetbrains.dokka.fatjar.main" target="1.8" /> - <module name="org.jetbrains.dokka.fatjar.test" target="1.8" /> - <module name="org.jetbrains.dokka.gradle-integration-tests.main" target="1.8" /> - <module name="org.jetbrains.dokka.gradle-integration-tests.test" target="1.8" /> - <module name="org.jetbrains.dokka.gradle-plugin.main" target="1.8" /> - <module name="org.jetbrains.dokka.gradle-plugin.test" target="1.8" /> - <module name="org.jetbrains.dokka.integration.main" target="1.8" /> - <module name="org.jetbrains.dokka.integration.test" target="1.8" /> - <module name="org.jetbrains.dokka.maven-plugin.main" target="1.8" /> - <module name="org.jetbrains.dokka.maven-plugin.test" target="1.8" /> - </bytecodeTargetLevel> - </component> -</project>
\ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml index 664d1ad2..0c589859 100644 --- a/.idea/jarRepositories.xml +++ b/.idea/jarRepositories.xml @@ -32,11 +32,6 @@ <option name="url" value="https://teamcity.jetbrains.com/guestAuth/repository/download/Kotlin_dev_CompilerAllPlugins/1.3.20-dev-564/maven" /> </remote-repository> <remote-repository> - <option name="id" value="MavenLocal" /> - <option name="name" value="MavenLocal" /> - <option name="url" value="file:$MAVEN_REPOSITORY$/" /> - </remote-repository> - <remote-repository> <option name="id" value="maven7" /> <option name="name" value="maven7" /> <option name="url" value="https://dl.bintray.com/orangy/maven" /> @@ -47,6 +42,11 @@ <option name="url" value="https://dl.bintray.com/jetbrains/markdown" /> </remote-repository> <remote-repository> + <option name="id" value="MavenLocal" /> + <option name="name" value="MavenLocal" /> + <option name="url" value="file:$MAVEN_REPOSITORY$/" /> + </remote-repository> + <remote-repository> <option name="id" value="BintrayJCenter" /> <option name="name" value="BintrayJCenter" /> <option name="url" value="https://jcenter.bintray.com/" /> @@ -76,5 +76,30 @@ <option name="name" value="Google" /> <option name="url" value="https://dl.google.com/dl/android/maven2/" /> </remote-repository> + <remote-repository> + <option name="id" value="maven10" /> + <option name="name" value="maven10" /> + <option name="url" value="https://kotlin.bintray.com/kotlin-plugin" /> + </remote-repository> + <remote-repository> + <option name="id" value="maven6" /> + <option name="name" value="maven6" /> + <option name="url" value="https://teamcity.jetbrains.com/guestAuth/repository/download/Kotlin_dev_CompilerAllPlugins/1.3.61/maven" /> + </remote-repository> + <remote-repository> + <option name="id" value="maven" /> + <option name="name" value="maven" /> + <option name="url" value="https://dl.bintray.com/jetbrains/markdown/" /> + </remote-repository> + <remote-repository> + <option name="id" value="maven" /> + <option name="name" value="maven" /> + <option name="url" value="https://dl.bintray.com/kotlin/kotlin-dev/" /> + </remote-repository> + <remote-repository> + <option name="id" value="MavenLocal" /> + <option name="name" value="MavenLocal" /> + <option name="url" value="file:$MAVEN_REPOSITORY$" /> + </remote-repository> </component> </project>
\ No newline at end of file diff --git a/.idea/kotlinScripting.xml b/.idea/kotlinScripting.xml index a6fe551d..bc444dea 100644 --- a/.idea/kotlinScripting.xml +++ b/.idea/kotlinScripting.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="KotlinScriptingSettings"> - <option name="isAutoReloadEnabled" value="true" /> + <option name="suppressDefinitionsCheck" value="true" /> </component> </project>
\ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 38d23597..35cd5ac3 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,11 +1,10 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> - <component name="Kotlin2JsCompilerArguments"> - <option name="sourceMapEmbedSources" /> - <option name="sourceMapPrefix" /> - </component> <component name="KotlinCommonCompilerArguments"> - <option name="apiVersion" value="1.1" /> - <option name="languageVersion" value="1.2" /> + <option name="apiVersion" value="1.3" /> + <option name="languageVersion" value="1.3" /> + </component> + <component name="KotlinCompilerSettings"> + <option name="additionalArguments" value="-version -Xskip-metadata-version-check" /> </component> </project>
\ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index e208459b..00000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,6 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project version="4"> - <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK"> - <output url="file://$PROJECT_DIR$/out" /> - </component> -</project>
\ No newline at end of file diff --git a/.run/it-android-0_dokka.run.xml b/.run/it-android-0_dokka.run.xml new file mode 100644 index 00000000..dcb61e2e --- /dev/null +++ b/.run/it-android-0_dokka.run.xml @@ -0,0 +1,24 @@ +<component name="ProjectRunConfigurationManager"> + <configuration default="false" name="it-android-0:dokka" type="GradleRunConfiguration" factoryName="Gradle" folderName="sample-projects"> + <ExternalSystemSettings> + <option name="executionName" /> + <option name="externalProjectPath" value="$PROJECT_DIR$/integration-tests/gradle/projects/it-android-0" /> + <option name="externalSystemIdString" value="GRADLE" /> + <option name="scriptParameters" value="" /> + <option name="taskDescriptions"> + <list /> + </option> + <option name="taskNames"> + <list> + <option value="clean" /> + <option value="dokka" /> + </list> + </option> + <option name="vmOptions" value="" /> + </ExternalSystemSettings> + <GradleScriptDebugEnabled>true</GradleScriptDebugEnabled> + <method v="2"> + <option name="Gradle.BeforeRunTask" enabled="false" tasks="publishToMavenLocal" externalProjectPath="$PROJECT_DIR$" vmOptions="" scriptParameters="" /> + </method> + </configuration> +</component>
\ No newline at end of file diff --git a/.run/it-basic_dokka.run.xml b/.run/it-basic_dokka.run.xml new file mode 100644 index 00000000..42d6ca63 --- /dev/null +++ b/.run/it-basic_dokka.run.xml @@ -0,0 +1,24 @@ +<component name="ProjectRunConfigurationManager"> + <configuration default="false" name="it-basic:dokka" type="GradleRunConfiguration" factoryName="Gradle" folderName="sample-projects"> + <ExternalSystemSettings> + <option name="executionName" /> + <option name="externalProjectPath" value="$PROJECT_DIR$/integration-tests/gradle/projects/it-basic" /> + <option name="externalSystemIdString" value="GRADLE" /> + <option name="scriptParameters" value="" /> + <option name="taskDescriptions"> + <list /> + </option> + <option name="taskNames"> + <list> + <option value="clean" /> + <option value="dokka" /> + </list> + </option> + <option name="vmOptions" value="" /> + </ExternalSystemSettings> + <GradleScriptDebugEnabled>true</GradleScriptDebugEnabled> + <method v="2"> + <option name="Gradle.BeforeRunTask" enabled="true" tasks="publishToMavenLocal" externalProjectPath="$PROJECT_DIR$" vmOptions="" scriptParameters="" /> + </method> + </configuration> +</component>
\ No newline at end of file diff --git a/.run/it-multiplatform-0_dokka.run.xml b/.run/it-multiplatform-0_dokka.run.xml new file mode 100644 index 00000000..3ed501ea --- /dev/null +++ b/.run/it-multiplatform-0_dokka.run.xml @@ -0,0 +1,24 @@ +<component name="ProjectRunConfigurationManager"> + <configuration default="false" name="it-multiplatform-0:dokka" type="GradleRunConfiguration" factoryName="Gradle" folderName="sample-projects"> + <ExternalSystemSettings> + <option name="executionName" /> + <option name="externalProjectPath" value="$PROJECT_DIR$/integration-tests/gradle/projects/it-multiplatform-0" /> + <option name="externalSystemIdString" value="GRADLE" /> + <option name="scriptParameters" value="" /> + <option name="taskDescriptions"> + <list /> + </option> + <option name="taskNames"> + <list> + <option value="clean" /> + <option value="dokka" /> + </list> + </option> + <option name="vmOptions" value="" /> + </ExternalSystemSettings> + <GradleScriptDebugEnabled>true</GradleScriptDebugEnabled> + <method v="2"> + <option name="Gradle.BeforeRunTask" enabled="false" tasks="publishToMavenLocal" externalProjectPath="$PROJECT_DIR$" vmOptions="" scriptParameters="" /> + </method> + </configuration> +</component>
\ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3e81c6d4..b9e12ce2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,11 +1,3 @@ -## Branches - -As of late January 2020: - -* master is the latest released version (0.10.0). -* dev-0.10.1 is the maintenance branch for 0.10.0. It will contain mostly bugfixes. -* dev-0.11.0 is a big rewrite of dokka, it's changing fast and things may break but this is where new features should be developed. - ## Building dokka Dokka is built with Gradle. To build it, use `./gradlew build`. @@ -7,215 +7,274 @@ and can generate documentation in multiple formats including standard Javadoc, H ## Using dokka +### Plugins +Dokka can be customized with plugins. Each output format is internally a plugin. +Additionally, `kotlin-as-java` plugin can be used to generate documentation as seen from Java perspective. +Currently maintained plugins are: +* `dokka-base` - the main plugin needed to run dokka, contains html format +* `gfm-plugin` - configures `GFM` output format +* `jekyll-plugin` - configures `Jekyll` output format +* `javadoc-plugin` - configures `Javadoc` output format, automatically applies `kotlin-as-java-plugin` +* `kotlin-as-java-plugin` - translates Kotlin definitions to Java + +Please see the usage instructions below for how to add plugins to dokka. + +### Source sets +Dokka generates documentation based on source sets. + +For single-platform projects, there is almost always only one source set - `main`. + +For multi-platform projects, source sets are the same as in Kotlin plugin: + + * One source set for each platform, eg. `jvmMain` or `jsMain`; + * One source set for each common source set, eg. the default `commonMain` and custom ones like `jsAndJvmMain`. + +When configuring multi-platform projects manually (eg. in the CLI or in Gradle without autoconfiguration) +source sets must declare their dependent source sets. +Eg. in the following Kotlin plugin configuration: + +* `jsMain` and `jvmMain` both depend on `commonMain` (by default and transitively) and `jsAndJvmMain`; +* `linuxX64Main` only depends on `commonMain`. + +```kotlin +kotlin { // Kotlin plugin configuration + jvm() + js() + linuxX64() + + sourceSets { + val commonMain by getting {} + val jvmAndJsSecondCommonMain by creating { dependsOn(commonMain) } + val jvmMain by getting { dependsOn(jvmAndJsSecondCommonMain) } + val jsMain by getting { dependsOn(jvmAndJsSecondCommonMain) } + val linuxX64Main by getting { dependsOn(commonMain) } + } +} +``` + ### Using the Gradle plugin -```groovy +The preferred way is to use `plugins` block. Since Kotlin compiler used by dokka is still in EAP, +you not only need to add `dokka` to the `build.gradle.kts` file, but you also need to modify the `settings.gradle.kts` file: + +build.gradle.kts: +```kotlin +plugins { + id("org.jetbrains.dokka") version "1.4-M3" +} + +repositories { + jcenter() // or maven(url="https://dl.bintray.com/kotlin/dokka") + maven("https://dl.bintray.com/kotlin/kotlin-eap") +} +``` + +settings.gradle.kts: +```kotlin +pluginManagement { + repositories { + gradlePluginPortal() + maven("https://dl.bintray.com/kotlin/kotlin-eap") + } +} +``` + +You can also use the legacy plugin application method with `buildscript` block. +Note that by using the `buildscript` way type-safe accessors are not available in Gradle Kotlin DSL, +eg. you'll have to use `named<DokkaTask>("dokkaHtml")` instead of `dokkaHtml`: + +```kotlin buildscript { repositories { jcenter() + maven("https://dl.bintray.com/kotlin/kotlin-eap") } dependencies { - classpath "org.jetbrains.dokka:dokka-gradle-plugin:${dokka_version}" + classpath("org.jetbrains.dokka:dokka-gradle-plugin:${dokka_version}") } } repositories { - jcenter() // or maven { url 'https://dl.bintray.com/kotlin/dokka' } + jcenter() // or maven(url="https://dl.bintray.com/kotlin/dokka") + maven("https://dl.bintray.com/kotlin/kotlin-eap") } -apply plugin: 'org.jetbrains.dokka' +apply(plugin="org.jetbrains.dokka") ``` -or using the plugins block: +The plugin adds `dokkaHtml`, `dokkaJavadoc`, `dokkaGfm` and `dokkaJekyll` tasks to the project. + +Each task corresponds to one output format, so you should run `dokkaGfm` when you want to have a documentation in `GFM` format. +Output formats are explained [there](#output_formats) -```groovy -plugins { - id 'org.jetbrains.dokka' version '0.10.1' +If you encounter any problems when migrating from older versions of dokka, please see the [FAQ](https://github.com/Kotlin/dokka/wiki/faq). + +Minimal dokka configuration: + +Kotlin +(single-platform project) +```kotlin +tasks.dokkaHtml { + outputDirectory = "$buildDir/dokka" } -repositories { - jcenter() // or maven { url 'https://dl.bintray.com/kotlin/dokka' } +``` +(mutli-platform project) +```kotlin +tasks.dokkaHtml { + outputDirectory = "$buildDir/dokka" + dokkaSourceSets { + create("jvmMain") + create("jsMain") // or other names, identical to those in Kotlin-plugin + } } ``` -The plugin adds a task named `dokka` to the project. - -If you encounter any problems when migrating from older versions of Dokka, please see the [FAQ](https://github.com/Kotlin/dokka/wiki/faq). - -Minimal dokka configuration: - Groovy -```groovy -dokka { - outputFormat = 'html' +(single-platform project) +```kotlin +dokkaHtml { outputDirectory = "$buildDir/dokka" } ``` - -Kotlin +(mutli-platform project) ```kotlin -tasks { - val dokka by getting(DokkaTask::class) { - outputFormat = "html" - outputDirectory = "$buildDir/dokka" +dokkaHtml { + outputDirectory = "$buildDir/dokka" + dokkaSourceSets { + create("jvmMain") {} + create("jsMain") {} // or other names, identical to those in Kotlin-plugin } } ``` -You may need to add an `import org.jetbrains.dokka.gradle.DokkaTask` to the top of `build.gradle.kts` in this case. -[Output formats](#output_formats) +Dokka documents single-platform as well as multi-platform projects. +Most of the configuration options are set per one source set. +The available configuration options for are shown below: -The available configuration options for single platform are shown below: +```kotlin +dokkaHtml { + outputDirectory = "$buildDir/docs" -```groovy -dokka { - outputFormat = 'html' - outputDirectory = "$buildDir/javadoc" - - // In case of a Gradle multiproject build, you can include subprojects here to get merged documentation - // Note however, that you have to have the Kotlin plugin available in the root project and in the subprojects - subProjects = ["subproject1", "subproject2"] - - // Used for disabling auto extraction of sources and platforms in both multi-platform and single-platform modes - // When set to true, subProject and kotlinTasks are also omitted - disableAutoconfiguration = false + // Used for disabling auto extraction of sources and platforms + // When set to true kotlinTasks are also omitted + disableAutoconfiguration = false // Use default or set to custom path to cache directory // to enable package-list caching // When this is set to default, caches are stored in $USER_HOME/.cache/dokka - cacheRoot = 'default' - - configuration { - moduleName = 'data' + cacheRoot = "default" + dokkaSourceSets { + configureEach { // Or source set name, for single-platform the default source sets are `main` and `test` + moduleDisplayName = "data" - // Use to include or exclude non public members. - includeNonPublic = false - - // Do not output deprecated members. Applies globally, can be overridden by packageOptions - skipDeprecated = false - - // Emit warnings about not documented members. Applies globally, also can be overridden by packageOptions - reportUndocumented = true - - // Do not create index pages for empty packages - skipEmptyPackages = true - - // This is a list of platform names that will be shown in the final result. See the "Platforms" section of this readme - targets = ["JVM"] - - // Platform used for code analysis. See the "Platforms" section of this readme - platform = "JVM" - - // Property used for manual addition of files to the classpath - // This property does not override the classpath collected automatically but appends to it - classpath = [new File("$buildDir/other.jar")] - - // List of files with module and package documentation - // https://kotlinlang.org/docs/reference/kotlin-doc.html#module-and-package-documentation - includes = ['packages.md', 'extra.md'] - - // List of files or directories containing sample code (referenced with @sample tags) - samples = ['samples/basic.kt', 'samples/advanced.kt'] - - // By default, sourceRoots are taken from Kotlin Plugin, subProjects and kotlinTasks, following roots will be appended to them - // Full form sourceRoot declaration - // Repeat for multiple sourceRoots - sourceRoot { - // Path to a source root - path = "src" - } - - // These tasks will be used to determine source directories and classpath - kotlinTasks { - defaultKotlinTasks() + [':some:otherCompileKotlin', project("another").compileKotlin] - } + // Used when configuring source sets manually for declaring which source sets this one depends on + dependsOn("otherSourceSetName") - // Specifies the location of the project source code on the Web. - // If provided, Dokka generates "source" links for each declaration. - // Repeat for multiple mappings - sourceLink { - // Unix based directory relative path to the root of the project (where you execute gradle respectively). - path = "src/main/kotlin" // or simply "./" - - // URL showing where the source code can be accessed through the web browser - url = "https://github.com/cy6erGn0m/vertx3-lang-kotlin/blob/master/src/main/kotlin" //remove src/main/kotlin if you use "./" above - - // Suffix which is used to append the line number to the URL. Use #L for GitHub - lineSuffix = "#L" - } - - // Used for linking to JDK documentation - jdkVersion = 6 + // Use to include or exclude non public members + includeNonPublic = false - // Disable linking to online kotlin-stdlib documentation - noStdlibLink = false - - // Disable linking to online JDK documentation - noJdkLink = false - - // Allows linking to documentation of the project's dependencies (generated with Javadoc or Dokka) - // Repeat for multiple links - externalDocumentationLink { - // Root URL of the generated documentation to link with. The trailing slash is required! - url = new URL("https://example.com/docs/") - - // If package-list file is located in non-standard location - // packageListUrl = new URL("file:///home/user/localdocs/package-list") - } - - // Allows to customize documentation generation options on a per-package basis - // Repeat for multiple packageOptions - perPackageOption { - prefix = "kotlin" // will match kotlin and all sub-packages of it - // All options are optional, default values are below: + // Do not output deprecated members. Applies globally, can be overridden by packageOptions skipDeprecated = false - reportUndocumented = true // Emit warnings about not documented members - includeNonPublic = false - } - // Suppress a package - perPackageOption { - prefix = "kotlin.internal" // will match kotlin.internal and all sub-packages of it - suppress = true - } - } -} -``` -#### Multiplatform -Since version 0.10.0 dokka supports multiplatform projects. For a general understanding how a multiplatform documentation is generated, please consult the [FAQ](https://github.com/Kotlin/dokka/wiki/faq). -In the multiplatform mode, instead of using the `configuration` block, you should use a `multiplatform` block with inner blocks for each platform. -The `configuration` block's parameters belong to those inner blocks, which can be named arbitrarly, however if you want to use source roots and classpath provided by Kotlin Multiplatform plugin, -they must have the same names as in the Kotlin Multiplatform plugin. See an example below: + // Emit warnings about not documented members. Applies globally, also can be overridden by packageOptions + reportUndocumented = true -Groovy -```groovy -kotlin { // Kotlin Multiplatform plugin configuration - jvm() - js("customName") // Define a js platform named "customName" If you want to generate docs for it, you need to have this name in dokka configuration below -} + // Do not create index pages for empty packages + skipEmptyPackages = true -dokka { - outputDirectory = "$buildDir/dokka" - outputFormat = "html" + // This name will be shown in the final output + displayName = "JVM" - multiplatform { - customName { // The same name as in Kotlin Multiplatform plugin, so the sources are fetched automatically - includes = ['packages.md', 'extra.md'] - samples = ['samples/basic.kt', 'samples/advanced.kt'] - } - - differentName { // Different name, so source roots, classpath and platform must be passed explicitly. - targets = ["JVM"] - platform = "jvm" + // Platform used for code analysis. See the "Platforms" section of this readme + platform = "JVM" + + // Property used for manual addition of files to the classpath + // This property does not override the classpath collected automatically but appends to it + classpath = listOf("$buildDir/other.jar") + + // List of files with module and package documentation + // https://kotlinlang.org/docs/reference/kotlin-doc.html#module-and-package-documentation + includes = listOf("packages.md", "extra.md") + + // List of files or directories containing sample code (referenced with @sample tags) + samples = listOf("samples/basic.kt", "samples/advanced.kt") + + // By default, sourceRoots are taken from Kotlin Plugin and kotlinTasks, following roots will be appended to them + // Repeat for multiple sourceRoots sourceRoot { - path = kotlin.sourceSets.jvmMain.kotlin.srcDirs[0] + // Path to a source root + path = "src" } - sourceRoot { - path = kotlin.sourceSets.commonMain.kotlin.srcDirs[0] + + // These tasks will be used to determine source directories and classpath + kotlinTasks { + defaultKotlinTasks() + listOf( + ":some:otherCompileKotlin", + project("another").tasks.getByName("compileKotlin") + ) + } + + // Specifies the location of the project source code on the Web. + // If provided, Dokka generates "source" links for each declaration. + // Repeat for multiple mappings + sourceLink { + // Unix based directory relative path to the root of the project (where you execute gradle respectively). + path = "src/main/kotlin" // or simply "./" + + // URL showing where the source code can be accessed through the web browser + url = + "https://github.com/cy6erGn0m/vertx3-lang-kotlin/blob/master/src/main/kotlin" //remove src/main/kotlin if you use "./" above + + // Suffix which is used to append the line number to the URL. Use #L for GitHub + lineSuffix = "#L" + } + + // Used for linking to JDK documentation + jdkVersion = 8 + + // Disable linking to online kotlin-stdlib documentation + noStdlibLink = false + + // Disable linking to online JDK documentation + noJdkLink = false + + // Disable linking to online Android documentation (only applicable for Android projects) + noAndroidSdkLink = false + + // Allows linking to documentation of the project"s dependencies (generated with Javadoc or Dokka) + // Repeat for multiple links + externalDocumentationLink { + // Root URL of the generated documentation to link with. The trailing slash is required! + url = URL("https://example.com/docs/") + + // If package-list file is located in non-standard location + // packageListUrl = URL("file:///home/user/localdocs/package-list") + } + + // Allows to customize documentation generation options on a per-package basis + // Repeat for multiple packageOptions + perPackageOption { + prefix = "kotlin" // will match kotlin and all sub-packages of it + // All options are optional, default values are below: + skipDeprecated = false + reportUndocumented = true // Emit warnings about not documented members + includeNonPublic = false + } + // Suppress a package + perPackageOption { + prefix = "kotlin.internal" // will match kotlin.internal and all sub-packages of it + suppress = true } } } -} ``` +#### Multiplatform +Dokka supports single-platform and multi-platform projects using source sets abstraction. For most mutli-platform projects +you should assume that dokka's source sets correspond to Kotlin plugin's source sets. +Source sets can be named arbitrarily, however in order for autoconfiguration (extraction of source roots and classpath from Kotlin plugin) to work, +they must have the same names as source sets in the Kotlin Multiplatform plugin. +See an example below: + Kotlin ```kotlin kotlin { // Kotlin Multiplatform plugin configuration @@ -223,18 +282,17 @@ kotlin { // Kotlin Multiplatform plugin configuration js("customName") } -val dokka by getting(DokkaTask::class) { +dokkaHtml { outputDirectory = "$buildDir/dokka" - outputFormat = "html" - multiplatform { - val customName by creating { // The same name as in Kotlin Multiplatform plugin, so the sources are fetched automatically + dokkaSourceSets { + val customNameMain by creating { // The same name as in Kotlin Multiplatform plugin, so the sources are fetched automatically includes = listOf("packages.md", "extra.md") samples = listOf("samples/basic.kt", "samples/advanced.kt") } register("differentName") { // Different name, so source roots must be passed explicitly - targets = listOf("JVM") + displayName = "JVM" platform = "jvm" sourceRoot { path = kotlin.sourceSets.getByName("jvmMain").kotlin.srcDirs.first().toString() @@ -247,146 +305,124 @@ val dokka by getting(DokkaTask::class) { } ``` -For convenience, there is also a reserved block called `global`, which is a top-level configuration of `perPackageOptions`, `externalDocumentationLinks`, and `sourceLinks` shared by every platform. Eg. - +Groovy ```groovy -dokka { - multiplatform { - global { // perPackageOptions, sourceLinks and externalDocumentationLinks from here will be copied to every other platform (jvm and js in eg.) - perPackageOption { - prefix = "com.somePackage" - suppress = true - } - perPackageOption { // You can repeat this block for multiple perPackageOptions - prefix = "kotlin" - skipDeprecated = false - reportUndocumented = true - includeNonPublic = false - } - sourceLink { - path = "src/main/kotlin" - url = "https://github.com/cy6erGn0m/vertx3-lang-kotlin/blob/master/src/main/kotlin" - lineSuffix = "#L" - } - externalDocumentationLink { - url = new URL("https://example.com/docs/") - } - } - js {} - jvm {} - } +kotlin { // Kotlin Multiplatform plugin configuration + jvm() + js("customName") // Define a js platform named "customName" If you want to generate docs for it, you need to have this name followed by "Main" in the dokka configuration below + + // Note: Kotlin plugin creates `main` and `test` source sets for the platforms above automatically, eg. in this project there will be: + // `jvmMain`, `jvmTest`, `customNameMain` and `customNameTest` + // Those names can be used in the dokka tasks, as shown below: } -``` -The parameters from the `global` block are appended to all the other platform configurations (in the example - `js` and `jvm`) and cannot be overriden. - - -Note that `javadoc` output format cannot be used with multiplatform. - -To generate the documentation, use the `dokka` Gradle task: - -```bash -./gradlew dokka -``` -More dokka tasks can be added to a project like this: +dokkaHtml { + outputDirectory = "$buildDir/dokka" -```groovy -task dokkaMarkdown(type: org.jetbrains.dokka.gradle.DokkaTask) { - outputFormat = 'markdown' - outputDirectory = "$buildDir/markdown" + dokkaSourceSets { + customNameMain { // The same name as Kotlin Multiplatform plugin source set for `customName` platform, so the sources are fetched automatically + includes = ['packages.md', 'extra.md'] + samples = ['samples/basic.kt', 'samples/advanced.kt'] + } + + differentName { // Different name, so source roots, classpath and platform must be passed explicitly. + displayName = "JVM" + platform = "jvm" + sourceRoot { + path = kotlin.sourceSets.jvmMain.kotlin.srcDirs[0] + } + sourceRoot { + path = kotlin.sourceSets.commonMain.kotlin.srcDirs[0] + } + } + } } ``` -Please see the [Dokka Gradle example project](https://github.com/JetBrains/kotlin-examples/tree/master/gradle/dokka-gradle-example) for an example. +If you want to share the configuration between source sets, you can use Gradle's `configureEach` -#### Dokka Runtime -If you are using Gradle plugin and you want to use a custom version of dokka, you can do it by setting `dokkaRuntime` configuration: +#### Applying plugins +Dokka plugin creates Gradle configuration for each output format in the form of `dokka${format}Plugin`: -```groovy -buildscript { - ... +```kotlin +dependencies { + dokkaHtmlPlugin("org.jetbrains.dokka:kotlin-as-java-plugin:1.4-M3") } +``` -apply plugin: 'org.jetbrains.dokka' +You can also create a custom dokka task and add plugins directly inside: -repositories { - jcenter() +```kotlin +val customDokkaTask by creating(DokkaTask::class) { + dependencies { + plugins("org.jetbrains.dokka:kotlin-as-java-plugin:1.4-M3") + } } +``` -dependencies { - dokkaRuntime "org.jetbrains.dokka:dokka-fatjar:0.10.1" -} +Please note that `dokkaJavadoc` task will properly document only single `jvm` source set -dokka { - outputFormat = 'html' - outputDirectory = "$buildDir/dokkaHtml" -} +To generate the documentation, use the appropriate `dokka${format}` Gradle task: +```bash +./gradlew dokkaHtml ``` -To use your Fat Jar, just set the path to it: - ```groovy - buildscript { - ... - } - - apply plugin: 'org.jetbrains.dokka' - - repositories { - jcenter() - } - - dependencies { - dokkaRuntime files("/path/to/fatjar/dokka-fatjar-0.10.1.jar") - } - - dokka { - outputFormat = 'html' - outputDirectory = "$buildDir/dokkaHtml" - } - - ``` +Please see the [Dokka Gradle example project](https://github.com/JetBrains/kotlin-examples/tree/master/gradle/dokka-gradle-example) for an example. #### FAQ If you encounter any problems, please see the [FAQ](https://github.com/Kotlin/dokka/wiki/faq). #### Android -Since version 0.10.0 the separate Android plugin is merged with the default one. -Just make sure you apply the plugin after -`com.android.library` and `kotlin-android`. +Make sure you apply dokka after `com.android.library` and `kotlin-android`. -```groovy +```kotlin buildscript { repositories { jcenter() + maven("https://dl.bintray.com/kotlin/kotlin-eap") } dependencies { - classpath "org.jetbrains.dokka:dokka-gradle-plugin:${dokka_version}" + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlin_version}") + classpath("org.jetbrains.dokka:dokka-gradle-plugin:${dokka_version}") } } repositories { jcenter() + maven("https://dl.bintray.com/kotlin/kotlin-eap") } -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' -apply plugin: 'org.jetbrains.dokka' +apply(plugin= "com.android.library") +apply(plugin= "kotlin-android") +apply(plugin= "org.jetbrains.dokka") ``` -There is also a `noAndroidSdkLink` configuration parameter that works similar to `noJdkLink` and `noStdlibLink` -By default the variant documented by dokka is the first release variant encountered. -You can override that by setting the `androidVariants` property inside the `configuration` (or specific platform) block: -```groovy -dokka { - configuration { - androidVariants = ["debug", "release"] +```kotlin +dokkaHtml { + dokkaSourceSets { + create("main") { + noAndroidSdkLink = true + } } } ``` +#### Multi-module projects +For documenting Gradle multi-module projects, you can use `dokka${format}Multimodule` tasks. + +```kotlin +tasks.dokkaHtmlMultimodule { + outputDirectory = "$buildDir/multimodule" + documentationFileName = "README.md" +} +``` + +`DokkaMultimodule` depends on all dokka tasks in the subprojects, runs them, and creates a toplevel page (based on the `documentationFile`) +with links to all generated (sub)documentations + ### Using the Maven plugin -The Maven plugin does not support multiplatform projects. +The Maven plugin does not support multi-platform projects. The Maven plugin is available in JCenter. You need to add the JCenter repository to the list of plugin repositories if it's not there: @@ -397,6 +433,10 @@ The Maven plugin is available in JCenter. You need to add the JCenter repository <name>JCenter</name> <url>https://jcenter.bintray.com/</url> </pluginRepository> + <pluginRepository> + <id>kotlin-eap</id> + <url>https://dl.bintray.com/kotlin/kotlin-eap/</url> + </pluginRepository> </pluginRepositories> ``` @@ -422,9 +462,9 @@ By default files will be generated in `target/dokka`. The following goals are provided by the plugin: - * `dokka:dokka` - generate HTML documentation in Dokka format (showing declarations in Kotlin syntax); - * `dokka:javadoc` - generate HTML documentation in JavaDoc format (showing declarations in Java syntax); - * `dokka:javadocJar` - generate a .jar file with JavaDoc format documentation. + * `dokka:dokka` - generate HTML documentation in Dokka format (showing declarations in Kotlin syntax) + * `dokka:javadoc` - generate HTML documentation in Javadoc format (showing declarations in Java syntax) + * `dokka:javadocJar` - generate a .jar file with Javadoc format documentation The available configuration options are shown below: @@ -447,9 +487,8 @@ The available configuration options are shown below: <skip>false</skip> <!-- Default: ${project.artifactId} --> - <moduleName>data</moduleName> - <!-- See list of possible formats below --> - <outputFormat>html</outputFormat> + <moduleDisplayName>data</moduleDisplayName> + <!-- Default: ${project.basedir}/target/dokka --> <outputDir>some/out/dir</outputDir> @@ -535,107 +574,133 @@ The available configuration options are shown below: <includeNonPublic>false</includeNonPublic> </packageOptions> </perPackageOptions> + + <!-- Allows to use any dokka plugin, eg. GFM format --> + <dokkaPlugins> + <plugin> + <groupId>org.jetbrains.dokka</groupId> + <artifactId>gfm-plugin</artifactId> + <version>${dokka.version}</version> + </plugin> + </dokkaPlugins> </configuration> </plugin> ``` -Please see the [Dokka Maven example project](https://github.com/JetBrains/kotlin-examples/tree/master/maven/dokka-maven-example) for an example. - -[Output formats](#output_formats) - -### Using the Ant task - -The Ant task definition is also contained in the dokka-fatjar.jar referenced above. Here's an example usage: +#### Applying plugins +You can add plugins inside the `dokkaPlugins` block: ```xml -<project name="Dokka" default="document"> - <typedef resource="dokka-antlib.xml" classpath="dokka-fatjar.jar"/> - - <target name="document"> - <dokka format="html" outputDir="doc"/> - </target> -</project> +<plugin> + <groupId>org.jetbrains.dokka</groupId> + <artifactId>dokka-maven-plugin</artifactId> + <version>${dokka.version}</version> + <executions> + <execution> + <phase>pre-site</phase> + <goals> + <goal>dokka</goal> + </goals> + </execution> + </executions> + <configuration> + <dokkaPlugins> + <plugin> + <groupId>org.jetbrains.dokka</groupId> + <artifactId>kotlin-as-java-plugin</artifactId> + <version>${dokka.version}</version> + </plugin> + </dokkaPlugins> + </configuration> +</plugin> ``` -The Ant task supports the following attributes: - - * `outputDir` - the output directory where the documentation is generated - * `format` - the output format (see the list of supported formats below) - * `cacheRoot` - Use `default` or set to custom path to cache directory to enable package-list caching. When set to `default`, caches stored in $USER_HOME/.cache/dokka - -Inside the `dokka` tag you can create another tags named `<passconfig/>` that support the following attributes: - - * `classpath` - list of directories or .jar files to include in the classpath (used for resolving references) - * `samples` - list of directories containing sample code (documentation for those directories is not generated but declarations from them can be referenced using the `@sample` tag) - * `moduleName` - the name of the module being documented (used as the root directory of the generated documentation) - * `include` - names of files containing the documentation for the module and individual packages - * `skipDeprecated` - if set, deprecated elements are not included in the generated documentation - * `jdkVersion` - version for linking to JDK - * `analysisPlatform="jvm"` - platform used for analysing sourceRoots, see the [platforms](#platforms) section - * `<sourceRoot path="src" />` - source root - * `<packageOptions prefix="kotlin" includeNonPublic="false" reportUndocumented="true" skipDeprecated="false"/>` - - Per package options for package `kotlin` and sub-packages of it - * `noStdlibLink` - disable linking to online kotlin-stdlib documentation - * `noJdkLink` - disable linking to online JDK documentation - * `<externalDocumentationLink url="https://example.com/docs/" packageListUrl="file:///home/user/localdocs/package-list"/>` - - linking to external documentation, packageListUrl should be used if package-list located not in standard location - * `<target value="JVM"/>` - see the [platforms](#platforms) section - +Please see the [Dokka Maven example project](https://github.com/JetBrains/kotlin-examples/tree/master/maven/dokka-maven-example) for an example. ### Using the Command Line -To run Dokka from the command line, download the [Dokka jar](https://github.com/Kotlin/dokka/releases/download/0.10.1/dokka-fatjar-0.10.1.jar). +To run Dokka from the command line, download the [Dokka CLI runner](https://github.com/Kotlin/dokka/releases/download/1.4-M3/dokka-cli.jar). To generate documentation, run the following command: - - java -jar dokka-fatjar.jar <arguments> - +``` +java -jar dokka-cli.jar <arguments> +``` Dokka supports the following command line arguments: - * `-output` - the output directory where the documentation is generated - * `-format` - the [output format](#output-formats): - * `-cacheRoot` - use `default` or set to custom path to cache directory to enable package-list caching. When set to `default`, caches stored in $USER_HOME/.cache/dokka - * `-pass` - (repeatable) - configuration for single analyser pass. Following this argument, you can pass other arguments: - * `-src` - (repeatable) - source file or directory (allows many paths separated by the system path separator) - * `-classpath` - (repeatable) - directory or .jar file to include in the classpath (used for resolving references) - * `-sample` - (repeatable) - directory containing a sample code (documentation for those directories is not generated but declarations from them can be referenced using the `@sample` tag) - * `-module` - the name of the module being documented (used as the root directory of the generated documentation) - * `-include` - (repeatable) - names of files containing the documentation for the module and individual packages + * `-outputDir` - the output directory where the documentation is generated + * `-cacheRoot` - cache directory to enable package-list caching + * `-pluginsClasspath` - artifacts with dokka plugins, separated by `;`. At least dokka base and all its dependencies must be added there + * `-offlineMode` - do not resolve package-lists online + * `-failOnWarning` - throw an exception instead of a warning + * `-globalPackageOptions` - per package options added to all source sets + * `-globalLinks` - external documentation links added to all source sets + * `-globalSrcLink` - source links added to all source sets + * `-sourceSet` - (repeatable) - configuration for a single source set. Following this argument, you can pass other arguments: + * `-moduleName` - (required) - module name used as a part of source set ID when declaring dependent source sets + * `-moduleDisplayName` - displayed module name + * `-sourceSetName` - source set name as a part of source set ID when declaring dependent source sets + * `-displayName` - source set name displayed in the generated documentation + * `-src` - list of source files or directories separated by `;` + * `-classpath` - list of directories or .jar files to include in the classpath (used for resolving references) separated by `;` + * `-samples` - list of directories containing sample code (documentation for those directories is not generated but declarations from them can be referenced using the `@sample` tag) separated by `;` + * `-includes` - list of files containing the documentation for the module and individual packages separated by `;` + * `-includeNonPublic` - include protected and private code * `-skipDeprecated` - if set, deprecated elements are not included in the generated documentation * `-reportUndocumented` - warn about undocumented members * `-skipEmptyPackages` - do not create index pages for empty packages - * `-packageOptions` - list of package options in format `prefix,-deprecated,-privateApi,+warnUndocumented;prefix, ...` - * `-links` - external documentation links in format `url^packageListUrl^^url2...` - * `-srcLink` - (repeatable) - mapping between a source directory and a Web site for browsing the code in format `<path>=<url>[#lineSuffix]` + * `-packageOptions` - list of package options in format `prefix,-deprecated,-privateApi,+reportUndocumented;prefix, ...`, separated by `;` + * `-links` - list of external documentation links in format `url^packageListUrl^^url2...`, separated by `;` + * `-srcLink` - mapping between a source directory and a Web site for browsing the code in format `<path>=<url>[#lineSuffix]` * `-noStdlibLink` - disable linking to online kotlin-stdlib documentation * `-noJdkLink` - disable linking to online JDK documentation * `-jdkVersion` - version of JDK to use for linking to JDK JavaDoc * `-analysisPlatform` - platform used for analysis, see the [Platforms](#platforms) section - * `-target` - (repeatable) - generation target + * `-dependentSourceSets` - list of dependent source sets in format `moduleName/sourceSetName`, separated by `;` +You can also use a JSON file with dokka configuration: + ``` + java -jar <dokka_cli.jar> <path_to_config.json> + ``` ### Output formats<a name="output_formats"></a> + Dokka documents Java classes as seen in Kotlin by default, with javadoc format being the only exception. - * `html` - minimalistic html format used by default, Java classes are translated to Kotlin - * `javadoc` - looks like normal Javadoc, Kotlin classes are translated to Java - * `html-as-java` - looks like `html`, but Kotlin classes are translated to Java - * `markdown` - markdown structured as `html`, Java classes are translated to Kotlin - * `gfm` - GitHub flavored markdown - * `jekyll` - Jekyll compatible markdown - * `kotlin-website*` - internal format used for documentation on [kotlinlang.org](https://kotlinlang.org) - -### Platforms<a name="platforms"></a> + * `html` - HTML format used by default + * `javadoc` - looks like JDK's Javadoc, Kotlin classes are translated to Java + * `gfm` - GitHub flavored markdown + * `jekyll` - Jekyll compatible markdown -Dokka can annotate elements with special `platform` block with platform requirements -Example result and usage can be found on [kotlinlang.org](https://kotlinlang.org/api/latest/jvm/stdlib/) +If you want to generate the documentation as seen from Java perspective, you can add the `kotlin-as-java` plugin +to the dokka plugins classpath, eg. in Gradle: -Each multiplatform closure has two properties: `platform` and `targets`. If you use autoconfiguration, those are filled automatically. +```kotlin +dependencies{ + implementation("...") + dokkaGfmPlugin("org.jetbrains.dokka:kotlin-as-java-plugin:${dokka-version}") +} +``` -`targets` property is a list of platform names that will be shown in the final result. Note that the value of this property -doesn't affect analysis of source code, it just changes the result. You can think of this as a `name` property +### Platforms<a name="platforms"></a> -`platform` property is used for the analysis of source roots. Available values are: +Each dokka source set is analyzed for a specific platform. The platform should be extracted automatically from the Kotlin plugin. +In case of a manual source set configuration, you have to select one of the following: * `jvm` * `js` * `native` * `common` +## Building dokka + +Dokka is built with Gradle. To build it, use `./gradlew build`. +Alternatively, open the project directory in IntelliJ IDEA and use the IDE to build and run dokka. + +Here's how to import and configure Dokka in IntelliJ IDEA 2019.3: + * Select "Open" from the IDEA welcome screen, or File > Open if a project is + already open +* Select the directory with your clone of Dokka + * Note: IDEA may have an error after the project is initally opened; it is OK + to ignore this as the next step will address this error +* After IDEA opens the project, select File > New > Module from existing sources + and select the `build.gradle` file from the root directory of your Dokka clone +* After Dokka is loaded into IDEA, open the Gradle tool window (View > Tool + Windows > Gradle) and click on the top left "Refresh all Gradle projects" + button diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 777d45dd..00000000 --- a/build.gradle +++ /dev/null @@ -1,138 +0,0 @@ -allprojects { - ext { - if (!project.findProperty("dokka_version")) { - final String buildNumber = System.getenv("BUILD_NUMBER") - dokka_version = dokka_version_base + (buildNumber == null || System.getenv("FORCE_SNAPSHOT") != null ? "-SNAPSHOT" : "-$buildNumber") - } - } - - if (project == rootProject) { - println "Publication version: $dokka_version" - } - - group 'org.jetbrains.dokka' - version dokka_version - - def repo = { - artifactPattern("https://teamcity.jetbrains.com/guestAuth/repository/download/Kotlin_dev_CompilerAllPlugins/[revision]/internal/[module](.[ext])") - artifactPattern("https://teamcity.jetbrains.com/guestAuth/repository/download/Kotlin_dev_CompilerAllPlugins/[revision]/[module](.[ext])") - artifactPattern("https://teamcity.jetbrains.com/guestAuth/repository/download/IntelliJMarkdownParser_Build/[revision]/([module]_[ext]/)[module](.[ext])") - } - - buildscript { - repositories { - mavenCentral() - maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } - maven { url "https://dl.bintray.com/kotlin/kotlin-dev" } - maven { url "https://plugins.gradle.org/m2/" } - ivy(repo) - } - dependencies { - classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4' - classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.1' - - classpath "com.gradle.publish:plugin-publish-plugin:0.9.10" - } - } - - repositories { - jcenter() - mavenCentral() - mavenLocal() - maven { url 'https://kotlin.bintray.com/kotlin-plugin' } - maven { url 'https://www.jetbrains.com/intellij-repository/releases' } - maven { url "https://dl.bintray.com/jetbrains/markdown" } - maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } - maven { url "https://dl.bintray.com/kotlin/kotlin-dev" } - maven { url "https://teamcity.jetbrains.com/guestAuth/repository/download/Kotlin_dev_CompilerAllPlugins/$bundled_kotlin_compiler_version/maven" } - ivy(repo) - maven { url "https://kotlin.bintray.com/kotlinx" } - maven { url "https://dl.bintray.com/kotlin/kotlinx" } - maven { url "https://dl.bintray.com/orangy/maven" } // TODO: remove this repository when kotlinx.cli is available in maven - } -} - - -def bintrayPublication(project, List<String> _publications) { - configure(project, { - apply plugin: 'com.jfrog.bintray' - - bintray { - user = System.getenv('BINTRAY_USER') - key = System.getenv('BINTRAY_KEY') - - pkg { - repo = dokka_publication_channel - name = 'dokka' - userOrg = 'kotlin' - desc = 'Dokka, the Kotlin documentation tool' - vcsUrl = 'https://github.com/kotlin/dokka.git' - licenses = ['Apache-2.0'] - version { - name = dokka_version - } - } - - publications = _publications - } - }) -} - - -configurations { - ideaIC - intellijCore -} - -repositories { - maven { url 'https://kotlin.bintray.com/kotlin-plugin' } - maven { url 'https://www.jetbrains.com/intellij-repository/snapshots' } - maven { url 'https://www.jetbrains.com/intellij-repository/releases' } -} - -dependencies { - intellijCore "com.jetbrains.intellij.idea:intellij-core:$idea_version" - ideaIC "com.jetbrains.intellij.idea:ideaIC:$idea_version" -} - -def intellijCoreAnalysis() { - return zipTree(configurations.intellijCore.singleFile).matching ({ - include("intellij-core-analysis.jar") - }) -} - -def ideaRT() { - return zipTree(project.configurations.ideaIC.singleFile).matching ({ - include("lib/idea_rt.jar") - }) -} - -def repoLocation = uri(file("$buildDir/dist-maven")) - -configurations { - kotlin_plugin_full -} - - -allprojects { - - task publishToDistMaven { - group "publishing" - description "Publishes all Maven publications to Maven repository 'distMaven'." - dependsOn tasks.withType(PublishToMavenRepository).matching { - it.repository == publishing.repositories.distMaven - } - } - - plugins.withType(MavenPublishPlugin) { - publishing { - repositories { - maven { - name 'distMaven' - url repoLocation - } - } - } - - } -}
\ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 00000000..cae9ecca --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,59 @@ +import org.jetbrains.ValidatePublications +import org.jetbrains.configureDokkaVersion +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + kotlin("jvm") apply false + id("java") +} + +val dokka_version: String by project + +allprojects { + configureDokkaVersion() + + group = "org.jetbrains.dokka" + version = dokka_version + + val language_version: String by project + tasks.withType(KotlinCompile::class).all { + kotlinOptions { + freeCompilerArgs += "-Xjsr305=strict -Xskip-metadata-version-check -Xopt-in=kotlin.RequiresOptIn." + languageVersion = language_version + apiVersion = language_version + jvmTarget = "1.8" + } + } + + repositories { + jcenter() + mavenCentral() + maven(url = "https://dl.bintray.com/kotlin/kotlin-eap") + maven(url = "https://dl.bintray.com/kotlin/kotlin-dev") + } +} + +subprojects { + apply { + plugin("org.jetbrains.kotlin.jvm") + plugin("java") + } + + // Gradle metadata + java { + @Suppress("UnstableApiUsage") + withSourcesJar() + targetCompatibility = JavaVersion.VERSION_1_8 + } +} + +// Workaround for https://github.com/bintray/gradle-bintray-plugin/issues/267 +// Manually disable bintray tasks added to the root project +tasks.whenTaskAdded { + if ("bintray" in name) { + enabled = false + } +} + +println("Publication version: $dokka_version") +tasks.register<ValidatePublications>("validatePublications") diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle deleted file mode 100644 index b62b4150..00000000 --- a/buildSrc/build.gradle +++ /dev/null @@ -1,10 +0,0 @@ -apply plugin: 'groovy' -repositories { - mavenCentral() - jcenter() - maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } - maven { url "https://dl.bintray.com/kotlin/kotlin-dev" } -} -dependencies { - compile 'com.github.jengelman.gradle.plugins:shadow:2.0.4' -} diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts new file mode 100644 index 00000000..7a7b8f6a --- /dev/null +++ b/buildSrc/build.gradle.kts @@ -0,0 +1,13 @@ +plugins { + `kotlin-dsl` +} + +repositories { + mavenCentral() + jcenter() +} + +dependencies { + implementation("com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4") + implementation("com.github.jengelman.gradle.plugins:shadow:2.0.4") +} diff --git a/buildSrc/src/main/groovy/org/jetbrains/CorrectShadowPublishing.groovy b/buildSrc/src/main/groovy/org/jetbrains/CorrectShadowPublishing.groovy deleted file mode 100644 index 62cc3d3c..00000000 --- a/buildSrc/src/main/groovy/org/jetbrains/CorrectShadowPublishing.groovy +++ /dev/null @@ -1,40 +0,0 @@ -package org.jetbrains - -import org.gradle.api.Project -import org.gradle.api.artifacts.ModuleVersionIdentifier -import org.gradle.api.artifacts.ProjectDependency -import org.gradle.api.artifacts.SelfResolvingDependency -import org.gradle.api.internal.artifacts.ivyservice.projectmodule.ProjectDependencyPublicationResolver -import org.gradle.api.publish.maven.MavenPom -import org.gradle.api.publish.maven.MavenPublication - -//https://github.com/johnrengelman/shadow/issues/334 -static void configure(MavenPublication publication, Project project) { - publication.artifact(project.tasks.shadowJar) - - - publication.pom { MavenPom pom -> - pom.withXml { xml -> - def dependenciesNode = xml.asNode().appendNode('dependencies') - - project.configurations.shadow.allDependencies.each { - if (it instanceof ProjectDependency) { - final ProjectDependencyPublicationResolver projectDependencyResolver = project.gradle.services.get(ProjectDependencyPublicationResolver) - final ModuleVersionIdentifier identifier = projectDependencyResolver.resolve(ModuleVersionIdentifier, it) - addDependency(dependenciesNode, identifier) - } else if (!(it instanceof SelfResolvingDependency)) { - addDependency(dependenciesNode, it) - } - - } - } - } -} - -private static void addDependency(Node dependenciesNode, dep) { - def dependencyNode = dependenciesNode.appendNode('dependency') - dependencyNode.appendNode('groupId', dep.group) - dependencyNode.appendNode('artifactId', dep.name) - dependencyNode.appendNode('version', dep.version) - dependencyNode.appendNode('scope', 'compile') -} diff --git a/buildSrc/src/main/groovy/org/jetbrains/CrossPlatformExec.groovy b/buildSrc/src/main/groovy/org/jetbrains/CrossPlatformExec.groovy deleted file mode 100644 index d3973a8a..00000000 --- a/buildSrc/src/main/groovy/org/jetbrains/CrossPlatformExec.groovy +++ /dev/null @@ -1,84 +0,0 @@ -package org.jetbrains - -import org.gradle.api.tasks.AbstractExecTask -import org.gradle.api.tasks.TaskAction -import org.gradle.internal.os.OperatingSystem - -import java.nio.file.Files -import java.nio.file.Path -import java.nio.file.Paths - -class CrossPlatformExec extends AbstractExecTask { - private static final def windowsExtensions = ['bat', 'cmd', 'exe']; - private static final def unixExtensions = [null, 'sh']; - - private boolean windows; - - public CrossPlatformExec() { - super(CrossPlatformExec.class); - windows = OperatingSystem.current().windows; - } - - @Override - @TaskAction - protected void exec() { - List<String> commandLine = this.getCommandLine(); - - if (!commandLine.isEmpty()) { - commandLine[0] = findCommand(commandLine[0], windows); - } - - if (windows) { - if (!commandLine.isEmpty() && commandLine[0]) { - commandLine - } - commandLine.add(0, '/c'); - commandLine.add(0, 'cmd'); - } - - this.setCommandLine(commandLine); - - super.exec(); - } - - private static String findCommand(String command, boolean windows) { - command = normalizeCommandPaths(command); - def extensions = windows ? windowsExtensions : unixExtensions; - - return extensions.findResult(command) { extension -> - Path commandFile - if (extension) { - commandFile = Paths.get(command + '.' + extension); - } else { - commandFile = Paths.get(command); - } - - return resolveCommandFromFile(commandFile, windows); - }; - } - - private static String resolveCommandFromFile(Path commandFile, boolean windows) { - if (!Files.isExecutable(commandFile)) { - return null; - } - - return commandFile.toAbsolutePath().normalize(); - } - - private static String normalizeCommandPaths(String command) { - // need to escape backslash so it works with regex - String backslashSeparator = '\\\\'; - - String forwardSlashSeparator = '/'; - - // escape separator if it's a backslash - char backslash = '\\'; - String separator = File.separatorChar == backslash ? backslashSeparator : File.separator - - return command - // first replace all of the backslashes with forward slashes - .replaceAll(backslashSeparator, forwardSlashSeparator) - // then replace all forward slashes with whatever the separator actually is - .replaceAll(forwardSlashSeparator, separator); - } -}
\ No newline at end of file diff --git a/buildSrc/src/main/groovy/org/jetbrains/DependenciesVersionGetter.groovy b/buildSrc/src/main/groovy/org/jetbrains/DependenciesVersionGetter.groovy deleted file mode 100644 index 194f11af..00000000 --- a/buildSrc/src/main/groovy/org/jetbrains/DependenciesVersionGetter.groovy +++ /dev/null @@ -1,14 +0,0 @@ -package org.jetbrains - -import org.gradle.api.Project - -class DependenciesVersionGetter { - static Properties getVersions(Project project, String artifactVersionSelector) { - def dep = project.dependencies.create(group: 'teamcity', name: 'dependencies', version: artifactVersionSelector, ext: 'properties') - def file = project.configurations.detachedConfiguration(dep).resolve().first() - - def prop = new Properties() - prop.load(new FileReader(file)) - return prop - } -} diff --git a/buildSrc/src/main/groovy/org/jetbrains/PluginXmlTransformer.groovy b/buildSrc/src/main/groovy/org/jetbrains/PluginXmlTransformer.groovy deleted file mode 100644 index e711388f..00000000 --- a/buildSrc/src/main/groovy/org/jetbrains/PluginXmlTransformer.groovy +++ /dev/null @@ -1,71 +0,0 @@ -package org.jetbrains - -import com.github.jengelman.gradle.plugins.shadow.relocation.RelocateClassContext -import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator -import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer -import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext -import groovy.xml.XmlUtil -import org.gradle.api.file.FileTreeElement -import shadow.org.apache.tools.zip.ZipEntry -import shadow.org.apache.tools.zip.ZipOutputStream - -public class PluginXmlTransformer implements Transformer { - private Map<String, Node> transformedPluginXmlFiles = new HashMap<>(); - - @Override - boolean canTransformResource(FileTreeElement fileTreeElement) { - return fileTreeElement.relativePath.segments.contains("META-INF") && fileTreeElement.name.endsWith(".xml") - } - - @Override - void transform(TransformerContext context) { - def path = context.path - def inputStream = context.is - System.out.println(path) - Node node = new XmlParser().parse(inputStream) - relocateXml(node, context) - transformedPluginXmlFiles.put(path, node) - } - - @Override - boolean hasTransformedResource() { - return !transformedPluginXmlFiles.isEmpty() - } - - @Override - void modifyOutputStream(ZipOutputStream zipOutputStream) { - for (Map.Entry<String, Node> entry : transformedPluginXmlFiles.entrySet()) { - zipOutputStream.putNextEntry(new ZipEntry(entry.key)) - XmlUtil.serialize(entry.value, zipOutputStream) - } - } - - private static void relocateXml(Node node, TransformerContext context) { - Map attributes = node.attributes() - RelocateClassContext relocateClassContext = new RelocateClassContext() - relocateClassContext.stats = context.stats - for (Map.Entry entry : attributes.entrySet()) { - relocateClassContext.setClassName((String) entry.getValue()) - entry.setValue(relocateClassName(relocateClassContext, context)) - } - List<String> localText = node.localText() - if (localText.size() == 1) { - relocateClassContext.setClassName(localText[0]) - node.setValue(relocateClassName(relocateClassContext, context)) - } - node.children().each { - if (it instanceof Node) { - relocateXml((Node) it, context) - } - } - } - - private static String relocateClassName(RelocateClassContext relocateContext, TransformerContext context) { - for (Relocator relocator : context.relocators) { - if (relocator.canRelocateClass(relocateContext)) { - return relocator.relocateClass(relocateContext) - } - } - return relocateContext.className - } -}
\ No newline at end of file diff --git a/buildSrc/src/main/kotlin/org/jetbrains/CrossPlatformExec.kt b/buildSrc/src/main/kotlin/org/jetbrains/CrossPlatformExec.kt new file mode 100644 index 00000000..e02bdd61 --- /dev/null +++ b/buildSrc/src/main/kotlin/org/jetbrains/CrossPlatformExec.kt @@ -0,0 +1,63 @@ +package org.jetbrains + +import org.gradle.api.tasks.AbstractExecTask +import org.gradle.internal.os.OperatingSystem +import java.io.File +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.Paths + +open class CrossPlatformExec : AbstractExecTask<CrossPlatformExec>(CrossPlatformExec::class.java) { + private val windowsExtensions = listOf(".bat", ".cmd", ".exe") + private val unixExtensions = listOf("", ".sh") + + private val isWindows = OperatingSystem.current().isWindows + + override fun exec() { + val commandLine: MutableList<String> = this.commandLine + + if (commandLine.isNotEmpty()) { + commandLine[0] = findCommand(commandLine[0]) + } + + if (isWindows && commandLine.isNotEmpty() && commandLine[0].isNotBlank()) { + this.commandLine = listOf("cmd", "/c") + commandLine + } else { + this.commandLine = commandLine + } + + super.exec() + } + + private fun findCommand(command: String): String { + val command = normalizeCommandPaths(command) + val extensions = if (isWindows) windowsExtensions else unixExtensions + + return extensions.map { extension -> + resolveCommandFromFile(Paths.get("$command$extension")) + }.firstOrNull { it.isNotBlank() } ?: command + } + + private fun resolveCommandFromFile(commandFile: Path) = + if (!Files.isExecutable(commandFile)) { + "" + } else { + commandFile.toAbsolutePath().normalize().toString() + } + + + private fun normalizeCommandPaths(command: String): String { + // need to escape backslash so it works with regex + val backslashSeparator = "\\" + val forwardSlashSeparator = "/" + + // get the actual separator + val separator = if (File.separatorChar == '\\') backslashSeparator else File.separator + + return command + // first replace all of the backslashes with forward slashes + .replace(backslashSeparator, forwardSlashSeparator) + // then replace all forward slashes with whatever the separator actually is + .replace(forwardSlashSeparator, separator) + } +}
\ No newline at end of file diff --git a/buildSrc/src/main/kotlin/org/jetbrains/DokkaVersion.kt b/buildSrc/src/main/kotlin/org/jetbrains/DokkaVersion.kt new file mode 100644 index 00000000..2a5c21a7 --- /dev/null +++ b/buildSrc/src/main/kotlin/org/jetbrains/DokkaVersion.kt @@ -0,0 +1,26 @@ +package org.jetbrains + +import org.gradle.api.Project +import org.gradle.kotlin.dsl.extra +import org.gradle.kotlin.dsl.provideDelegate + +fun Project.configureDokkaVersion(): String { + var dokka_version: String? by this.extra + if (dokka_version == null) { + val dokka_version_base: String by this + dokka_version = dokkaVersionFromBase(dokka_version_base) + } + return checkNotNull(dokka_version) +} + +private fun dokkaVersionFromBase(baseVersion: String): String { + val buildNumber = System.getenv("BUILD_NUMBER") + val forceSnapshot = System.getenv("FORCE_SNAPSHOT") != null + if (forceSnapshot || buildNumber == null) { + return "$baseVersion-SNAPSHOT" + } + return "$baseVersion-$buildNumber" +} + +val Project.dokkaVersion: String + get() = configureDokkaVersion() diff --git a/buildSrc/src/main/kotlin/org/jetbrains/SetupMaven.kt b/buildSrc/src/main/kotlin/org/jetbrains/SetupMaven.kt new file mode 100644 index 00000000..4ef26a73 --- /dev/null +++ b/buildSrc/src/main/kotlin/org/jetbrains/SetupMaven.kt @@ -0,0 +1,48 @@ +package org.jetbrains + +import org.gradle.api.artifacts.Configuration +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.Sync +import org.gradle.kotlin.dsl.creating +import org.gradle.kotlin.dsl.dependencies +import org.gradle.kotlin.dsl.getValue +import java.io.File + +@Suppress("LeakingThis") +open class SetupMaven : Sync() { + @get:Input + var mavenVersion = "3.5.0" + + @get:Input + var mavenPluginToolsVersion = "3.5.2" + + @get:Input + var aetherVersion = "1.1.0" + + @get:Internal + val mavenBuildDir = "${project.buildDir}/maven" + + @get:Internal + val mavenBinDir = "${project.buildDir}/maven-bin" + + @get:Internal + val mvn = File(mavenBinDir, "apache-maven-$mavenVersion/bin/mvn") + + private val mavenBinaryConfiguration: Configuration by project.configurations.creating { + project.dependencies { + this@creating.invoke( + group = "org.apache.maven", + name = "apache-maven", + version = mavenVersion, + classifier = "bin", ext = "zip" + ) + } + } + + init { + from(mavenBinaryConfiguration.map { file -> project.zipTree(file) }) + into(mavenBinDir) + } + +} diff --git a/buildSrc/src/main/kotlin/org/jetbrains/ValidatePublications.kt b/buildSrc/src/main/kotlin/org/jetbrains/ValidatePublications.kt new file mode 100644 index 00000000..84c48b96 --- /dev/null +++ b/buildSrc/src/main/kotlin/org/jetbrains/ValidatePublications.kt @@ -0,0 +1,79 @@ +package org.jetbrains + +import com.jfrog.bintray.gradle.BintrayExtension +import org.gradle.api.DefaultTask +import org.gradle.api.GradleException +import org.gradle.api.Project +import org.gradle.api.artifacts.ProjectDependency +import org.gradle.api.publish.PublishingExtension +import org.gradle.api.publish.maven.MavenPublication +import org.gradle.api.tasks.TaskAction +import org.gradle.kotlin.dsl.findByType +import org.gradle.kotlin.dsl.provideDelegate + +open class ValidatePublications : DefaultTask() { + class MissingBintrayPublicationException(project: Project, publication: MavenPublication) : GradleException( + "Project ${project.path} has publication ${publication.name} that is not configured for bintray publication" + ) + + class UnpublishedProjectDependencyException( + project: Project, dependencyProject: Project + ) : GradleException( + "Published project ${project.path} cannot depend on unpublished projed ${dependencyProject.path}" + ) + + + @TaskAction + fun validatePublicationConfiguration() { + @Suppress("LocalVariableName") + project.subprojects.forEach { subProject -> + val publishing = subProject.extensions.findByType<PublishingExtension>() ?: return@forEach + publishing.publications + .filterIsInstance<MavenPublication>() + .filter { it.version == project.dokkaVersion } + .forEach { publication -> + checkPublicationIsConfiguredForBintray(subProject, publication) + checkProjectDependenciesArePublished(subProject) + } + } + } + + private fun checkPublicationIsConfiguredForBintray(project: Project, publication: MavenPublication) { + val bintrayExtension = project.extensions.findByType<BintrayExtension>() + ?: throw MissingBintrayPublicationException(project, publication) + + val isPublicationConfiguredForBintray = bintrayExtension.publications.orEmpty() + .any { publicationName -> publicationName == publication.name } + + if (!isPublicationConfiguredForBintray) { + throw MissingBintrayPublicationException(project, publication) + } + } + + private fun checkProjectDependenciesArePublished(project: Project) { + (project.configurations.findByName("implementation")?.allDependencies.orEmpty() + + project.configurations.findByName("api")?.allDependencies.orEmpty()) + .filterIsInstance<ProjectDependency>() + .forEach { projectDependency -> + val publishing = projectDependency.dependencyProject.extensions.findByType<PublishingExtension>() + ?: throw UnpublishedProjectDependencyException( + project = project, dependencyProject = projectDependency.dependencyProject + ) + + val isPublished = publishing.publications.filterIsInstance<MavenPublication>() + .filter { it.version == project.dokkaVersion } + .any() + + if (!isPublished) { + throw UnpublishedProjectDependencyException(project, projectDependency.dependencyProject) + } + } + } + + init { + group = "verification" + project.tasks.named("check") { + dependsOn(this@ValidatePublications) + } + } +} diff --git a/buildSrc/src/main/kotlin/org/jetbrains/projectUtils.kt b/buildSrc/src/main/kotlin/org/jetbrains/projectUtils.kt new file mode 100644 index 00000000..46d803a5 --- /dev/null +++ b/buildSrc/src/main/kotlin/org/jetbrains/projectUtils.kt @@ -0,0 +1,16 @@ +package org.jetbrains + +import org.gradle.api.Project + +fun Project.whenEvaluated(action: Project.() -> Unit) { + if (state.executed) { + action() + } else { + afterEvaluate { action() } + } +} + +fun Project.invokeWhenEvaluated(action: (project: Project) -> Unit) { + whenEvaluated { action(this) } +} + diff --git a/buildSrc/src/main/kotlin/org/jetbrains/publication.kt b/buildSrc/src/main/kotlin/org/jetbrains/publication.kt new file mode 100644 index 00000000..289bd117 --- /dev/null +++ b/buildSrc/src/main/kotlin/org/jetbrains/publication.kt @@ -0,0 +1,61 @@ +package org.jetbrains + +import com.github.jengelman.gradle.plugins.shadow.ShadowExtension +import com.jfrog.bintray.gradle.BintrayExtension +import org.gradle.api.Project +import org.gradle.api.publish.PublishingExtension +import org.gradle.api.publish.maven.MavenPublication +import org.gradle.kotlin.dsl.* + +class DokkaPublicationBuilder { + enum class Component { + Java, Shadow + } + + var artifactId: String? = null + var component: Component = Component.Java +} + +fun Project.registerDokkaArtifactPublication(publicationName: String, configure: DokkaPublicationBuilder.() -> Unit) { + configure<PublishingExtension> { + publications { + register<MavenPublication>(publicationName) { + val builder = DokkaPublicationBuilder().apply(configure) + this.artifactId = builder.artifactId + when (builder.component) { + DokkaPublicationBuilder.Component.Java -> from(components["java"]) + DokkaPublicationBuilder.Component.Shadow -> run { + artifact(tasks["sourcesJar"]) + extensions.getByType(ShadowExtension::class.java).component(this) + } + } + } + } + } + + configureBintrayPublication(publicationName) +} + +fun Project.configureBintrayPublication(vararg publications: String) { + val dokka_version: String by this + val dokka_publication_channel: String by this + extensions.configure<BintrayExtension>("bintray") { + user = System.getenv("BINTRAY_USER") + key = System.getenv("BINTRAY_KEY") + dryRun = System.getenv("BINTRAY_DRY_RUN") == "true" || + project.properties["bintray_dry_run"] == "true" + pkg = PackageConfig().apply { + repo = dokka_publication_channel + name = "dokka" + userOrg = "kotlin" + desc = "Dokka, the Kotlin documentation tool" + vcsUrl = "https://github.com/kotlin/dokka.git" + setLicenses("Apache-2.0") + version = VersionConfig().apply { + name = dokka_version + } + } + setPublications(*publications) + } +} + diff --git a/buildSrc/src/main/kotlin/org/jetbrains/taskUtils.kt b/buildSrc/src/main/kotlin/org/jetbrains/taskUtils.kt new file mode 100644 index 00000000..ef9c5e6a --- /dev/null +++ b/buildSrc/src/main/kotlin/org/jetbrains/taskUtils.kt @@ -0,0 +1,13 @@ +package org.jetbrains + +import org.gradle.api.Task + +fun Task.dependsOnMavenLocalPublication() { + project.rootProject.allprojects.forEach { otherProject -> + otherProject.invokeWhenEvaluated { evaluatedProject -> + evaluatedProject.tasks.findByName("publishToMavenLocal")?.let { publishingTask -> + this.dependsOn(publishingTask) + } + } + } +} diff --git a/core/.gitignore b/core/.gitignore new file mode 100644 index 00000000..1e1b410b --- /dev/null +++ b/core/.gitignore @@ -0,0 +1 @@ +src/main/resources/dokka/scripts/*.svg
\ No newline at end of file diff --git a/core/build.gradle b/core/build.gradle deleted file mode 100644 index 9e3026cb..00000000 --- a/core/build.gradle +++ /dev/null @@ -1,53 +0,0 @@ -import javax.tools.ToolProvider - -buildscript { - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - -apply plugin: 'kotlin' - -sourceCompatibility = 1.8 - -tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { - kotlinOptions { - languageVersion = language_version - apiVersion = language_version - jvmTarget = "1.8" - } -} - -dependencies { - compile "org.jetbrains.kotlin:kotlin-stdlib:$bundled_kotlin_compiler_version" - compile "org.jetbrains.kotlin:kotlin-reflect:$bundled_kotlin_compiler_version" - - compile group: 'com.google.inject', name: 'guice', version: '3.0' - compile "org.jsoup:jsoup:1.8.3" - - compile "org.jetbrains.kotlin:kotlin-compiler:$bundled_kotlin_compiler_version" - compile "org.jetbrains.kotlin:kotlin-script-runtime:$bundled_kotlin_compiler_version" - - compile "org.jetbrains:markdown:0.1.41" - - compile intellijCoreAnalysis() - - compile "org.jetbrains.kotlin:kotlin-plugin-ij193:$kotlin_plugin_version" //TODO: parametrize ij version after 1.3.70 - - compile 'org.jetbrains.kotlinx:kotlinx-html-jvm:0.6.8' - - //tools.jar - def toolsJar = files(((URLClassLoader) ToolProvider.getSystemToolClassLoader()).getURLs().findAll { it.path.endsWith("jar") }) - compileOnly toolsJar - testCompile toolsJar - - compile project(":integration") - - testCompile group: 'junit', name: 'junit', version: '4.12' - testCompile group: 'org.jetbrains.kotlin', name: 'kotlin-test-junit', version: kotlin_version - testCompile "com.nhaarman:mockito-kotlin-kt1.1:1.5.0" - implementation "com.google.code.gson:gson:$gson_version" - testCompile ideaRT() - testImplementation "org.jetbrains.kotlin:kotlin-stdlib-js:$bundled_kotlin_compiler_version" - testImplementation "org.jetbrains.kotlin:kotlin-stdlib-common:$bundled_kotlin_compiler_version" -}
\ No newline at end of file diff --git a/core/build.gradle.kts b/core/build.gradle.kts new file mode 100644 index 00000000..168baf48 --- /dev/null +++ b/core/build.gradle.kts @@ -0,0 +1,33 @@ +import org.jetbrains.registerDokkaArtifactPublication + +plugins { + `maven-publish` + id("com.jfrog.bintray") +} + +dependencies { + api("org.jetbrains:markdown:0.1.45") + implementation(kotlin("reflect")) + implementation("com.google.code.gson:gson:2.8.5") + implementation("org.jsoup:jsoup:1.12.1") + + testImplementation(project(":testApi")) + testImplementation(kotlin("test-junit")) +} + +tasks { + processResources { + val dokka_version: String by project + eachFile { + if (name == "dokka-version.properties") { + filter { line -> + line.replace("<dokka-version>", dokka_version) + } + } + } + } +} + +registerDokkaArtifactPublication("dokkaCore") { + artifactId = "dokka-core" +} diff --git a/core/migration_guide.md b/core/migration_guide.md new file mode 100644 index 00000000..82be3768 --- /dev/null +++ b/core/migration_guide.md @@ -0,0 +1,166 @@ +## Changes between 0.10.x and 1.4-M3 + +There are two main changes between dokka 0.10.x and 1.4-M3 + +The first is the introduction of plugability - new documentation creating process is divided into several steps and each step provides extension points to be used. To learn more about new dokka pipeline and possible plugins, please read Developer's guide. + +Second difference comes with the change with the subject of dokka pass. Previously, separate dokka passes where set for every targeted platform, now every source set has its own pass and the name itself changed to `sourceSet`. + +### Gradle + +With changing the approach from platform-based to source-set-based, we replace both `configuration` and `multiplatform` blocks with `dokkaSourceSets`. It's still a collection of dokka passes configuration, so the structure stays as it was. +Format selection is now done using plugins with dokka providing preconfigured tasks for different formats: `dokkaHtml`, `dokkaJavadoc`, `dokkaGfm` and `dokkaJekyll`. + +* `moduleName` has changed to `moduleDisplayName` +* `targets` has been dropped. Declaration merging is now done by the source set mechanism. Name customization can be done using `displayName` property +* `outputFormat` has been dropped. Format can be selected with appropriate plugins, please refer to the README + +#### Groovy +##### Old +```groovy +dokka { + outputFormat = 'html' + outputDirectory = "$buildDir/dokka" + multiplatform { + js { + includes = ["src/jsMain/resources/doc.md"] + samples = ["src/jsMain/resources/Samples.kt"] + sourceLink { + path = "src/jsMain/kotlin" + url = "https:/dokka.documentation.com/jsMain/kotlin" + lineSuffix = "#L" + } + } + jvm { + includes = ["src/jvmMain/resources/doc.md"] + samples = ["src/jsMain/resources/Samples.kt"] + sourceLink { + path = "src/jvmMain/kotlin" + url = "https:/dokka.documentation.com/jvmMain/kotlin" + lineSuffix = "#L" + } + } + } +} +``` +##### New +```groovy +dokkaHtml { // or dokkaGfm, dokkaJekyll, ... + outputDirectory = "$buildDir/dokka" + + dokkaSourceSets { + commonMain {} + jsMain { + includes = ["src/jsMain/resources/doc.md"] + samples = ["src/jsMain/resources/Samples.kt"] + sourceLink { + path = "src/jsMain/kotlin" + url = "https:/dokka.documentation.com/jsMain/kotlin" + lineSuffix = "#L" + } + } + + jvmMain { + includes = ["src/jvmMain/resources/doc.md"] + samples = ["src/jsMain/resources/Samples.kt"] + sourceLink { + path = "src/jvmMain/kotlin" + url = "https:/dokka.documentation.com/jvmMain/kotlin" + lineSuffix = "#L" + } + } + } +} +``` + +#### Kotlin + +##### Old +```kotlin +kotlin { // Kotlin Multiplatform plugin configuration + jvm() + js("customName") +} + +val dokka by getting(DokkaTask::class) { + outputDirectory = "$buildDir/dokka" + outputFormat = "html" + + multiplatform { + val customName by creating { // The same name as in Kotlin Multiplatform plugin, so the sources are fetched automatically + includes = listOf("packages.md", "extra.md") + samples = listOf("samples/basic.kt", "samples/advanced.kt") + } + + register("differentName") { // Different name, so source roots must be passed explicitly + targets = listOf("JVM") + platform = "jvm" + sourceRoot { + path = kotlin.sourceSets.getByName("jvmMain").kotlin.srcDirs.first().toString() + } + sourceRoot { + path = kotlin.sourceSets.getByName("commonMain").kotlin.srcDirs.first().toString() + } + } + } +} +``` + +##### New +```kotlin +kotlin { // Kotlin Multiplatform plugin configuration + jvm() + js("customName") +} + +dokkaHtml { // or dokkaGfm, dokkaJekyll, ... + outputDirectory = "$buildDir/dokka" + + dokkaSourceSets { + val customNameMain by creating { // The same source set name as in Kotlin Multiplatform plugin, so the sources are fetched automatically + includes = listOf("packages.md", "extra.md") + samples = listOf("samples/basic.kt", "samples/advanced.kt") + } + + val commonMain by creating {} // Document commonj source set + + create("differentName") { // Different name, so source roots must be passed explicitly + sourceSetName = listOf("JVM") + platform = "jvm" + sourceRoot { + path = kotlin.sourceSets.getByName("jvmMain").kotlin.srcDirs.first().toString() + } + dependsOn(commonMain) // The jvm source set depends on the common source set + } + } +} +``` + +#### Multimodule page + +There is a new task, `dokkaMultimodule`, that creates an index page for all documented subprojects. It is available only for top-level project. + +```groovy +dokkaMultimodule { + // File to be used as an excerpt for each subprojects + documentationFileName = "README.md" + // output format for rendering multimodule page (accepts the same values as regular dokka task) + outputFormat = "html" + // output directory for page + outputDirectory = "$buildDir/dokka" +} +``` + +### Maven + +There are no changes in maven configuration API for dokka, all previous configurations should work without issues. +The default platform label is "JVM", `sourceSetName` can be used to change it. + +### Ant +Support for the Ant plugin has been dropped, dokka should be used with CLI in Ant instead. + +### Command Line +Dokka fajtar has been dropped, thus the command line interface has changed slightly. +Most importantly, all plugins and their dependencies have to be provided in the `-pluginsClasspath` argument (paths are seperated with ';'). +A build tool like Gradle or Maven is recommended to resolve and download all required artifacts. +Instead of creating a long configuration command, dokka can be configured with a JSON file. Please refer to the README. diff --git a/core/src/main/kotlin/CoreExtensions.kt b/core/src/main/kotlin/CoreExtensions.kt new file mode 100644 index 00000000..b8689154 --- /dev/null +++ b/core/src/main/kotlin/CoreExtensions.kt @@ -0,0 +1,29 @@ +package org.jetbrains.dokka + +import org.jetbrains.dokka.plugability.ExtensionPoint +import org.jetbrains.dokka.renderers.Renderer +import org.jetbrains.dokka.transformers.documentation.DocumentableMerger +import org.jetbrains.dokka.transformers.documentation.DocumentableToPageTranslator +import org.jetbrains.dokka.transformers.documentation.DocumentableTransformer +import org.jetbrains.dokka.transformers.documentation.PreMergeDocumentableTransformer +import org.jetbrains.dokka.transformers.pages.PageCreator +import org.jetbrains.dokka.transformers.pages.PageTransformer +import org.jetbrains.dokka.transformers.sources.SourceToDocumentableTranslator +import kotlin.reflect.KProperty + +object CoreExtensions { + val sourceToDocumentableTranslator by coreExtension<SourceToDocumentableTranslator>() + val preMergeDocumentableTransformer by coreExtension<PreMergeDocumentableTransformer>() + val documentableMerger by coreExtension<DocumentableMerger>() + val documentableTransformer by coreExtension<DocumentableTransformer>() + val documentableToPageTranslator by coreExtension<DocumentableToPageTranslator>() + val allModulePageCreator by coreExtension<PageCreator>() + val pageTransformer by coreExtension<PageTransformer>() + val allModulePageTransformer by coreExtension<PageTransformer>() + val renderer by coreExtension<Renderer>() + + private fun <T : Any> coreExtension() = object { + operator fun provideDelegate(thisRef: CoreExtensions, property: KProperty<*>): Lazy<ExtensionPoint<T>> = + lazy { ExtensionPoint<T>(thisRef::class.qualifiedName!!, property.name) } + } +}
\ No newline at end of file diff --git a/core/src/main/kotlin/DokkaBootstrap.kt b/core/src/main/kotlin/DokkaBootstrap.kt new file mode 100644 index 00000000..159172a5 --- /dev/null +++ b/core/src/main/kotlin/DokkaBootstrap.kt @@ -0,0 +1,11 @@ +package org.jetbrains.dokka + +import java.util.function.BiConsumer +import kotlin.jvm.Throws + +interface DokkaBootstrap { + fun configure(serializedConfigurationJSON: String, logger: BiConsumer<String, String>) + + @Throws(Throwable::class) + fun generate() +} diff --git a/core/src/main/kotlin/DokkaBootstrapImpl.kt b/core/src/main/kotlin/DokkaBootstrapImpl.kt index b48b62d4..fabbc889 100644 --- a/core/src/main/kotlin/DokkaBootstrapImpl.kt +++ b/core/src/main/kotlin/DokkaBootstrapImpl.kt @@ -1,49 +1,78 @@ package org.jetbrains.dokka -import com.google.gson.Gson import org.jetbrains.dokka.DokkaConfiguration.PackageOptions +import org.jetbrains.dokka.utilities.DokkaLogger import java.util.function.BiConsumer -fun parsePerPackageOptions(arg: String): List<PackageOptions> { - if (arg.isBlank()) return emptyList() +fun parsePerPackageOptions(args: List<String>): List<PackageOptions> = args.map { it.split(",") }.map { + val prefix = it.first() + if (prefix == "") + throw IllegalArgumentException( + "Please do not register packageOptions with all match pattern, use global settings instead" + ) - return arg.split(";").map { it.split(",") }.map { - val prefix = it.first() - if (prefix == "") - throw IllegalArgumentException("Please do not register packageOptions with all match pattern, use global settings instead") - val args = it.subList(1, it.size) - val deprecated = args.find { it.endsWith("deprecated") }?.startsWith("+") ?: true - val reportUndocumented = args.find { it.endsWith("warnUndocumented") }?.startsWith("+") ?: true - val privateApi = args.find { it.endsWith("privateApi") }?.startsWith("+") ?: false - val suppress = args.find { it.endsWith("suppress") }?.startsWith("+") ?: false - PackageOptionsImpl(prefix, includeNonPublic = privateApi, reportUndocumented = reportUndocumented, skipDeprecated = !deprecated, suppress = suppress) - } + val args = it.subList(1, it.size) + + val deprecated = args.find { it.endsWith("skipDeprecated") }?.startsWith("+") + ?: DokkaDefaults.skipDeprecated + + val reportUndocumented = args.find { it.endsWith("reportUndocumented") }?.startsWith("+") + ?: DokkaDefaults.reportUndocumented + + val privateApi = args.find { it.endsWith("includeNonPublic") }?.startsWith("+") + ?: DokkaDefaults.includeNonPublic + + val suppress = args.find { it.endsWith("suppress") }?.startsWith("+") + ?: DokkaDefaults.suppress + + PackageOptionsImpl( + prefix, + includeNonPublic = privateApi, + reportUndocumented = reportUndocumented, + skipDeprecated = !deprecated, + suppress = suppress + ) } + +/** + * Accessed with reflection + */ +@Suppress("unused") class DokkaBootstrapImpl : DokkaBootstrap { - private class DokkaProxyLogger(val consumer: BiConsumer<String, String>) : DokkaLogger { + class DokkaProxyLogger(val consumer: BiConsumer<String, String>) : DokkaLogger { + override var warningsCount: Int = 0 + override var errorsCount: Int = 0 + + override fun debug(message: String) { + consumer.accept("debug", message) + } + override fun info(message: String) { consumer.accept("info", message) } + override fun progress(message: String) { + consumer.accept("progress", message) + } + override fun warn(message: String) { - consumer.accept("warn", message) + consumer.accept("warn", message).also { warningsCount++ } } override fun error(message: String) { - consumer.accept("error", message) + consumer.accept("error", message).also { errorsCount++ } } } - lateinit var generator: DokkaGenerator - val gson = Gson() + private lateinit var generator: DokkaGenerator fun configure(logger: DokkaLogger, configuration: DokkaConfigurationImpl) = with(configuration) { - fun defaultLinks(config: PassConfigurationImpl): List<ExternalDocumentationLinkImpl> { + fun defaultLinks(config: DokkaSourceSetImpl): List<ExternalDocumentationLinkImpl> { val links = mutableListOf<ExternalDocumentationLinkImpl>() if (!config.noJdkLink) links += DokkaConfiguration.ExternalDocumentationLink @@ -57,20 +86,21 @@ class DokkaBootstrapImpl : DokkaBootstrap { return links } - val configurationWithLinks = - configuration.copy(passesConfigurations = - passesConfigurations - .map { - val links: List<ExternalDocumentationLinkImpl> = it.externalDocumentationLinks + defaultLinks(it) - it.copy(externalDocumentationLinks = links) - } - ) + val configurationWithLinks = configuration.copy( + sourceSets = sourceSets.map { + val links: List<ExternalDocumentationLinkImpl> = + it.externalDocumentationLinks + defaultLinks(it) + it.copy(externalDocumentationLinks = links) + } + ) generator = DokkaGenerator(configurationWithLinks, logger) } - override fun configure(logger: BiConsumer<String, String>, serializedConfigurationJSON: String) - = configure(DokkaProxyLogger(logger), gson.fromJson(serializedConfigurationJSON, DokkaConfigurationImpl::class.java)) + override fun configure(serializedConfigurationJSON: String, logger: BiConsumer<String, String>) = configure( + DokkaProxyLogger(logger), + DokkaConfigurationImpl(serializedConfigurationJSON) + ) override fun generate() = generator.generate() } diff --git a/core/src/main/kotlin/DokkaException.kt b/core/src/main/kotlin/DokkaException.kt new file mode 100644 index 00000000..0010249c --- /dev/null +++ b/core/src/main/kotlin/DokkaException.kt @@ -0,0 +1,3 @@ +package org.jetbrains.dokka + +class DokkaException(message: String) : RuntimeException(message) diff --git a/core/src/main/kotlin/DokkaGenerator.kt b/core/src/main/kotlin/DokkaGenerator.kt new file mode 100644 index 00000000..4262d890 --- /dev/null +++ b/core/src/main/kotlin/DokkaGenerator.kt @@ -0,0 +1,164 @@ +package org.jetbrains.dokka + +import org.jetbrains.dokka.model.DModule +import org.jetbrains.dokka.DokkaConfiguration.* +import org.jetbrains.dokka.pages.RootPageNode +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.DokkaPlugin +import org.jetbrains.dokka.utilities.DokkaLogger +import org.jetbrains.dokka.utilities.report + + +/** + * DokkaGenerator is the main entry point for generating documentation + * + * [generate] method has been split into submethods for test reasons + */ +class DokkaGenerator( + private val configuration: DokkaConfiguration, + private val logger: DokkaLogger +) { + fun generate() = timed(logger) { + report("Initializing plugins") + val context = initializePlugins(configuration, logger) + + report("Creating documentation models") + val modulesFromPlatforms = createDocumentationModels(context) + + report("Transforming documentation model before merging") + val transformedDocumentationBeforeMerge = transformDocumentationModelBeforeMerge(modulesFromPlatforms, context) + + report("Merging documentation models") + val documentationModel = mergeDocumentationModels(transformedDocumentationBeforeMerge, context) + + report("Transforming documentation model after merging") + val transformedDocumentation = transformDocumentationModelAfterMerge(documentationModel, context) + + report("Creating pages") + val pages = createPages(transformedDocumentation, context) + + report("Transforming pages") + val transformedPages = transformPages(pages, context) + + report("Rendering") + render(transformedPages, context) + + reportAfterRendering(context) + }.dump("\n\n === TIME MEASUREMENT ===\n") + + fun generateAllModulesPage() = timed { + report("Initializing plugins") + val context = initializePlugins(configuration, logger) + + report("Creating all modules page") + val pages = createAllModulePage(context) + + report("Transforming pages") + val transformedPages = transformAllModulesPage(pages, context) + + report("Rendering") + render(transformedPages, context) + }.dump("\n\n === TIME MEASUREMENT ===\n") + + + fun initializePlugins( + configuration: DokkaConfiguration, + logger: DokkaLogger, + additionalPlugins: List<DokkaPlugin> = emptyList() + ) = DokkaContext.create(configuration, logger, additionalPlugins) + + fun createDocumentationModels( + context: DokkaContext + ) = context.configuration.sourceSets + .flatMap { sourceSet -> translateSources(sourceSet, context) } + + fun transformDocumentationModelBeforeMerge( + modulesFromPlatforms: List<DModule>, + context: DokkaContext + ) = context[CoreExtensions.preMergeDocumentableTransformer].fold(modulesFromPlatforms) { acc, t -> t(acc) } + + fun mergeDocumentationModels( + modulesFromPlatforms: List<DModule>, + context: DokkaContext + ) = context.single(CoreExtensions.documentableMerger).invoke(modulesFromPlatforms, context) + + fun transformDocumentationModelAfterMerge( + documentationModel: DModule, + context: DokkaContext + ) = context[CoreExtensions.documentableTransformer].fold(documentationModel) { acc, t -> t(acc, context) } + + fun createPages( + transformedDocumentation: DModule, + context: DokkaContext + ) = context.single(CoreExtensions.documentableToPageTranslator).invoke(transformedDocumentation) + + fun createAllModulePage( + context: DokkaContext + ) = context.single(CoreExtensions.allModulePageCreator).invoke() + + fun transformPages( + pages: RootPageNode, + context: DokkaContext + ) = context[CoreExtensions.pageTransformer].fold(pages) { acc, t -> t(acc) } + + fun transformAllModulesPage( + pages: RootPageNode, + context: DokkaContext + ) = context[CoreExtensions.allModulePageTransformer].fold(pages) { acc, t -> t(acc) } + + fun render( + transformedPages: RootPageNode, + context: DokkaContext + ) { + val renderer = context.single(CoreExtensions.renderer) + renderer.render(transformedPages) + } + + fun reportAfterRendering(context: DokkaContext) { + context.unusedPoints.takeIf { it.isNotEmpty() }?.also { + logger.info("Unused extension points found: ${it.joinToString(", ")}") + } + + logger.report() + + if (context.configuration.failOnWarning && (logger.warningsCount > 0 || logger.errorsCount > 0)) { + throw DokkaException( + "Failed with warningCount=${logger.warningsCount} and errorCount=${logger.errorsCount}" + ) + } + } + + private fun translateSources(sourceSet: DokkaSourceSet, context: DokkaContext) = + context[CoreExtensions.sourceToDocumentableTranslator].map { + it.invoke(sourceSet, context) + } +} + +private class Timer(startTime: Long, private val logger: DokkaLogger?) { + private val steps = mutableListOf("" to startTime) + + fun report(name: String) { + logger?.progress(name) + steps += (name to System.currentTimeMillis()) + } + + fun dump(prefix: String = "") { + logger?.info(prefix) + val namePad = steps.map { it.first.length }.max() ?: 0 + val timePad = steps.windowed(2).map { (p1, p2) -> p2.second - p1.second }.max()?.toString()?.length ?: 0 + steps.windowed(2).forEach { (p1, p2) -> + if (p1.first.isNotBlank()) { + logger?.info("${p1.first.padStart(namePad)}: ${(p2.second - p1.second).toString().padStart(timePad)}") + } + } + } +} + +private fun timed(logger: DokkaLogger? = null, block: Timer.() -> Unit): Timer = + Timer(System.currentTimeMillis(), logger).apply { + try { + block() + } finally { + report("") + } + } diff --git a/core/src/main/kotlin/DokkaMultimoduleBootstrapImpl.kt b/core/src/main/kotlin/DokkaMultimoduleBootstrapImpl.kt new file mode 100644 index 00000000..c0726584 --- /dev/null +++ b/core/src/main/kotlin/DokkaMultimoduleBootstrapImpl.kt @@ -0,0 +1,29 @@ +/** + * Accessed with reflection + */ +@file:Suppress("unused") + +package org.jetbrains.dokka + +import org.jetbrains.dokka.DokkaBootstrapImpl.DokkaProxyLogger +import org.jetbrains.dokka.utilities.DokkaLogger +import java.util.function.BiConsumer + +class DokkaMultimoduleBootstrapImpl : DokkaBootstrap { + + private lateinit var generator: DokkaGenerator + + fun configure(logger: DokkaLogger, configuration: DokkaConfiguration) { + generator = DokkaGenerator(configuration, logger) + } + + override fun configure(serializedConfigurationJSON: String, logger: BiConsumer<String, String>) = configure( + DokkaProxyLogger(logger), + DokkaConfigurationImpl(serializedConfigurationJSON) + ) + + override fun generate() { + generator.generateAllModulesPage() + } + +} diff --git a/core/src/main/kotlin/DokkaVersion.kt b/core/src/main/kotlin/DokkaVersion.kt new file mode 100644 index 00000000..410058f3 --- /dev/null +++ b/core/src/main/kotlin/DokkaVersion.kt @@ -0,0 +1,10 @@ +package org.jetbrains.dokka + +import java.util.* + +object DokkaVersion { + val version: String by lazy { + val stream = javaClass.getResourceAsStream("/META-INF/dokka/dokka-version.properties") + Properties().apply { load(stream) }.getProperty("dokka-version") + } +} diff --git a/core/src/main/kotlin/Formats/AnalysisComponents.kt b/core/src/main/kotlin/Formats/AnalysisComponents.kt deleted file mode 100644 index d78d4a0c..00000000 --- a/core/src/main/kotlin/Formats/AnalysisComponents.kt +++ /dev/null @@ -1,45 +0,0 @@ -package org.jetbrains.dokka.Formats - -import com.google.inject.Binder -import org.jetbrains.dokka.* -import org.jetbrains.dokka.KotlinAsJavaElementSignatureProvider -import org.jetbrains.dokka.KotlinElementSignatureProvider -import org.jetbrains.dokka.ElementSignatureProvider -import org.jetbrains.dokka.Samples.DefaultSampleProcessingService -import org.jetbrains.dokka.Samples.SampleProcessingService -import org.jetbrains.dokka.Utilities.bind -import org.jetbrains.dokka.Utilities.toType -import kotlin.reflect.KClass - - -interface DefaultAnalysisComponentServices { - val packageDocumentationBuilderClass: KClass<out PackageDocumentationBuilder> - val javaDocumentationBuilderClass: KClass<out JavaDocumentationBuilder> - val sampleProcessingService: KClass<out SampleProcessingService> - val elementSignatureProvider: KClass<out ElementSignatureProvider> -} - -interface DefaultAnalysisComponent : FormatDescriptorAnalysisComponent, DefaultAnalysisComponentServices { - override fun configureAnalysis(binder: Binder): Unit = with(binder) { - bind<ElementSignatureProvider>() toType elementSignatureProvider - bind<PackageDocumentationBuilder>() toType packageDocumentationBuilderClass - bind<JavaDocumentationBuilder>() toType javaDocumentationBuilderClass - bind<SampleProcessingService>() toType sampleProcessingService - } -} - - -object KotlinAsJava : DefaultAnalysisComponentServices { - override val packageDocumentationBuilderClass = KotlinAsJavaDocumentationBuilder::class - override val javaDocumentationBuilderClass = JavaPsiDocumentationBuilder::class - override val sampleProcessingService = DefaultSampleProcessingService::class - override val elementSignatureProvider = KotlinAsJavaElementSignatureProvider::class -} - - -object KotlinAsKotlin : DefaultAnalysisComponentServices { - override val packageDocumentationBuilderClass = KotlinPackageDocumentationBuilder::class - override val javaDocumentationBuilderClass = KotlinJavaDocumentationBuilder::class - override val sampleProcessingService = DefaultSampleProcessingService::class - override val elementSignatureProvider = KotlinElementSignatureProvider::class -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Formats/FormatDescriptor.kt b/core/src/main/kotlin/Formats/FormatDescriptor.kt deleted file mode 100644 index 4bac8aa0..00000000 --- a/core/src/main/kotlin/Formats/FormatDescriptor.kt +++ /dev/null @@ -1,43 +0,0 @@ -package org.jetbrains.dokka.Formats - -import com.google.inject.Binder -import org.jetbrains.dokka.* -import org.jetbrains.dokka.Utilities.bind -import org.jetbrains.dokka.Utilities.lazyBind -import org.jetbrains.dokka.Utilities.toOptional -import org.jetbrains.dokka.Utilities.toType -import kotlin.reflect.KClass - - -interface FormatDescriptorAnalysisComponent { - fun configureAnalysis(binder: Binder) -} - -interface FormatDescriptorOutputComponent { - fun configureOutput(binder: Binder) -} - -interface FormatDescriptor : FormatDescriptorAnalysisComponent, FormatDescriptorOutputComponent - - -abstract class FileGeneratorBasedFormatDescriptor : FormatDescriptor { - - override fun configureOutput(binder: Binder): Unit = with(binder) { - bind<Generator>() toType NodeLocationAwareGenerator::class - bind<NodeLocationAwareGenerator>() toType generatorServiceClass - bind(generatorServiceClass.java) // https://github.com/google/guice/issues/847 - - bind<LanguageService>() toType languageServiceClass - - lazyBind<OutlineFormatService>() toOptional (outlineServiceClass) - lazyBind<FormatService>() toOptional formatServiceClass - lazyBind<PackageListService>() toOptional packageListServiceClass - } - - abstract val formatServiceClass: KClass<out FormatService>? - abstract val outlineServiceClass: KClass<out OutlineFormatService>? - abstract val generatorServiceClass: KClass<out FileGenerator> - abstract val packageListServiceClass: KClass<out PackageListService>? - - open val languageServiceClass: KClass<out LanguageService> = KotlinLanguageService::class -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Formats/FormatService.kt b/core/src/main/kotlin/Formats/FormatService.kt deleted file mode 100644 index 8f4855e3..00000000 --- a/core/src/main/kotlin/Formats/FormatService.kt +++ /dev/null @@ -1,32 +0,0 @@ -package org.jetbrains.dokka - -/** - * Abstract representation of a formatting service used to output documentation in desired format - * - * Bundled Formatters: - * * [HtmlFormatService] – outputs documentation to HTML format - * * [MarkdownFormatService] – outputs documentation in Markdown format - */ -interface FormatService { - /** Returns extension for output files */ - val extension: String - - /** extension which will be used for internal and external linking */ - val linkExtension: String - get() = extension - - fun createOutputBuilder(to: StringBuilder, location: Location): FormattedOutputBuilder - - fun enumerateSupportFiles(callback: (resource: String, targetPath: String) -> Unit) { - } -} - -interface FormattedOutputBuilder { - /** Appends formatted content */ - fun appendNodes(nodes: Iterable<DocumentationNode>) -} - -/** Format content to [String] using specified [location] */ -fun FormatService.format(location: Location, nodes: Iterable<DocumentationNode>): String = StringBuilder().apply { - createOutputBuilder(this, location).appendNodes(nodes) -}.toString() diff --git a/core/src/main/kotlin/Formats/GFMFormatService.kt b/core/src/main/kotlin/Formats/GFMFormatService.kt deleted file mode 100644 index 036ec856..00000000 --- a/core/src/main/kotlin/Formats/GFMFormatService.kt +++ /dev/null @@ -1,61 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import com.google.inject.name.Named -import org.jetbrains.dokka.Utilities.impliedPlatformsName - -open class GFMOutputBuilder( - to: StringBuilder, - location: Location, - generator: NodeLocationAwareGenerator, - languageService: LanguageService, - extension: String, - impliedPlatforms: List<String> -) : MarkdownOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) { - override fun appendTable(vararg columns: String, body: () -> Unit) { - to.appendln(columns.joinToString(" | ", "| ", " |")) - to.appendln("|" + "---|".repeat(columns.size)) - body() - } - - override fun appendUnorderedList(body: () -> Unit) { - if (inTableCell) { - wrapInTag("ul", body) - } else { - super.appendUnorderedList(body) - } - } - - override fun appendOrderedList(body: () -> Unit) { - if (inTableCell) { - wrapInTag("ol", body) - } else { - super.appendOrderedList(body) - } - } - - override fun appendListItem(body: () -> Unit) { - if (inTableCell) { - wrapInTag("li", body) - } else { - super.appendListItem(body) - } - } -} - -open class GFMFormatService( - generator: NodeLocationAwareGenerator, - signatureGenerator: LanguageService, - linkExtension: String, - impliedPlatforms: List<String> -) : MarkdownFormatService(generator, signatureGenerator, linkExtension, impliedPlatforms) { - - @Inject constructor( - generator: NodeLocationAwareGenerator, - signatureGenerator: LanguageService, - @Named(impliedPlatformsName) impliedPlatforms: List<String> - ) : this(generator, signatureGenerator, "md", impliedPlatforms) - - override fun createOutputBuilder(to: StringBuilder, location: Location): FormattedOutputBuilder = - GFMOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) -} diff --git a/core/src/main/kotlin/Formats/HtmlFormatService.kt b/core/src/main/kotlin/Formats/HtmlFormatService.kt deleted file mode 100644 index d36ea0a2..00000000 --- a/core/src/main/kotlin/Formats/HtmlFormatService.kt +++ /dev/null @@ -1,166 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import com.google.inject.name.Named -import org.jetbrains.dokka.Utilities.impliedPlatformsName -import java.io.File - -open class HtmlOutputBuilder(to: StringBuilder, - location: Location, - generator: NodeLocationAwareGenerator, - languageService: LanguageService, - extension: String, - impliedPlatforms: List<String>, - val templateService: HtmlTemplateService) - : StructuredOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) -{ - override fun appendText(text: String) { - to.append(text.htmlEscape()) - } - - override fun appendSymbol(text: String) { - to.append("<span class=\"symbol\">${text.htmlEscape()}</span>") - } - - override fun appendKeyword(text: String) { - to.append("<span class=\"keyword\">${text.htmlEscape()}</span>") - } - - override fun appendIdentifier(text: String, kind: IdentifierKind, signature: String?) { - val id = signature?.let { " id=\"$it\"" }.orEmpty() - to.append("<span class=\"identifier\"$id>${text.htmlEscape()}</span>") - } - - override fun appendBlockCode(language: String, body: () -> Unit) { - val openTags = if (language.isNotBlank()) - "<pre><code class=\"lang-$language\">" - else - "<pre><code>" - wrap(openTags, "</code></pre>", body) - } - - override fun appendHeader(level: Int, body: () -> Unit) = - wrapInTag("h$level", body, newlineBeforeOpen = true, newlineAfterClose = true) - override fun appendParagraph(body: () -> Unit) = - wrapInTag("p", body, newlineBeforeOpen = true, newlineAfterClose = true) - - override fun appendSoftParagraph(body: () -> Unit) = appendParagraph(body) - - override fun appendLine() { - to.appendln("<br/>") - } - - override fun appendAnchor(anchor: String) { - to.appendln("<a name=\"${anchor.htmlEscape()}\"></a>") - } - - override fun appendTable(vararg columns: String, body: () -> Unit) = - wrapInTag("table", body, newlineAfterOpen = true, newlineAfterClose = true) - override fun appendTableBody(body: () -> Unit) = - wrapInTag("tbody", body, newlineAfterOpen = true, newlineAfterClose = true) - override fun appendTableRow(body: () -> Unit) = - wrapInTag("tr", body, newlineAfterOpen = true, newlineAfterClose = true) - override fun appendTableCell(body: () -> Unit) = - wrapInTag("td", body, newlineAfterOpen = true, newlineAfterClose = true) - - override fun appendLink(href: String, body: () -> Unit) = wrap("<a href=\"$href\">", "</a>", body) - - override fun appendStrong(body: () -> Unit) = wrapInTag("strong", body) - override fun appendEmphasis(body: () -> Unit) = wrapInTag("em", body) - override fun appendStrikethrough(body: () -> Unit) = wrapInTag("s", body) - override fun appendCode(body: () -> Unit) = wrapInTag("code", body) - - override fun appendUnorderedList(body: () -> Unit) = wrapInTag("ul", body, newlineAfterClose = true) - override fun appendOrderedList(body: () -> Unit) = wrapInTag("ol", body, newlineAfterClose = true) - override fun appendListItem(body: () -> Unit) = wrapInTag("li", body, newlineAfterClose = true) - - override fun appendBreadcrumbSeparator() { - to.append(" / ") - } - - override fun appendNodes(nodes: Iterable<DocumentationNode>) { - templateService.appendHeader(to, getPageTitle(nodes), generator.relativePathToRoot(location)) - super.appendNodes(nodes) - templateService.appendFooter(to) - } - - override fun appendNonBreakingSpace() { - to.append(" ") - } - - override fun ensureParagraph() {} -} - -open class HtmlFormatService @Inject constructor(generator: NodeLocationAwareGenerator, - signatureGenerator: LanguageService, - val templateService: HtmlTemplateService, - @Named(impliedPlatformsName) val impliedPlatforms: List<String>) -: StructuredFormatService(generator, signatureGenerator, "html"), OutlineFormatService { - - override fun enumerateSupportFiles(callback: (String, String) -> Unit) { - callback("/dokka/styles/style.css", "style.css") - } - - override fun createOutputBuilder(to: StringBuilder, location: Location) = - HtmlOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms, templateService) - - override fun appendOutline(location: Location, to: StringBuilder, nodes: Iterable<DocumentationNode>) { - templateService.appendHeader(to, "Module Contents", generator.relativePathToRoot(location)) - super.appendOutline(location, to, nodes) - templateService.appendFooter(to) - } - - override fun getOutlineFileName(location: Location): File { - return File("${location.path}-outline.html") - } - - override fun appendOutlineHeader(location: Location, node: DocumentationNode, to: StringBuilder) { - val link = ContentNodeDirectLink(node) - link.append(languageService.render(node, LanguageService.RenderMode.FULL)) - val tempBuilder = StringBuilder() - createOutputBuilder(tempBuilder, location).appendContent(link) - to.appendln("<a href=\"${location.path}\">$tempBuilder</a><br/>") - } - - override fun appendOutlineLevel(to: StringBuilder, body: () -> Unit) { - to.appendln("<ul>") - body() - to.appendln("</ul>") - } -} - -fun getPageTitle(nodes: Iterable<DocumentationNode>): String? { - val breakdownByLocation = nodes.groupBy { node -> formatPageTitle(node) } - return breakdownByLocation.keys.singleOrNull() -} - -fun formatPageTitle(node: DocumentationNode): String { - val path = node.path - val moduleName = path.first().name - if (path.size == 1) { - return moduleName - } - - val qName = qualifiedNameForPageTitle(node) - return "$qName - $moduleName" -} - -private fun qualifiedNameForPageTitle(node: DocumentationNode): String { - if (node.kind == NodeKind.Package) { - var packageName = node.qualifiedName() - if (packageName.isEmpty()) { - packageName = "root package" - } - return packageName - } - - val path = node.path - var pathFromToplevelMember = path.dropWhile { it.kind !in NodeKind.classLike } - if (pathFromToplevelMember.isEmpty()) { - pathFromToplevelMember = path.dropWhile { it.kind != NodeKind.Property && it.kind != NodeKind.Function } - } - if (pathFromToplevelMember.isNotEmpty()) { - return pathFromToplevelMember.map { it.name }.filter { it.length > 0 }.joinToString(".") - } - return node.qualifiedName() -} diff --git a/core/src/main/kotlin/Formats/HtmlTemplateService.kt b/core/src/main/kotlin/Formats/HtmlTemplateService.kt deleted file mode 100644 index a65a7b18..00000000 --- a/core/src/main/kotlin/Formats/HtmlTemplateService.kt +++ /dev/null @@ -1,38 +0,0 @@ -package org.jetbrains.dokka - -import java.io.File - -interface HtmlTemplateService { - fun appendHeader(to: StringBuilder, title: String?, basePath: File) - fun appendFooter(to: StringBuilder) - - companion object { - fun default(css: String? = null): HtmlTemplateService { - return object : HtmlTemplateService { - override fun appendFooter(to: StringBuilder) { - if (!to.endsWith('\n')) { - to.append('\n') - } - to.appendln("</BODY>") - to.appendln("</HTML>") - } - override fun appendHeader(to: StringBuilder, title: String?, basePath: File) { - to.appendln("<HTML>") - to.appendln("<HEAD>") - to.appendln("<meta charset=\"UTF-8\">") - if (title != null) { - to.appendln("<title>$title</title>") - } - if (css != null) { - val cssPath = basePath.resolve(css).toUnixString() - to.appendln("<link rel=\"stylesheet\" href=\"$cssPath\">") - } - to.appendln("</HEAD>") - to.appendln("<BODY>") - } - } - } - } -} - - diff --git a/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlPackageListService.kt b/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlPackageListService.kt deleted file mode 100644 index 09bb2602..00000000 --- a/core/src/main/kotlin/Formats/JavaLayoutHtml/JavaLayoutHtmlPackageListService.kt +++ /dev/null @@ -1,117 +0,0 @@ -package org.jetbrains.dokka.Formats - -import org.jetbrains.dokka.* -import org.jetbrains.dokka.ExternalDocumentationLinkResolver.Companion.DOKKA_PARAM_PREFIX -import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.descriptors.impl.EnumEntrySyntheticClassDescriptor -import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe -import org.jetbrains.kotlin.resolve.descriptorUtil.isCompanionObject -import org.jetbrains.kotlin.types.KotlinType - -class JavaLayoutHtmlPackageListService: PackageListService { - - private fun StringBuilder.appendParam(name: String, value: String) { - append(DOKKA_PARAM_PREFIX) - append(name) - append(":") - appendln(value) - } - - override fun formatPackageList(module: DocumentationModule): String { - val packages = module.members(NodeKind.Package).map { it.name } - - return buildString { - appendParam("format", "java-layout-html") - appendParam("mode", "kotlin") - for (p in packages) { - appendln(p) - } - } - } - -} - -class JavaLayoutHtmlInboundLinkResolutionService(private val paramMap: Map<String, List<String>>) : InboundExternalLinkResolutionService { - private fun getContainerPath(symbol: DeclarationDescriptor): String? { - return when (symbol) { - is PackageFragmentDescriptor -> symbol.fqName.asString().replace('.', '/') + "/" - is ClassifierDescriptor -> getContainerPath(symbol.findPackage()) + symbol.nameWithOuter() + ".html" - else -> null - } - } - - private fun DeclarationDescriptor.findPackage(): PackageFragmentDescriptor = - generateSequence(this) { it.containingDeclaration }.filterIsInstance<PackageFragmentDescriptor>().first() - - private fun ClassifierDescriptor.nameWithOuter(): String = - generateSequence(this) { it.containingDeclaration as? ClassifierDescriptor } - .toList().asReversed().joinToString(".") { it.name.asString() } - - private fun getPagePath(symbol: DeclarationDescriptor): String? { - return when (symbol) { - is PackageFragmentDescriptor -> getContainerPath(symbol) + "package-summary.html" - is EnumEntrySyntheticClassDescriptor -> getContainerPath(symbol.containingDeclaration) + "#" + symbol.signatureForAnchorUrlEncoded() - is ClassifierDescriptor -> getContainerPath(symbol) + "#" - is FunctionDescriptor, is PropertyDescriptor -> getContainerPath(symbol.containingDeclaration!!) + "#" + symbol.signatureForAnchorUrlEncoded() - else -> null - } - } - - private fun DeclarationDescriptor.signatureForAnchor(): String? { - - fun ReceiverParameterDescriptor.extractReceiverName(): String { - var receiverClass: DeclarationDescriptor = type.constructor.declarationDescriptor!! - if (receiverClass.isCompanionObject()) { - receiverClass = receiverClass.containingDeclaration!! - } else if (receiverClass is TypeParameterDescriptor) { - val upperBoundClass = receiverClass.upperBounds.singleOrNull()?.constructor?.declarationDescriptor - if (upperBoundClass != null) { - receiverClass = upperBoundClass - } - } - - return receiverClass.name.asString() - } - - fun KotlinType.qualifiedNameForSignature(): String { - val desc = constructor.declarationDescriptor - return desc?.fqNameUnsafe?.asString() ?: "<ERROR TYPE NAME>" - } - - fun StringBuilder.appendReceiverAndCompanion(desc: CallableDescriptor) { - if (desc.containingDeclaration.isCompanionObject()) { - append("Companion.") - } - desc.extensionReceiverParameter?.let { - append("(") - append(it.extractReceiverName()) - append(").") - } - } - - return when(this) { - is EnumEntrySyntheticClassDescriptor -> buildString { - append("ENUM_VALUE:") - append(name.asString()) - } - is FunctionDescriptor -> buildString { - appendReceiverAndCompanion(this@signatureForAnchor) - append(name.asString()) - valueParameters.joinTo(this, prefix = "(", postfix = ")") { - it.type.qualifiedNameForSignature() - } - } - is PropertyDescriptor -> buildString { - appendReceiverAndCompanion(this@signatureForAnchor) - append(name.asString()) - append(":") - append(returnType?.qualifiedNameForSignature()) - } - else -> null - } - } - - private fun DeclarationDescriptor.signatureForAnchorUrlEncoded(): String? = signatureForAnchor()?.urlEncoded() - - override fun getPath(symbol: DeclarationDescriptor) = getPagePath(symbol) -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Formats/JavaLayoutHtmlFormat.kt b/core/src/main/kotlin/Formats/JavaLayoutHtmlFormat.kt deleted file mode 100644 index 885cdf6c..00000000 --- a/core/src/main/kotlin/Formats/JavaLayoutHtmlFormat.kt +++ /dev/null @@ -1,91 +0,0 @@ -package org.jetbrains.dokka.Formats - -import com.google.inject.Binder -import com.google.inject.Inject -import kotlinx.html.li -import kotlinx.html.stream.appendHTML -import kotlinx.html.ul -import org.jetbrains.dokka.* -import org.jetbrains.dokka.Samples.DefaultSampleProcessingService -import org.jetbrains.dokka.Utilities.bind -import org.jetbrains.dokka.Utilities.toType -import java.io.File - - -class JavaLayoutHtmlFormatDescriptor : FormatDescriptor, DefaultAnalysisComponent { - override val packageDocumentationBuilderClass = KotlinPackageDocumentationBuilder::class - override val javaDocumentationBuilderClass = KotlinJavaDocumentationBuilder::class - override val sampleProcessingService = DefaultSampleProcessingService::class - override val elementSignatureProvider = KotlinElementSignatureProvider::class - - override fun configureOutput(binder: Binder): Unit = with(binder) { - bind<Generator>() toType generatorServiceClass - } - - val formatServiceClass = JavaLayoutHtmlFormatService::class - val generatorServiceClass = JavaLayoutHtmlFormatGenerator::class -} - - -class JavaLayoutHtmlFormatService : FormatService { - override val extension: String - get() = TODO("not implemented") - - - override fun createOutputBuilder(to: StringBuilder, location: Location): FormattedOutputBuilder { - TODO("not implemented") - } -} - -class JavaLayoutHtmlFormatOutputBuilder : FormattedOutputBuilder { - override fun appendNodes(nodes: Iterable<DocumentationNode>) { - - } -} - - -class JavaLayoutHtmlFormatNavListBuilder @Inject constructor() : OutlineFormatService { - override fun getOutlineFileName(location: Location): File { - TODO() - } - - override fun appendOutlineHeader(location: Location, node: DocumentationNode, to: StringBuilder) { - with(to.appendHTML()) { - //a(href = ) - li { - when { - node.kind == NodeKind.Package -> appendOutline(location, to, node.members) - } - } - } - } - - override fun appendOutlineLevel(to: StringBuilder, body: () -> Unit) { - with(to.appendHTML()) { - ul { body() } - } - } - -} - -class JavaLayoutHtmlFormatGenerator @Inject constructor( - private val outlineFormatService: OutlineFormatService -) : Generator { - override fun buildPages(nodes: Iterable<DocumentationNode>) { - - } - - override fun buildOutlines(nodes: Iterable<DocumentationNode>) { - for (node in nodes) { - if (node.kind == NodeKind.Module) { - //outlineFormatService.formatOutline() - } - } - } - - override fun buildSupportFiles() {} - - override fun buildPackageList(nodes: Iterable<DocumentationNode>) { - - } -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Formats/JekyllFormatService.kt b/core/src/main/kotlin/Formats/JekyllFormatService.kt deleted file mode 100644 index a948dfa9..00000000 --- a/core/src/main/kotlin/Formats/JekyllFormatService.kt +++ /dev/null @@ -1,44 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import com.google.inject.name.Named -import org.jetbrains.dokka.Utilities.impliedPlatformsName - -open class JekyllOutputBuilder(to: StringBuilder, - location: Location, - generator: NodeLocationAwareGenerator, - languageService: LanguageService, - extension: String, - impliedPlatforms: List<String>) - : MarkdownOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) { - override fun appendNodes(nodes: Iterable<DocumentationNode>) { - to.appendln("---") - appendFrontMatter(nodes, to) - to.appendln("---") - to.appendln("") - super.appendNodes(nodes) - } - - protected open fun appendFrontMatter(nodes: Iterable<DocumentationNode>, to: StringBuilder) { - to.appendln("title: ${getPageTitle(nodes)}") - } -} - - -open class JekyllFormatService( - generator: NodeLocationAwareGenerator, - signatureGenerator: LanguageService, - linkExtension: String, - impliedPlatforms: List<String> -) : MarkdownFormatService(generator, signatureGenerator, linkExtension, impliedPlatforms) { - - @Inject constructor( - generator: NodeLocationAwareGenerator, - signatureGenerator: LanguageService, - @Named(impliedPlatformsName) impliedPlatforms: List<String> - ) : this(generator, signatureGenerator, "html", impliedPlatforms) - - override fun createOutputBuilder(to: StringBuilder, location: Location): FormattedOutputBuilder = - JekyllOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) - -} diff --git a/core/src/main/kotlin/Formats/KotlinWebsiteHtmlFormatService.kt b/core/src/main/kotlin/Formats/KotlinWebsiteHtmlFormatService.kt deleted file mode 100644 index 3cdea156..00000000 --- a/core/src/main/kotlin/Formats/KotlinWebsiteHtmlFormatService.kt +++ /dev/null @@ -1,264 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import com.google.inject.name.Named -import org.jetbrains.dokka.Utilities.impliedPlatformsName -import java.io.File - - -object EmptyHtmlTemplateService : HtmlTemplateService { - override fun appendFooter(to: StringBuilder) {} - - override fun appendHeader(to: StringBuilder, title: String?, basePath: File) {} -} - - -open class KotlinWebsiteHtmlOutputBuilder( - to: StringBuilder, - location: Location, - generator: NodeLocationAwareGenerator, - languageService: LanguageService, - extension: String, - val impliedPlatforms: List<String>, - templateService: HtmlTemplateService -) : HtmlOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms, templateService) { - private var needHardLineBreaks = false - private var insideDiv = 0 - - override fun appendLine() {} - - override fun appendBreadcrumbs(path: Iterable<FormatLink>) { - if (path.count() > 1) { - to.append("<div class='api-docs-breadcrumbs'>") - super.appendBreadcrumbs(path) - to.append("</div>") - } - } - - override fun appendSinceKotlin(version: String) { - } - - override fun appendSinceKotlinWrapped(version: String) { - } - - override fun appendCode(body: () -> Unit) = wrapIfNotEmpty("<code>", "</code>", body) - - protected fun div(to: StringBuilder, cssClass: String, otherAttributes: String = "", block: () -> Unit) { - to.append("<div class=\"$cssClass\"$otherAttributes") - to.append(">") - insideDiv++ - block() - insideDiv-- - to.append("</div>\n") - } - - override fun appendAsSignature(node: ContentNode, block: () -> Unit) { - val contentLength = node.textLength - if (contentLength == 0) return - div(to, "signature") { - needHardLineBreaks = contentLength >= 62 - try { - block() - } finally { - needHardLineBreaks = false - } - } - } - - override fun appendAsOverloadGroup(to: StringBuilder, platforms: PlatformsData, block: () -> Unit) { - div(to, "overload-group", calculateDataAttributes(platforms)) { - block() - } - } - - override fun appendLink(href: String, body: () -> Unit) = wrap("<a href=\"$href\">", "</a>", body) - - override fun appendTable(vararg columns: String, body: () -> Unit) { - //to.appendln("<table class=\"api-docs-table\">") - div(to, "api-declarations-list") { - body() - } - //to.appendln("</table>") - } - - override fun appendTableBody(body: () -> Unit) { - //to.appendln("<tbody>") - body() - //to.appendln("</tbody>") - } - - override fun appendTableRow(body: () -> Unit) { - //to.appendln("<tr>") - body() - //to.appendln("</tr>") - } - - override fun appendTableCell(body: () -> Unit) { -// to.appendln("<td>") - body() -// to.appendln("\n</td>") - } - - override fun appendSymbol(text: String) { - to.append("<span class=\"symbol\">${text.htmlEscape()}</span>") - } - - override fun appendKeyword(text: String) { - to.append("<span class=\"keyword\">${text.htmlEscape()}</span>") - } - - override fun appendIdentifier(text: String, kind: IdentifierKind, signature: String?) { - val id = signature?.let { " id=\"$it\"" }.orEmpty() - to.append("<span class=\"${identifierClassName(kind)}\"$id>${text.htmlEscape()}</span>") - } - - override fun appendSoftLineBreak() { - if (needHardLineBreaks) - to.append("<br/>") - } - - override fun appendIndentedSoftLineBreak() { - if (needHardLineBreaks) { - to.append("<br/> ") - } - } - - private fun identifierClassName(kind: IdentifierKind) = when (kind) { - IdentifierKind.ParameterName -> "parameterName" - IdentifierKind.SummarizedTypeName -> "summarizedTypeName" - else -> "identifier" - } - - private data class PlatformsForElement( - val platformToVersion: Map<String, String> - ) - - private fun calculatePlatforms(platforms: PlatformsData): PlatformsForElement { - //val kotlinVersion = platforms.singleOrNull(String::isKotlinVersion)?.removePrefix("Kotlin ") - val jreVersion = platforms.keys.filter(String::isJREVersion).min()?.takeUnless { it.endsWith("6") } - val targetPlatforms = platforms.filterNot { it.key.isJREVersion() } + - listOfNotNull(jreVersion?.let { it to platforms[it]!! }) - - return PlatformsForElement( - targetPlatforms.mapValues { (_, nodes) -> effectiveSinceKotlinForNodes(nodes) } - ) - } - - private fun calculateDataAttributes(platforms: PlatformsData): String { - val platformToVersion = calculatePlatforms(platforms).platformToVersion - val (platformNames, versions) = platformToVersion.toList().unzip() - return "data-platform=\"${platformNames.joinToString()}\" "+ - "data-kotlin-version=\"${versions.joinToString()}\"" - } - - override fun appendIndexRow(platforms: PlatformsData, block: () -> Unit) { -// if (platforms.isNotEmpty()) -// wrap("<tr${calculateDataAttributes(platforms)}>", "</tr>", block) -// else -// appendTableRow(block) - div(to, "declarations", otherAttributes = " ${calculateDataAttributes(platforms)}") { - block() - } - } - - override fun appendPlatforms(platforms: PlatformsData) { - val platformToVersion = calculatePlatforms(platforms).platformToVersion - div(to, "tags") { - div(to, "spacer") {} - platformToVersion.entries.sortedBy { - platformSortWeight(it.key) - }.forEach { (platform, version) -> - div(to, "tags__tag platform tag-value-$platform", - otherAttributes = " data-tag-version=\"$version\"") { - to.append(platform) - } - } - div(to, "tags__tag kotlin-version") { - to.append(mergeVersions(platformToVersion.values.toList())) - } - } - } - - override fun appendAsNodeDescription(platforms: PlatformsData, block: () -> Unit) { - div(to, "node-page-main", otherAttributes = " ${calculateDataAttributes(platforms)}") { - block() - } - - } - - override fun appendBreadcrumbSeparator() { - to.append(" / ") - } - - override fun appendPlatformsAsText(platforms: PlatformsData) { - appendHeader(5) { - val filtered = platforms.keys.filterNot { it.isJREVersion() }.sortedBy { platformSortWeight(it) } - if (filtered.isNotEmpty()) { - to.append("For ") - filtered.joinTo(to) - } - } - } - - override fun appendSampleBlockCode(language: String, imports: () -> Unit, body: () -> Unit) { - div(to, "sample", otherAttributes = " data-min-compiler-version=\"1.3\"") { - appendBlockCode(language) { - imports() - wrap("\n\nfun main(args: Array<String>) {".htmlEscape(), "}") { - wrap("\n//sampleStart\n", "\n//sampleEnd\n", body) - } - } - } - } - - override fun appendSoftParagraph(body: () -> Unit) = appendParagraph(body) - - - override fun appendSectionWithTag(section: ContentSection) { - appendParagraph { - appendStrong { appendText(section.tag) } - appendText(" ") - appendContent(section) - } - } - - override fun appendAsPlatformDependentBlock(platforms: PlatformsData, block: (PlatformsData) -> Unit) { - if (platforms.isNotEmpty()) - wrap("<div ${calculateDataAttributes(platforms)}>", "</div>") { - block(platforms) - } - else - block(platforms) - } - - override fun appendAsSummaryGroup(platforms: PlatformsData, block: (PlatformsData) -> Unit) { - div(to, "summary-group", otherAttributes = " ${calculateDataAttributes(platforms)}") { - block(platforms) - } - } - - fun platformSortWeight(name: String) = when(name.toLowerCase()) { - "common" -> 0 - "jvm" -> 1 - "js" -> 3 - "native" -> 4 - else -> 2 // This is hack to support JRE/JUnit and so on - } -} - -class KotlinWebsiteHtmlFormatService @Inject constructor( - generator: NodeLocationAwareGenerator, - signatureGenerator: LanguageService, - @Named(impliedPlatformsName) impliedPlatforms: List<String>, - templateService: HtmlTemplateService -) : HtmlFormatService(generator, signatureGenerator, templateService, impliedPlatforms) { - - override fun enumerateSupportFiles(callback: (String, String) -> Unit) {} - - override fun createOutputBuilder(to: StringBuilder, location: Location) = - KotlinWebsiteHtmlOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms, templateService) -} - - -private fun String.isKotlinVersion() = this.startsWith("Kotlin") -private fun String.isJREVersion() = this.startsWith("JRE", ignoreCase=true)
\ No newline at end of file diff --git a/core/src/main/kotlin/Formats/MarkdownFormatService.kt b/core/src/main/kotlin/Formats/MarkdownFormatService.kt deleted file mode 100644 index 216dd2ef..00000000 --- a/core/src/main/kotlin/Formats/MarkdownFormatService.kt +++ /dev/null @@ -1,250 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import com.google.inject.name.Named -import org.jetbrains.dokka.Utilities.impliedPlatformsName -import java.util.* - -enum class ListKind { - Ordered, - Unordered -} - -private class ListState(val kind: ListKind, var size: Int = 1) { - fun getTagAndIncrement() = when (kind) { - ListKind.Ordered -> "${size++}. " - else -> "* " - } -} - -private val TWO_LINE_BREAKS = System.lineSeparator() + System.lineSeparator() - -open class MarkdownOutputBuilder(to: StringBuilder, - location: Location, - generator: NodeLocationAwareGenerator, - languageService: LanguageService, - extension: String, - impliedPlatforms: List<String>) - : StructuredOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) -{ - private val listStack = ArrayDeque<ListState>() - protected var inTableCell = false - protected var inCodeBlock = false - private var lastTableCellStart = -1 - private var maxBackticksInCodeBlock = 0 - - private fun appendNewline() { - while (to.endsWith(' ')) { - to.setLength(to.length - 1) - } - to.appendln() - } - - private fun ensureNewline() { - if (inTableCell && listStack.isEmpty()) { - if (to.length != lastTableCellStart && !to.endsWith("<br>")) { - to.append("<br>") - } - } - else { - if (!endsWithNewline()) { - appendNewline() - } - } - } - - private fun endsWithNewline(): Boolean { - var index = to.length - 1 - while (index > 0) { - val c = to[index] - if (c != ' ') { - return c == '\n' - } - index-- - } - return false - } - - override fun ensureParagraph() { - if (!to.endsWith(TWO_LINE_BREAKS)) { - if (!to.endsWith('\n')) { - appendNewline() - } - appendNewline() - } - } - override fun appendBreadcrumbSeparator() { - to.append(" / ") - } - - private val backTickFindingRegex = """(`+)""".toRegex() - - override fun appendText(text: String) { - if (inCodeBlock) { - to.append(text) - val backTicks = backTickFindingRegex.findAll(text) - val longestBackTickRun = backTicks.map { it.value.length }.max() ?: 0 - maxBackticksInCodeBlock = maxBackticksInCodeBlock.coerceAtLeast(longestBackTickRun) - } - else { - if (text == "\n" && inTableCell) { - to.append(" ") - } else { - to.append(text.htmlEscape()) - } - } - } - - override fun appendCode(body: () -> Unit) { - inCodeBlock = true - val codeBlockStart = to.length - maxBackticksInCodeBlock = 0 - - wrapIfNotEmpty("`", "`", body, checkEndsWith = true) - - if (maxBackticksInCodeBlock > 0) { - val extraBackticks = "`".repeat(maxBackticksInCodeBlock) - to.insert(codeBlockStart, extraBackticks) - to.append(extraBackticks) - } - - inCodeBlock = false - } - - override fun appendUnorderedList(body: () -> Unit) { - listStack.push(ListState(ListKind.Unordered)) - body() - listStack.pop() - ensureNewline() - } - - override fun appendOrderedList(body: () -> Unit) { - listStack.push(ListState(ListKind.Ordered)) - body() - listStack.pop() - ensureNewline() - } - - override fun appendListItem(body: () -> Unit) { - ensureNewline() - to.append(listStack.peek()?.getTagAndIncrement()) - body() - ensureNewline() - } - - override fun appendStrong(body: () -> Unit) = wrap("**", "**", body) - override fun appendEmphasis(body: () -> Unit) = wrap("*", "*", body) - override fun appendStrikethrough(body: () -> Unit) = wrap("~~", "~~", body) - - override fun appendLink(href: String, body: () -> Unit) { - if (inCodeBlock) { - wrap("`[`", "`]($href)`", body) - } - else { - wrap("[", "]($href)", body) - } - } - - override fun appendLine() { - if (inTableCell) { - to.append("<br>") - } - else { - appendNewline() - } - } - - override fun appendAnchor(anchor: String) { - // no anchors in Markdown - } - - override fun appendParagraph(body: () -> Unit) { - when { - inTableCell -> { - ensureNewline() - body() - } - listStack.isNotEmpty() -> { - body() - ensureNewline() - } - else -> { - ensureParagraph() - body() - ensureParagraph() - } - } - } - - override fun appendHeader(level: Int, body: () -> Unit) { - when { - inTableCell -> { - body() - } - else -> { - ensureParagraph() - to.append("${"#".repeat(level)} ") - body() - ensureParagraph() - } - } - } - - override fun appendBlockCode(language: String, body: () -> Unit) { - inCodeBlock = true - ensureParagraph() - to.appendln(if (language.isEmpty()) "```" else "``` $language") - body() - ensureNewline() - to.appendln("```") - appendLine() - inCodeBlock = false - } - - override fun appendTable(vararg columns: String, body: () -> Unit) { - ensureParagraph() - body() - ensureParagraph() - } - - override fun appendTableBody(body: () -> Unit) { - body() - } - - override fun appendTableRow(body: () -> Unit) { - to.append("|") - body() - appendNewline() - } - - override fun appendTableCell(body: () -> Unit) { - to.append(" ") - inTableCell = true - lastTableCellStart = to.length - body() - inTableCell = false - to.append(" |") - } - - override fun appendNonBreakingSpace() { - if (inCodeBlock) { - to.append(" ") - } - else { - to.append(" ") - } - } -} - -open class MarkdownFormatService(generator: NodeLocationAwareGenerator, - signatureGenerator: LanguageService, - linkExtension: String, - val impliedPlatforms: List<String>) -: StructuredFormatService(generator, signatureGenerator, "md", linkExtension) { - @Inject constructor(generator: NodeLocationAwareGenerator, - signatureGenerator: LanguageService, - @Named(impliedPlatformsName) impliedPlatforms: List<String>): this(generator, signatureGenerator, "md", impliedPlatforms) - - override fun createOutputBuilder(to: StringBuilder, location: Location): FormattedOutputBuilder = - MarkdownOutputBuilder(to, location, generator, languageService, extension, impliedPlatforms) -} diff --git a/core/src/main/kotlin/Formats/OutlineService.kt b/core/src/main/kotlin/Formats/OutlineService.kt deleted file mode 100644 index 958e93af..00000000 --- a/core/src/main/kotlin/Formats/OutlineService.kt +++ /dev/null @@ -1,29 +0,0 @@ -package org.jetbrains.dokka - -import java.io.File - -/** - * Service for building the outline of the package contents. - */ -interface OutlineFormatService { - fun getOutlineFileName(location: Location): File - - fun appendOutlineHeader(location: Location, node: DocumentationNode, to: StringBuilder) - fun appendOutlineLevel(to: StringBuilder, body: () -> Unit) - - /** Appends formatted outline to [StringBuilder](to) using specified [location] */ - fun appendOutline(location: Location, to: StringBuilder, nodes: Iterable<DocumentationNode>) { - for (node in nodes) { - appendOutlineHeader(location, node, to) - if (node.members.any()) { - val sortedMembers = node.members.sortedBy { it.name.toLowerCase() } - appendOutlineLevel(to) { - appendOutline(location, to, sortedMembers) - } - } - } - } - - fun formatOutline(location: Location, nodes: Iterable<DocumentationNode>): String = - StringBuilder().apply { appendOutline(location, this, nodes) }.toString() -} diff --git a/core/src/main/kotlin/Formats/PackageListService.kt b/core/src/main/kotlin/Formats/PackageListService.kt deleted file mode 100644 index e675d927..00000000 --- a/core/src/main/kotlin/Formats/PackageListService.kt +++ /dev/null @@ -1,66 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject - - -interface PackageListService { - fun formatPackageList(module: DocumentationModule): String -} - -class DefaultPackageListService @Inject constructor( - val generator: NodeLocationAwareGenerator, - val formatService: FormatService -) : PackageListService { - - override fun formatPackageList(module: DocumentationModule): String { - val packages = mutableSetOf<String>() - val nonStandardLocations = mutableMapOf<String, String>() - - fun visit(node: DocumentationNode, relocated: Boolean = false) { - val nodeKind = node.kind - - when (nodeKind) { - NodeKind.Package -> { - packages.add(node.qualifiedName()) - node.members.forEach { visit(it) } - } - NodeKind.Signature -> { - if (relocated) - nonStandardLocations[node.name] = generator.relativePathToLocation(module, node.owner!!) - } - NodeKind.ExternalClass -> { - node.members.forEach { visit(it, relocated = true) } - } - NodeKind.GroupNode -> { - if (node.members.isNotEmpty()) { - // Only nodes only has single file is need to be relocated - // TypeAliases for example - node.origins - .filter { it.members.isEmpty() } - .forEach { visit(it, relocated = true) } - } - } - else -> { - if (nodeKind in NodeKind.classLike || nodeKind in NodeKind.memberLike) { - node.details(NodeKind.Signature).forEach { visit(it, relocated) } - node.members.forEach { visit(it, relocated) } - } - } - } - } - - module.members.forEach { visit(it) } - - return buildString { - appendln("\$dokka.linkExtension:${formatService.linkExtension}") - - nonStandardLocations.map { (signature, location) -> "\$dokka.location:$signature\u001f$location" } - .sorted().joinTo(this, separator = "\n", postfix = "\n") - - packages.sorted().joinTo(this, separator = "\n", postfix = "\n") - } - - } - -} - diff --git a/core/src/main/kotlin/Formats/StandardFormats.kt b/core/src/main/kotlin/Formats/StandardFormats.kt deleted file mode 100644 index 86f70a37..00000000 --- a/core/src/main/kotlin/Formats/StandardFormats.kt +++ /dev/null @@ -1,55 +0,0 @@ -package org.jetbrains.dokka.Formats - -import com.google.inject.Binder -import org.jetbrains.dokka.* -import org.jetbrains.dokka.Samples.KotlinWebsiteSampleProcessingService -import org.jetbrains.dokka.Utilities.bind -import kotlin.reflect.KClass - -abstract class KotlinFormatDescriptorBase - : FileGeneratorBasedFormatDescriptor(), - DefaultAnalysisComponent, - DefaultAnalysisComponentServices by KotlinAsKotlin { - override val generatorServiceClass = FileGenerator::class - override val outlineServiceClass: KClass<out OutlineFormatService>? = null - override val packageListServiceClass: KClass<out PackageListService>? = DefaultPackageListService::class -} - -abstract class HtmlFormatDescriptorBase : FileGeneratorBasedFormatDescriptor(), DefaultAnalysisComponent { - override val formatServiceClass = HtmlFormatService::class - override val outlineServiceClass = HtmlFormatService::class - override val generatorServiceClass = FileGenerator::class - override val packageListServiceClass = DefaultPackageListService::class - - override fun configureOutput(binder: Binder): Unit = with(binder) { - super.configureOutput(binder) - bind<HtmlTemplateService>().toProvider { HtmlTemplateService.default("style.css") } - } -} - -class HtmlFormatDescriptor : HtmlFormatDescriptorBase(), DefaultAnalysisComponentServices by KotlinAsKotlin - -class HtmlAsJavaFormatDescriptor : HtmlFormatDescriptorBase(), DefaultAnalysisComponentServices by KotlinAsJava - -class KotlinWebsiteHtmlFormatDescriptor : KotlinFormatDescriptorBase() { - override val formatServiceClass = KotlinWebsiteHtmlFormatService::class - override val sampleProcessingService = KotlinWebsiteSampleProcessingService::class - override val outlineServiceClass = YamlOutlineService::class - - override fun configureOutput(binder: Binder) = with(binder) { - super.configureOutput(binder) - bind<HtmlTemplateService>().toInstance(EmptyHtmlTemplateService) - } -} - -class JekyllFormatDescriptor : KotlinFormatDescriptorBase() { - override val formatServiceClass = JekyllFormatService::class -} - -class MarkdownFormatDescriptor : KotlinFormatDescriptorBase() { - override val formatServiceClass = MarkdownFormatService::class -} - -class GFMFormatDescriptor : KotlinFormatDescriptorBase() { - override val formatServiceClass = GFMFormatService::class -} diff --git a/core/src/main/kotlin/Formats/StructuredFormatService.kt b/core/src/main/kotlin/Formats/StructuredFormatService.kt deleted file mode 100644 index 76f9366f..00000000 --- a/core/src/main/kotlin/Formats/StructuredFormatService.kt +++ /dev/null @@ -1,1014 +0,0 @@ -package org.jetbrains.dokka - -import org.jetbrains.dokka.LanguageService.RenderMode -import org.jetbrains.kotlin.utils.keysToMap -import java.util.* - -data class FormatLink(val text: String, val href: String) - -private data class Summarized( - val data: List<SummarizedBySummary> -) { - - constructor(data: Map<ContentNode, Map<ContentNode, List<DocumentationNode>>>) : this( - data.entries.map { (summary, signatureToMember) -> - SummarizedBySummary( - summary, - signatureToMember.map { (signature, nodes) -> - SummarizedNodes(signature, nodes) - } - ) - } - ) - - data class SummarizedNodes(val content: ContentNode, val nodes: List<DocumentationNode>) { - val platforms = effectivePlatformsForMembers(nodes) - } - data class SummarizedBySummary(val content: ContentNode, val signatures: List<SummarizedNodes>) { - val platforms = effectivePlatformsForMembers(signatures.flatMap { it.nodes }) - val platformsOnSignature = !samePlatforms(signatures.map { it.platforms }) - } - - - fun computePlatformLevel(): PlatformPlacement { - if (data.any { it.platformsOnSignature }) { - return PlatformPlacement.Signature - } - if (samePlatforms(data.map { it.platforms })) { - return PlatformPlacement.Row - } - return PlatformPlacement.Summary - } - val platformPlacement: PlatformPlacement = computePlatformLevel() - val platforms = effectivePlatformsForMembers(data.flatMap { it.signatures.flatMap { it.nodes } }) - - - enum class PlatformPlacement { - Row, Summary, Signature - } -} - -abstract class StructuredOutputBuilder(val to: StringBuilder, - val location: Location, - val generator: NodeLocationAwareGenerator, - val languageService: LanguageService, - val extension: String, - impliedPlatforms: List<String>) : FormattedOutputBuilder { - - protected fun wrap(prefix: String, suffix: String, body: () -> Unit) { - to.append(prefix) - body() - to.append(suffix) - } - - protected fun wrapIfNotEmpty(prefix: String, suffix: String, body: () -> Unit, checkEndsWith: Boolean = false) { - val startLength = to.length - to.append(prefix) - body() - if (checkEndsWith && to.endsWith(suffix)) { - to.setLength(to.length - suffix.length) - } else if (to.length > startLength + prefix.length) { - to.append(suffix) - } else { - to.setLength(startLength) - } - } - - protected fun wrapInTag(tag: String, - body: () -> Unit, - newlineBeforeOpen: Boolean = false, - newlineAfterOpen: Boolean = false, - newlineAfterClose: Boolean = false) { - if (newlineBeforeOpen && !to.endsWith('\n')) to.appendln() - to.append("<$tag>") - if (newlineAfterOpen) to.appendln() - body() - to.append("</$tag>") - if (newlineAfterClose) to.appendln() - } - - protected abstract fun ensureParagraph() - - open fun appendSampleBlockCode(language: String, imports: () -> Unit, body: () -> Unit) = appendBlockCode(language, body) - abstract fun appendBlockCode(language: String, body: () -> Unit) - abstract fun appendHeader(level: Int = 1, body: () -> Unit) - abstract fun appendParagraph(body: () -> Unit) - - open fun appendSoftParagraph(body: () -> Unit) { - ensureParagraph() - body() - } - - abstract fun appendLine() - abstract fun appendAnchor(anchor: String) - - abstract fun appendTable(vararg columns: String, body: () -> Unit) - abstract fun appendTableBody(body: () -> Unit) - abstract fun appendTableRow(body: () -> Unit) - abstract fun appendTableCell(body: () -> Unit) - - abstract fun appendText(text: String) - - open fun appendSinceKotlin(version: String) { - appendText("Since: ") - appendCode { appendText(version) } - } - - open fun appendSinceKotlinWrapped(version: String) { - wrap(" (", ")") { - appendSinceKotlin(version) - } - } - - open fun appendSectionWithTag(section: ContentSection) { - appendParagraph { - appendStrong { appendText(section.tag) } - appendLine() - appendContent(section) - } - } - - open fun appendAsPlatformDependentBlock(platforms: PlatformsData, block: (PlatformsData) -> Unit) { - block(platforms) - } - - open fun appendAsSummaryGroup(platforms: PlatformsData, block: (PlatformsData) -> Unit) { - appendAsPlatformDependentBlock(platforms, block) - } - - open fun appendSymbol(text: String) { - appendText(text) - } - - open fun appendKeyword(text: String) { - appendText(text) - } - - open fun appendIdentifier(text: String, kind: IdentifierKind, signature: String?) { - appendText(text) - } - - open fun appendAsNodeDescription(platforms: PlatformsData, block: () -> Unit) { - block() - } - - fun appendEntity(text: String) { - to.append(text) - } - - abstract fun appendLink(href: String, body: () -> Unit) - - open fun appendLink(link: FormatLink) { - appendLink(link.href) { appendText(link.text) } - } - - abstract fun appendStrong(body: () -> Unit) - abstract fun appendStrikethrough(body: () -> Unit) - abstract fun appendEmphasis(body: () -> Unit) - abstract fun appendCode(body: () -> Unit) - abstract fun appendUnorderedList(body: () -> Unit) - abstract fun appendOrderedList(body: () -> Unit) - abstract fun appendListItem(body: () -> Unit) - - abstract fun appendBreadcrumbSeparator() - abstract fun appendNonBreakingSpace() - open fun appendSoftLineBreak() { - } - - open fun appendIndentedSoftLineBreak() { - } - - fun appendContent(content: List<ContentNode>) { - for (contentNode in content) { - appendContent(contentNode) - } - } - - open fun appendContent(content: ContentNode) { - when (content) { - is ContentText -> appendText(content.text) - is ContentSymbol -> appendSymbol(content.text) - is ContentKeyword -> appendKeyword(content.text) - is ContentIdentifier -> appendIdentifier(content.text, content.kind, content.signature) - is ContentNonBreakingSpace -> appendNonBreakingSpace() - is ContentSoftLineBreak -> appendSoftLineBreak() - is ContentIndentedSoftLineBreak -> appendIndentedSoftLineBreak() - is ContentEntity -> appendEntity(content.text) - is ContentStrong -> appendStrong { appendContent(content.children) } - is ContentStrikethrough -> appendStrikethrough { appendContent(content.children) } - is ContentCode -> appendCode { appendContent(content.children) } - is ContentEmphasis -> appendEmphasis { appendContent(content.children) } - is ContentUnorderedList -> appendUnorderedList { appendContent(content.children) } - is ContentOrderedList -> appendOrderedList { appendContent(content.children) } - is ContentListItem -> appendListItem { - val child = content.children.singleOrNull() - if (child is ContentParagraph) { - appendContent(child.children) - } else { - appendContent(content.children) - } - } - - is NodeRenderContent -> { - val node = content.node - appendContent(languageService.render(node, content.mode)) - } - is ContentNodeLink -> { - val node = content.node - val linkTo = if (node != null) locationHref(location, node, generator) else "#" - appendLinkIfNotThisPage(linkTo, content) - } - is ContentExternalLink -> appendLinkIfNotThisPage(content.href, content) - - is ContentParagraph -> { - if (!content.isEmpty()) { - appendParagraph { appendContent(content.children) } - } - } - - is ContentBlockSampleCode, is ContentBlockCode -> { - content as ContentBlockCode - fun ContentBlockCode.appendBlockCodeContent() { - children - .dropWhile { it is ContentText && it.text.isBlank() } - .forEach { appendContent(it) } - } - when (content) { - is ContentBlockSampleCode -> - appendSampleBlockCode(content.language, content.importsBlock::appendBlockCodeContent) { content.appendBlockCodeContent() } - is ContentBlockCode -> - appendBlockCode(content.language) { content.appendBlockCodeContent() } - } - } - is ContentHeading -> appendHeader(content.level) { appendContent(content.children) } - is ContentBlock -> appendContent(content.children) - } - } - - private fun appendLinkIfNotThisPage(href: String, content: ContentBlock) { - if (href == ".") { - appendContent(content.children) - } else { - appendLink(href) { appendContent(content.children) } - } - } - - open fun link( - from: DocumentationNode, - to: DocumentationNode, - name: (DocumentationNode) -> String = DocumentationNode::name - ): FormatLink = link(from, to, extension, name) - - open fun link( - from: DocumentationNode, - to: DocumentationNode, - extension: String, - name: (DocumentationNode) -> String = DocumentationNode::name - ): FormatLink = - FormatLink(name(to), generator.relativePathToLocation(from, to)) - - private fun DocumentationNode.isModuleOrPackage(): Boolean = - kind == NodeKind.Module || kind == NodeKind.Package - - protected open fun appendAsSignature(node: ContentNode, block: () -> Unit) { - block() - } - - protected open fun appendAsOverloadGroup(to: StringBuilder, platforms: PlatformsData, block: () -> Unit) { - block() - } - - protected open fun appendIndexRow(platforms: PlatformsData, block: () -> Unit) { - appendTableRow(block) - } - - protected open fun appendPlatformsAsText(platforms: PlatformsData) { - appendPlatforms(platforms) - } - - protected open fun appendPlatforms(platforms: PlatformsData) { - if (platforms.isNotEmpty()) { - appendText(platforms.keys.joinToString(prefix = "(", postfix = ") ")) - } - } - - protected open fun appendBreadcrumbs(path: Iterable<FormatLink>) { - for ((index, item) in path.withIndex()) { - if (index > 0) { - appendBreadcrumbSeparator() - } - appendLink(item) - } - } - - fun Content.getSectionsWithSubjects(): Map<String, List<ContentSection>> = - sections.filter { it.subjectName != null }.groupBy { it.tag } - - private fun ContentNode.appendSignature() { - if (this is ContentBlock && this.isEmpty()) { - return - } - - val signatureAsCode = ContentCode() - signatureAsCode.append(this) - appendContent(signatureAsCode) - } - - open inner class PageBuilder(val nodes: Iterable<DocumentationNode>, val noHeader: Boolean = false) { - open fun build() { - val breakdownByLocation = nodes.groupBy { node -> - node.path.filterNot { it.name.isEmpty() }.map { link(node, it) }.distinct() - } - - for ((path, nodes) in breakdownByLocation) { - if (!noHeader && path.isNotEmpty()) { - appendBreadcrumbs(path) - appendLine() - appendLine() - } - appendLocation(nodes.filter { it.kind != NodeKind.ExternalClass }) - } - } - - private fun appendLocation(nodes: Iterable<DocumentationNode>) { - val singleNode = nodes.singleOrNull() - if (singleNode != null && singleNode.isModuleOrPackage()) { - if (singleNode.kind == NodeKind.Package) { - val packageName = if (singleNode.name.isEmpty()) "<root>" else singleNode.name - appendHeader(2) { appendText("Package $packageName") } - } - appendContent(singleNode.content) - } else { - val breakdownByName = nodes.groupBy { node -> node.name } - for ((name, items) in breakdownByName) { - if (!noHeader) - appendHeader { appendText(name) } - appendDocumentation(items, singleNode != null) - } - } - } - - private fun appendDocumentation(overloads: Iterable<DocumentationNode>, isSingleNode: Boolean) { - val breakdownBySummary = overloads.groupByTo(LinkedHashMap()) { node -> - when (node.kind) { - NodeKind.GroupNode -> node.origins.map { it.content } - else -> node.content - } - } - - if (breakdownBySummary.size == 1) { - val node = breakdownBySummary.values.single() - appendAsNodeDescription(effectivePlatformsForMembers(node)) { - formatOverloadGroup(node, isSingleNode) - } - } else { - for ((_, items) in breakdownBySummary) { - appendAsOverloadGroup(to, effectivePlatformsForMembers(items)) { - formatOverloadGroup(items) - } - } - } - } - - private fun formatOverloadGroup(items: List<DocumentationNode>, isSingleNode: Boolean = false) { - - val platformsPerGroup = samePlatforms( - items.flatMap { - if (it.kind == NodeKind.GroupNode) { - it.origins.groupBy { origin -> - languageService.render(origin) - }.values.map { origins -> effectivePlatformsForMembers(origins) } - } else { - listOf(effectivePlatformsForNode(it)) - } - } - ) - - if (platformsPerGroup) { - appendAsPlatformDependentBlock(effectivePlatformsForMembers(items)) { platforms -> - appendPlatforms(platforms) - } - } - for ((index, item) in items.withIndex()) { - if (index > 0) appendLine() - - if (item.kind == NodeKind.GroupNode) { - renderGroupNode(item, isSingleNode, !platformsPerGroup) - } else { - renderSimpleNode(item, isSingleNode, !platformsPerGroup) - } - - } - // All items have exactly the same documentation, so we can use any item to render it - val item = items.first() - // TODO: remove this block cause there is no one node with OverloadGroupNote detail - item.details(NodeKind.OverloadGroupNote).forEach { - appendContent(it.content) - } - - if (item.kind == NodeKind.GroupNode) { - val groupByContent = item.origins.groupBy { it.content } - if (groupByContent.count { !it.key.isEmpty() } > 1) { - if (groupByContent.size > 1) println("[mult] Found ov diff: ${generator.location(item).path}") - } - for ((content, origins) in groupByContent) { - if (content.isEmpty()) continue - appendAsPlatformDependentBlock(effectivePlatformsForMembers(origins)) { platforms -> - if (groupByContent.count { !it.key.isEmpty() } > 1) { - appendPlatformsAsText(platforms) - } - appendContent(content.summary) - content.appendDescription() - } - } - } else { - val platforms = effectivePlatformsForNode(item) - appendAsPlatformDependentBlock(platforms) { - appendContent(item.summary) - item.content.appendDescription() - } - } - } - - - fun renderSimpleNode(item: DocumentationNode, isSingleNode: Boolean, withPlatforms: Boolean = true) { - appendAsPlatformDependentBlock(effectivePlatformsForMembers(listOf(item))) { platforms -> - // TODO: use summarizesignatures - val rendered = languageService.render(item) - item.detailOrNull(NodeKind.Signature)?.let { - if (item.kind !in NodeKind.classLike || !isSingleNode) - appendAnchor(it.name) - } - if (withPlatforms) { - appendPlatforms(platforms) - } - appendAsSignature(rendered) { - appendCode { appendContent(rendered) } - item.appendSourceLink() - } - item.appendOverrides() - item.appendDeprecation() - } - } - - fun renderGroupNode(item: DocumentationNode, isSingleNode: Boolean, withPlatforms: Boolean = true) { - // TODO: use summarizesignatures - val groupBySignature = item.origins.groupBy { - languageService.render(it) - } - - for ((sign, nodes) in groupBySignature) { - appendAsPlatformDependentBlock(effectivePlatformsForMembers(nodes)) { platforms -> - val first = nodes.first() - first.detailOrNull(NodeKind.Signature)?.let { - if (item.kind !in NodeKind.classLike || !isSingleNode) - appendAnchor(it.name) - } - - if (withPlatforms) { - appendPlatforms(platforms) - } - - appendAsSignature(sign) { - appendCode { appendContent(sign) } - } - first.appendOverrides() - first.appendDeprecation() - } - - } - } - - private fun DocumentationNode.appendSourceLink() { - val sourceUrl = details(NodeKind.SourceUrl).firstOrNull() - if (sourceUrl != null) { - to.append(" ") - appendLink(sourceUrl.name) { to.append("(source)") } - } - } - - private fun DocumentationNode.appendOverrides() { - overrides.forEach { - appendParagraph { - to.append("Overrides ") - val location = generator.relativePathToLocation(this, it) - - appendLink(FormatLink(it.owner!!.name + "." + it.name, location)) - } - } - } - - private fun DocumentationNode.appendDeprecation() { - if (deprecation != null) { - val deprecationParameter = deprecation!!.details(NodeKind.Parameter).firstOrNull() - val deprecationValue = deprecationParameter?.details(NodeKind.Value)?.firstOrNull() - appendLine() - when { - deprecationValue != null -> { - appendStrong { to.append("Deprecated:") } - appendText(" " + deprecationValue.name.removeSurrounding("\"")) - appendLine() - appendLine() - } - deprecation?.content != Content.Empty -> { - appendStrong { to.append("Deprecated:") } - to.append(" ") - appendContent(deprecation!!.content) - } - else -> { - appendStrong { to.append("Deprecated") } - appendLine() - appendLine() - } - } - } - } - - -// protected fun platformsOfItems(items: List<DocumentationNode>): Set<String> { -// val platforms = items.asSequence().map { -// when (it.kind) { -// NodeKind.ExternalClass, NodeKind.Package, NodeKind.Module -> platformsOfItems(it.members) -// NodeKind.GroupNode -> platformsOfItems(it.origins) -// else -> it.platformsToShow.toSet() -// } -// } -// -// fun String.isKotlinVersion() = this.startsWith("Kotlin") -// -// if (platforms.count() == 0) return emptySet() -// -// // Calculating common platforms for items -// return platforms.reduce { result, platformsOfItem -> -// val otherKotlinVersion = result.find { it.isKotlinVersion() } -// val (kotlinVersions, otherPlatforms) = platformsOfItem.partition { it.isKotlinVersion() } -// -// // When no Kotlin version specified, it means that version is 1.0 -// if (otherKotlinVersion != null && kotlinVersions.isNotEmpty()) { -// result.intersect(platformsOfItem) + mergeVersions(otherKotlinVersion, kotlinVersions) -// } else { -// result.intersect(platformsOfItem) -// } -// } -// } -// -// protected fun unionPlatformsOfItems(items: List<DocumentationNode>): Set<String> { -// val platforms = items.asSequence().map { -// when (it.kind) { -// NodeKind.GroupNode -> unionPlatformsOfItems(it.origins) -// else -> it.platformsToShow.toSet() -// } -// } -// -// fun String.isKotlinVersion() = this.startsWith("Kotlin") -// -// if (platforms.count() == 0) return emptySet() -// -// // Calculating common platforms for items -// return platforms.reduce { result, platformsOfItem -> -// val otherKotlinVersion = result.find { it.isKotlinVersion() } -// val (kotlinVersions, otherPlatforms) = platformsOfItem.partition { it.isKotlinVersion() } -// -// // When no Kotlin version specified, it means that version is 1.0 -// if (otherKotlinVersion != null && kotlinVersions.isNotEmpty()) { -// result.union(otherPlatforms) + mergeVersions(otherKotlinVersion, kotlinVersions) -// } else { -// result.union(otherPlatforms) -// } -// } -// } - -// val DocumentationNode.platformsToShow: List<String> -// get() = platforms - - private fun Content.appendDescription() { - if (description != ContentEmpty) { - appendContent(description) - } - - - getSectionsWithSubjects().forEach { - appendSectionWithSubject(it.key, it.value) - } - - for (section in sections.filter { it.subjectName == null }) { - appendSectionWithTag(section) - } - } - - fun appendSectionWithSubject(title: String, subjectSections: List<ContentSection>) { - appendHeader(3) { appendText(title) } - subjectSections.forEach { - val subjectName = it.subjectName - if (subjectName != null) { - appendSoftParagraph { - appendAnchor(subjectName) - appendCode { to.append(subjectName) } - to.append(" - ") - appendContent(it) - } - } - } - } - - fun appendOriginsGroupByContent(node: DocumentationNode) { - require(node.kind == NodeKind.GroupNode) - val groupByContent = - node.origins.groupBy { it.content } - .mapValues { (_, origins) -> - effectivePlatformsForMembers(origins) - } - .filterNot { it.key.isEmpty() } - .toList() - .sortedByDescending { it.second.size } - - if (groupByContent.size > 1) println("[mult] Found diff: ${generator.location(node).path}") - for ((content, platforms) in groupByContent) { - appendAsPlatformDependentBlock(platforms) { - if (groupByContent.size > 1) { - appendPlatformsAsText(platforms) - } - appendContent(content.summary) - content.appendDescription() - } - } - } - } - - inner class SingleNodePageBuilder(val node: DocumentationNode, noHeader: Boolean = false) : - PageBuilder(listOf(node), noHeader) { - - override fun build() { - super.build() - SectionsBuilder(node).build() - } - } - - inner class GroupNodePageBuilder(val node: DocumentationNode) : PageBuilder(listOf(node)) { - - override fun build() { - val breakdownByLocation = node.path.filterNot { it.name.isEmpty() }.map { link(node, it) } - - appendBreadcrumbs(breakdownByLocation) - appendLine() - appendLine() - appendHeader { appendText(node.name) } - - appendAsNodeDescription(effectivePlatformsForNode(node)) { - renderGroupNode(node, true) - - appendOriginsGroupByContent(node) - } - - SectionsBuilder(node).build() - } - } -// -// private fun unionPlatformsOfItems(items: List<DocumentationNode>): Set<String> { -// val platforms = items.flatMapTo(mutableSetOf<String>()) { -// when (it.kind) { -// NodeKind.GroupNode -> unionPlatformsOfItems(it.origins) -// else -> it.platforms -// } -// } -// -// return platforms -// } - - - inner class SectionsBuilder(val node: DocumentationNode): PageBuilder(listOf(node)) { - override fun build() { - if (node.kind == NodeKind.ExternalClass) { - appendSection("Extensions for ${node.name}", node.members) - return - } - - fun DocumentationNode.membersOrGroupMembers(predicate: (DocumentationNode) -> Boolean): List<DocumentationNode> { - return members.filter(predicate) + members(NodeKind.GroupNode).filter{ it.origins.isNotEmpty() && predicate(it.origins.first()) } - } - - fun DocumentationNode.membersOrGroupMembers(kind: NodeKind): List<DocumentationNode> { - return membersOrGroupMembers { it.kind == kind } - } - - appendSection("Packages", node.members(NodeKind.Package), platformsBasedOnMembers = true) - appendSection("Types", node.membersOrGroupMembers { it.kind in NodeKind.classLike /*&& it.kind != NodeKind.TypeAlias*/ && it.kind != NodeKind.AnnotationClass && it.kind != NodeKind.Exception }) - appendSection("Annotations", node.membersOrGroupMembers(NodeKind.AnnotationClass)) - appendSection("Exceptions", node.membersOrGroupMembers(NodeKind.Exception)) - appendSection("Extensions for External Classes", node.members(NodeKind.ExternalClass)) - appendSection("Enum Values", node.membersOrGroupMembers(NodeKind.EnumItem), sortMembers = false, omitSamePlatforms = true) - appendSection("Constructors", node.membersOrGroupMembers(NodeKind.Constructor), omitSamePlatforms = true) - appendSection("Properties", node.membersOrGroupMembers(NodeKind.Property), omitSamePlatforms = true) - appendSection("Inherited Properties", node.inheritedMembers(NodeKind.Property)) - appendSection("Functions", node.membersOrGroupMembers(NodeKind.Function), omitSamePlatforms = true) - appendSection("Inherited Functions", node.inheritedMembers(NodeKind.Function)) - appendSection("Companion Object Properties", node.membersOrGroupMembers(NodeKind.CompanionObjectProperty), omitSamePlatforms = true) - appendSection("Inherited Companion Object Properties", node.inheritedCompanionObjectMembers(NodeKind.Property)) - appendSection("Companion Object Functions", node.membersOrGroupMembers(NodeKind.CompanionObjectFunction), omitSamePlatforms = true) - appendSection("Inherited Companion Object Functions", node.inheritedCompanionObjectMembers(NodeKind.Function)) - appendSection("Other members", node.members.filter { - it.kind !in setOf( - NodeKind.Class, - NodeKind.Interface, - NodeKind.Enum, - NodeKind.Object, - NodeKind.AnnotationClass, - NodeKind.Exception, - NodeKind.TypeAlias, - NodeKind.Constructor, - NodeKind.Property, - NodeKind.Package, - NodeKind.Function, - NodeKind.CompanionObjectProperty, - NodeKind.CompanionObjectFunction, - NodeKind.ExternalClass, - NodeKind.EnumItem, - NodeKind.AllTypes, - NodeKind.GroupNode - ) - }) - - val allExtensions = node.extensions - appendSection("Extension Properties", allExtensions.filter { it.kind == NodeKind.Property }) - appendSection("Extension Functions", allExtensions.filter { it.kind == NodeKind.Function }) - appendSection("Companion Object Extension Properties", allExtensions.filter { it.kind == NodeKind.CompanionObjectProperty }) - appendSection("Companion Object Extension Functions", allExtensions.filter { it.kind == NodeKind.CompanionObjectFunction }) - appendSection("Inheritors", - node.inheritors.filter { it.kind != NodeKind.EnumItem }) - - if (node.kind == NodeKind.Module) { - appendHeader(3) { to.append("Index") } - node.members(NodeKind.AllTypes).singleOrNull()?.let { allTypes -> - appendLink(link(node, allTypes) { "All Types" }) - } - } - } - - private fun appendSection(caption: String, members: List<DocumentationNode>, - sortMembers: Boolean = true, - omitSamePlatforms: Boolean = false, - platformsBasedOnMembers: Boolean = false) { - if (members.isEmpty()) return - - appendHeader(3) { appendText(caption) } - - val children = if (sortMembers) members.sortedBy { it.name.toLowerCase() } else members - val membersMap = children.groupBy { link(node, it) } - - - - appendTable("Name", "Summary") { - appendTableBody { - for ((memberLocation, membersList) in membersMap) { - val platforms = effectivePlatformsForMembers(membersList) -// val platforms = if (platformsBasedOnMembers) -// members.flatMapTo(mutableSetOf()) { platformsOfItems(it.members) } + elementPlatforms -// else -// elementPlatforms - - val summarized = computeSummarySignatures(membersList) - - appendIndexRow(platforms) { - appendTableCell { - if (summarized.platformPlacement == Summarized.PlatformPlacement.Row) { - appendPlatforms(platforms) - } - appendHeader(level = 4) { - // appendParagraph { - appendLink(memberLocation) - } - if (node.sinceKotlin != null) { - appendSinceKotlin(node.sinceKotlin.toString()) - } - - if (membersList.singleOrNull()?.sinceKotlin != null){ - appendSinceKotlinWrapped(membersList.single().sinceKotlin.toString()) - } -// } -// if (members.singleOrNull()?.kind != NodeKind.ExternalClass) { -// appendPlatforms(platforms) -// } -// } - } - appendTableCell { - appendSummarySignatures(summarized) - } - } - } - } - } - } - -// -// private fun platformsOfItems(items: List<DocumentationNode>, omitSamePlatforms: Boolean = true): Set<String> { -// if (items.all { it.kind != NodeKind.Package && it.kind != NodeKind.Module && it.kind != NodeKind.ExternalClass }) { -// return unionPlatformsOfItems(items) -// } -// -// val platforms = platformsOfItems(items) -// if (platforms.isNotEmpty() && (platforms != node.platformsToShow.toSet() || !omitSamePlatforms)) { -// return platforms -// } -// return emptySet() -// } - - - - private fun computeSummarySignatures(items: List<DocumentationNode>): Summarized = - Summarized(items.groupBy { it.summary }.mapValues { (_, nodes) -> - val nodesToAppend = nodes.flatMap { if(it.kind == NodeKind.GroupNode) it.origins else listOf(it) } - - val summarySignature = languageService.summarizeSignatures(nodesToAppend) - if (summarySignature != null) { - mapOf(summarySignature to nodesToAppend) - } else { - nodesToAppend.groupBy { - languageService.render(it, RenderMode.SUMMARY) - } - } - }) - - - private fun appendSummarySignatures( - summarized: Summarized - ) { - for(summary in summarized.data) { - - appendAsSummaryGroup(summary.platforms) { - if (summarized.platformPlacement == Summarized.PlatformPlacement.Summary) { - appendPlatforms(summary.platforms) - } - appendContent(summary.content) - summary.signatures.subList(0, summary.signatures.size - 1).forEach { - appendSignatures( - it, - summarized.platformPlacement == Summarized.PlatformPlacement.Signature - ) - appendLine() - } - appendSignatures( - summary.signatures.last(), - summarized.platformPlacement == Summarized.PlatformPlacement.Signature - ) - } - - } - } - - private fun appendSignatures( - signature: Summarized.SummarizedNodes, - withPlatforms: Boolean - ) { - -// val platforms = if (platformsBasedOnMembers) -// items.flatMapTo(mutableSetOf()) { platformsOfItems(it.members) } + elementPlatforms -// else -// elementPlatforms - - - appendAsPlatformDependentBlock(signature.platforms) { - if (withPlatforms) { - appendPlatforms(signature.platforms) - } - appendAsSignature(signature.content) { - signature.content.appendSignature() - } - appendSoftLineBreak() - } - } - } - - private fun DocumentationNode.isClassLikeGroupNode(): Boolean { - if (kind != NodeKind.GroupNode) { - return false - } - - return origins.all { it.kind in NodeKind.classLike } - } - - - inner class AllTypesNodeBuilder(val node: DocumentationNode) - : PageBuilder(listOf(node)) { - - override fun build() { - appendContent(node.owner!!.summary) - appendHeader(3) { to.append("All Types") } - - appendTable("Name", "Summary") { - appendTableBody { - for (type in node.members) { - val platforms = effectivePlatformsForNode(type) - appendIndexRow(platforms) { - appendPlatforms(platforms) - if (type.kind == NodeKind.ExternalClass) { - val packageName = type.owner?.name - if (packageName != null) { - appendText(" (extensions in package $packageName)") - } - } - appendHeader(level = 5) { - appendLink(link(node, type) { - if (it.kind == NodeKind.ExternalClass) it.name else it.qualifiedName() - }) - } - - appendContent(type.summary) - } - } - } - } - } - } - - override fun appendNodes(nodes: Iterable<DocumentationNode>) { - val singleNode = nodes.singleOrNull() - when (singleNode?.kind) { - NodeKind.AllTypes -> AllTypesNodeBuilder(singleNode).build() - NodeKind.GroupNode -> GroupNodePageBuilder(singleNode).build() - null -> PageBuilder(nodes).build() - else -> SingleNodePageBuilder(singleNode).build() - } - } -} - -abstract class StructuredFormatService(val generator: NodeLocationAwareGenerator, - val languageService: LanguageService, - override val extension: String, - override final val linkExtension: String = extension) : FormatService { - -} - -typealias PlatformsData = Map<String, Set<DocumentationNode>> - -fun memberPlatforms(node: DocumentationNode): PlatformsData { - val members = when { - node.kind == NodeKind.GroupNode -> node.origins - node.kind in NodeKind.classLike -> emptyList() - node.kind in NodeKind.memberLike -> emptyList() - else -> node.members - } - - return members.map(::effectivePlatformsForNode).fold(mapOf(), ::mergePlatforms) -} - -fun mergePlatforms(a: PlatformsData, b: PlatformsData): PlatformsData { - val mutable = a.toMutableMap() - b.forEach { (name, declarations) -> - mutable.merge(name, declarations) { a, b -> a.union(b) } - } - return mutable -} - -fun effectivePlatformsForNode(node: DocumentationNode): PlatformsData { - val platforms = node.platforms + memberPlatforms(node).keys - return platforms.keysToMap { setOf(node) } -} - -fun effectivePlatformsForMembers(nodes: Collection<DocumentationNode>): PlatformsData { - return nodes.map { effectivePlatformsForNode(it) }.reduce(::mergePlatforms) -} - -fun mergeVersions(kotlinVersions: List<String>): String { - return kotlinVersions.distinct().min().orEmpty() -} - -fun effectiveSinceKotlinForNode(node: DocumentationNode, baseVersion: String = "1.0"): String { - val members = when { - node.kind == NodeKind.GroupNode -> node.origins - node.kind in NodeKind.classLike -> emptyList() - node.kind in NodeKind.memberLike -> emptyList() - else -> node.members - } - val newBase = node.sinceKotlin ?: baseVersion - val memberVersion = if (members.isNotEmpty()) effectiveSinceKotlinForNodes(members, newBase) else newBase - - return node.sinceKotlin ?: memberVersion -} - -fun effectiveSinceKotlinForNodes(nodes: Collection<DocumentationNode>, baseVersion: String = "1.0"): String { - val map = nodes.map { effectiveSinceKotlinForNode(it, baseVersion) } - return mergeVersions(map) -} - -fun samePlatforms(platformsPerNode: Collection<PlatformsData>): Boolean { - - val first = platformsPerNode.firstOrNull()?.keys ?: return true - return platformsPerNode.all { it.keys == first } -} - -fun locationHref( - from: Location, - to: DocumentationNode, - generator: NodeLocationAwareGenerator, - pathOnly: Boolean = false -): String { - val topLevelPage = to.references(RefKind.TopLevelPage).singleOrNull()?.to - if (topLevelPage != null) { - val signature = to.detailOrNull(NodeKind.Signature) - return from.relativePathTo( - generator.location(topLevelPage), - (signature?.name ?: to.name).takeUnless { pathOnly } - ) - } - return from.relativePathTo(generator.location(to)) -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Formats/YamlOutlineService.kt b/core/src/main/kotlin/Formats/YamlOutlineService.kt deleted file mode 100644 index 3c92d8ff..00000000 --- a/core/src/main/kotlin/Formats/YamlOutlineService.kt +++ /dev/null @@ -1,26 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import java.io.File - -class YamlOutlineService @Inject constructor( - val generator: NodeLocationAwareGenerator, - val languageService: LanguageService -) : OutlineFormatService { - override fun getOutlineFileName(location: Location): File = File("${location.path}.yml") - - var outlineLevel = 0 - override fun appendOutlineHeader(location: Location, node: DocumentationNode, to: StringBuilder) { - val indent = " ".repeat(outlineLevel) - to.appendln("$indent- title: ${languageService.renderName(node)}") - to.appendln("$indent url: ${generator.relativePathToLocation(node.path.first(), node)}") - } - - override fun appendOutlineLevel(to: StringBuilder, body: () -> Unit) { - val indent = " ".repeat(outlineLevel) - to.appendln("$indent content:") - outlineLevel++ - body() - outlineLevel-- - } -} diff --git a/core/src/main/kotlin/Generation/DocumentationMerger.kt b/core/src/main/kotlin/Generation/DocumentationMerger.kt deleted file mode 100644 index 53dc23a9..00000000 --- a/core/src/main/kotlin/Generation/DocumentationMerger.kt +++ /dev/null @@ -1,235 +0,0 @@ -package org.jetbrains.dokka.Generation - -import org.jetbrains.dokka.* - -class DocumentationMerger( - private val documentationModules: List<DocumentationModule>, - val logger: DokkaLogger -) { - private val producedNodeRefGraph: NodeReferenceGraph = NodeReferenceGraph() - private val signatureMap: Map<DocumentationNode, String> - private val oldToNewNodeMap: MutableMap<DocumentationNode, DocumentationNode> = mutableMapOf() - - init { - if (documentationModules.groupBy { it.name }.size > 1) { - throw IllegalArgumentException("Modules should have similar names: ${documentationModules.joinToString(", ") {it.name}}") - } - - signatureMap = documentationModules - .flatMap { it.nodeRefGraph.nodeMapView.entries } - .associate { (k, v) -> v to k } - - - documentationModules.map { it.nodeRefGraph } - .flatMap { it.references } - .forEach { producedNodeRefGraph.addReference(it) } - } - - private fun mergePackageReferences( - from: DocumentationNode, - packages: List<DocumentationReference> - ): List<DocumentationReference> { - val packagesByName = packages - .map { it.to } - .groupBy { it.name } - - val resultReferences = mutableListOf<DocumentationReference>() - for ((name, listOfPackages) in packagesByName) { - try { - val producedPackage = mergePackagesWithEqualNames(name, from, listOfPackages) - updatePendingReferences() - - resultReferences.add( - DocumentationReference(from, producedPackage, RefKind.Member) - ) - } catch (t: Throwable) { - val entries = listOfPackages.joinToString(",") { "references:${it.allReferences().size}" } - throw Error("Failed to merge package $name from $from with entries $entries. ${t.message}", t) - } - } - - return resultReferences - } - - private fun mergePackagesWithEqualNames( - name: String, - from: DocumentationNode, - packages: List<DocumentationNode> - ): DocumentationNode { - val mergedPackage = DocumentationNode(name, Content.Empty, NodeKind.Package) - - for (contentToAppend in packages.map { it.content }.distinct()) { - mergedPackage.updateContent { - for (otherChild in contentToAppend.children) { - children.add(otherChild) - } - } - } - - for (node in packages) { - oldToNewNodeMap[node] = mergedPackage - } - - val references = packages.flatMap { it.allReferences() } - val mergedReferences = mergeReferences(mergedPackage, references) - for (ref in mergedReferences) { - if (ref.kind == RefKind.Owner) { - continue - } - mergedPackage.addReference(ref) - } - - from.append(mergedPackage, RefKind.Member) - - return mergedPackage - } - - private fun mergeMemberGroupBy(it: DocumentationNode): String { - val signature = signatureMap[it] - - if (signature != null) { - return signature - } - - logger.error("Failed to find signature for $it in \n${it.allReferences().joinToString { "\n ${it.kind} ${it.to}" }}") - return "<ERROR>" - } - - private fun mergeMemberReferences( - from: DocumentationNode, - refs: List<DocumentationReference> - ): List<DocumentationReference> { - val membersBySignature: Map<String, List<DocumentationNode>> = refs.map { it.to } - .groupBy(this::mergeMemberGroupBy) - - val mergedMembers: MutableList<DocumentationReference> = mutableListOf() - for ((signature, members) in membersBySignature) { - val newNode = mergeMembersWithEqualSignature(signature, members) - - producedNodeRefGraph.register(signature, newNode) - updatePendingReferences() - from.append(newNode, RefKind.Member) - - mergedMembers.add(DocumentationReference(from, newNode, RefKind.Member)) - } - - return mergedMembers - } - - private fun mergeMembersWithEqualSignature( - signature: String, - nodes: List<DocumentationNode> - ): DocumentationNode { - require(nodes.isNotEmpty()) - - val singleNode = nodes.singleOrNull() - if (singleNode != null) { - singleNode.dropReferences { it.kind == RefKind.Owner } - return singleNode - } - - // Specialization processing - // Given (Common, JVM, JRE6, JS) and (JVM, JRE6) and (JVM, JRE7) - // Sorted: (JVM, JRE6), (JVM, JRE7), (Common, JVM, JRE6, JS) - // Should output: (JVM, JRE6), (JVM, JRE7), (Common, JS) - // Should not remove first platform - val nodesSortedByPlatformCount = nodes.sortedBy { it.platforms.size } - val allPlatforms = mutableSetOf<String>() - nodesSortedByPlatformCount.forEach { node -> - node.platforms - .filterNot { allPlatforms.add(it) } - .filter { it != node.platforms.first() } - .forEach { platform -> - node.dropReferences { it.kind == RefKind.Platform && it.to.name == platform } - } - } - - // TODO: Quick and dirty fox for merging extensions for external classes. Fix this probably in StructuredFormatService - // TODO: while refactoring documentation model - - val groupNode = if(nodes.first().kind == NodeKind.ExternalClass){ - DocumentationNode(nodes.first().name, Content.Empty, NodeKind.ExternalClass) - } else { - DocumentationNode(nodes.first().name, Content.Empty, NodeKind.GroupNode) - } - groupNode.appendTextNode(signature, NodeKind.Signature, RefKind.Detail) - - for (node in nodes) { - node.dropReferences { it.kind == RefKind.Owner } - groupNode.append(node, RefKind.Origin) - node.append(groupNode, RefKind.TopLevelPage) - - oldToNewNodeMap[node] = groupNode - } - - if (groupNode.kind == NodeKind.ExternalClass){ - val refs = nodes.flatMap { it.allReferences() }.filter { it.kind != RefKind.Owner && it.kind != RefKind.TopLevelPage } - refs.forEach { - if (it.kind != RefKind.Link) { - it.to.dropReferences { ref -> ref.kind == RefKind.Owner } - it.to.append(groupNode, RefKind.Owner) - } - groupNode.append(it.to, it.kind) - } - } - - // if nodes are classes, nested members should be also merged and - // inserted at the same level with class - if (nodes.all { it.kind in NodeKind.classLike }) { - val members = nodes.flatMap { it.allReferences() }.filter { it.kind == RefKind.Member } - val mergedMembers = mergeMemberReferences(groupNode, members) - - for (ref in mergedMembers) { - if (ref.kind == RefKind.Owner) { - continue - } - - groupNode.append(ref.to, RefKind.Member) - } - } - - return groupNode - } - - - private fun mergeReferences( - from: DocumentationNode, - refs: List<DocumentationReference> - ): List<DocumentationReference> { - val (refsToPackages, otherRefs) = refs.partition { it.to.kind == NodeKind.Package } - val mergedPackages = mergePackageReferences(from, refsToPackages) - - val (refsToMembers, refsNotToMembers) = otherRefs.partition { it.kind == RefKind.Member } - val mergedMembers = mergeMemberReferences(from, refsToMembers) - - return mergedPackages + mergedMembers + refsNotToMembers - } - - fun merge(): DocumentationModule { - val mergedDocumentationModule = DocumentationModule( - name = documentationModules.first().name, - content = documentationModules.first().content, - nodeRefGraph = producedNodeRefGraph - ) - - val refs = documentationModules.flatMap { - it.allReferences() - } - mergeReferences(mergedDocumentationModule, refs) - - return mergedDocumentationModule - } - - private fun updatePendingReferences() { - for (ref in producedNodeRefGraph.references) { - ref.lazyNodeFrom.update() - ref.lazyNodeTo.update() - } - } - - private fun NodeResolver.update() { - if (this is NodeResolver.Exact && exactNode in oldToNewNodeMap) { - exactNode = oldToNewNodeMap[exactNode]!! - } - } -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Generation/DokkaGenerator.kt b/core/src/main/kotlin/Generation/DokkaGenerator.kt deleted file mode 100644 index 90d7cfcc..00000000 --- a/core/src/main/kotlin/Generation/DokkaGenerator.kt +++ /dev/null @@ -1,223 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Guice -import com.google.inject.Injector -import com.intellij.openapi.util.Disposer -import com.intellij.openapi.vfs.VirtualFileManager -import com.intellij.psi.PsiFile -import com.intellij.psi.PsiJavaFile -import com.intellij.psi.PsiManager -import org.jetbrains.dokka.Generation.DocumentationMerger -import org.jetbrains.dokka.Utilities.DokkaAnalysisModule -import org.jetbrains.dokka.Utilities.DokkaOutputModule -import org.jetbrains.dokka.Utilities.DokkaRunModule -import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys -import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation -import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity -import org.jetbrains.kotlin.cli.common.messages.MessageCollector -import org.jetbrains.kotlin.cli.common.messages.MessageRenderer -import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment -import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.MemberDescriptor -import org.jetbrains.kotlin.resolve.LazyTopDownAnalyzer -import org.jetbrains.kotlin.resolve.TopDownAnalysisMode -import org.jetbrains.kotlin.utils.PathUtil -import java.io.File -import kotlin.system.measureTimeMillis - -class DokkaGenerator(val dokkaConfiguration: DokkaConfiguration, - val logger: DokkaLogger) { - - private val documentationModules: MutableList<DocumentationModule> = mutableListOf() - private val globalInjector = Guice.createInjector(DokkaRunModule(dokkaConfiguration)) - - - fun generate() = with(dokkaConfiguration) { - - - for (pass in passesConfigurations) { - val documentationModule = DocumentationModule(pass.moduleName) - appendSourceModule(pass, documentationModule) - documentationModules.add(documentationModule) - } - - val totalDocumentationModule = DocumentationMerger(documentationModules, logger).merge() - totalDocumentationModule.prepareForGeneration(dokkaConfiguration) - - val timeBuild = measureTimeMillis { - logger.info("Generating pages... ") - val outputInjector = globalInjector.createChildInjector(DokkaOutputModule(dokkaConfiguration, logger)) - val instance = outputInjector.getInstance(Generator::class.java) - instance.buildAll(totalDocumentationModule) - } - logger.info("done in ${timeBuild / 1000} secs") - } - - private fun appendSourceModule( - passConfiguration: DokkaConfiguration.PassConfiguration, - documentationModule: DocumentationModule - ) = with(passConfiguration) { - - val sourcePaths = passConfiguration.sourceRoots.map { it.path } - val environment = createAnalysisEnvironment(sourcePaths, passConfiguration) - - logger.info("Module: $moduleName") - logger.info("Output: ${File(dokkaConfiguration.outputDir)}") - logger.info("Sources: ${sourcePaths.joinToString()}") - logger.info("Classpath: ${environment.classpath.joinToString()}") - - logger.info("Analysing sources and libraries... ") - val startAnalyse = System.currentTimeMillis() - - val defaultPlatformAsList = passConfiguration.targets - val defaultPlatformsProvider = object : DefaultPlatformsProvider { - override fun getDefaultPlatforms(descriptor: DeclarationDescriptor): List<String> { -// val containingFilePath = descriptor.sourcePsi()?.containingFile?.virtualFile?.canonicalPath -// ?.let { File(it).absolutePath } -// val sourceRoot = containingFilePath?.let { path -> sourceRoots.find { path.startsWith(it.path) } } - if (descriptor is MemberDescriptor && descriptor.isExpect) { - return defaultPlatformAsList.take(1) - } - return /*sourceRoot?.platforms ?: */defaultPlatformAsList - } - } - - val injector = globalInjector.createChildInjector( - DokkaAnalysisModule(environment, dokkaConfiguration, defaultPlatformsProvider, documentationModule.nodeRefGraph, passConfiguration, logger)) - - buildDocumentationModule(injector, documentationModule, { isNotSample(it, passConfiguration.samples) }, includes) - - val timeAnalyse = System.currentTimeMillis() - startAnalyse - logger.info("done in ${timeAnalyse / 1000} secs") - - Disposer.dispose(environment) - } - - fun createAnalysisEnvironment( - sourcePaths: List<String>, - passConfiguration: DokkaConfiguration.PassConfiguration - ): AnalysisEnvironment { - val environment = AnalysisEnvironment(DokkaMessageCollector(logger), passConfiguration.analysisPlatform) - - environment.apply { - if (analysisPlatform == Platform.jvm) { - addClasspath(PathUtil.getJdkClassesRootsFromCurrentJre()) - } - // addClasspath(PathUtil.getKotlinPathsForCompiler().getRuntimePath()) - for (element in passConfiguration.classpath) { - addClasspath(File(element)) - } - - addSources(sourcePaths) - addSources(passConfiguration.samples) - - loadLanguageVersionSettings(passConfiguration.languageVersion, passConfiguration.apiVersion) - } - - return environment - } - - private fun isNotSample(file: PsiFile, samples: List<String>): Boolean { - val sourceFile = File(file.virtualFile!!.path) - return samples.none { sample -> - val canonicalSample = File(sample).canonicalPath - val canonicalSource = sourceFile.canonicalPath - canonicalSource.startsWith(canonicalSample) - } - } -} - -class DokkaMessageCollector(val logger: DokkaLogger) : MessageCollector { - override fun clear() { - seenErrors = false - } - - private var seenErrors = false - - override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageLocation?) { - if (severity == CompilerMessageSeverity.ERROR) { - seenErrors = true - } - logger.error(MessageRenderer.PLAIN_FULL_PATHS.render(severity, message, location)) - } - - override fun hasErrors() = seenErrors -} - -fun buildDocumentationModule(injector: Injector, - documentationModule: DocumentationModule, - filesToDocumentFilter: (PsiFile) -> Boolean = { file -> true }, - includes: List<String> = listOf()) { - - val coreEnvironment = injector.getInstance(KotlinCoreEnvironment::class.java) - val fragmentFiles = coreEnvironment.getSourceFiles().filter(filesToDocumentFilter) - - val resolutionFacade = injector.getInstance(DokkaResolutionFacade::class.java) - val analyzer = resolutionFacade.getFrontendService(LazyTopDownAnalyzer::class.java) - analyzer.analyzeDeclarations(TopDownAnalysisMode.TopLevelDeclarations, fragmentFiles) - - val fragments = fragmentFiles.mapNotNull { resolutionFacade.resolveSession.getPackageFragment(it.packageFqName) } - .distinct() - - val packageDocs = injector.getInstance(PackageDocs::class.java) - for (include in includes) { - packageDocs.parse(include, fragments) - } - if (documentationModule.content.isEmpty()) { - documentationModule.updateContent { - for (node in packageDocs.moduleContent.children) { - append(node) - } - } - } - - parseJavaPackageDocs(packageDocs, coreEnvironment) - - with(injector.getInstance(DocumentationBuilder::class.java)) { - documentationModule.appendFragments(fragments, packageDocs.packageContent, - injector.getInstance(PackageDocumentationBuilder::class.java)) - - propagateExtensionFunctionsToSubclasses(fragments, resolutionFacade) - } - - val javaFiles = coreEnvironment.getJavaSourceFiles().filter(filesToDocumentFilter) - with(injector.getInstance(JavaDocumentationBuilder::class.java)) { - javaFiles.map { appendFile(it, documentationModule, packageDocs.packageContent) } - } -} - -fun parseJavaPackageDocs(packageDocs: PackageDocs, coreEnvironment: KotlinCoreEnvironment) { - val contentRoots = coreEnvironment.configuration.get(CLIConfigurationKeys.CONTENT_ROOTS) - ?.filterIsInstance<JavaSourceRoot>() - ?.map { it.file } - ?: listOf() - contentRoots.forEach { root -> - root.walkTopDown().filter { it.name == "overview.html" }.forEach { - packageDocs.parseJava(it.path, it.relativeTo(root).parent.replace("/", ".")) - } - } -} - - -fun KotlinCoreEnvironment.getJavaSourceFiles(): List<PsiJavaFile> { - val sourceRoots = configuration.get(CLIConfigurationKeys.CONTENT_ROOTS) - ?.filterIsInstance<JavaSourceRoot>() - ?.map { it.file } - ?: listOf() - - val result = arrayListOf<PsiJavaFile>() - val localFileSystem = VirtualFileManager.getInstance().getFileSystem("file") - sourceRoots.forEach { sourceRoot -> - sourceRoot.absoluteFile.walkTopDown().forEach { - val vFile = localFileSystem.findFileByPath(it.path) - if (vFile != null) { - val psiFile = PsiManager.getInstance(project).findFile(vFile) - if (psiFile is PsiJavaFile) { - result.add(psiFile) - } - } - } - } - return result -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Generation/FileGenerator.kt b/core/src/main/kotlin/Generation/FileGenerator.kt deleted file mode 100644 index ee2c068e..00000000 --- a/core/src/main/kotlin/Generation/FileGenerator.kt +++ /dev/null @@ -1,108 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import com.google.inject.name.Named -import java.io.File -import java.io.IOException -import java.io.PrintWriter -import java.io.StringWriter - -class FileGenerator @Inject constructor(@Named("outputDir") override val root: File) : NodeLocationAwareGenerator { - - @set:Inject(optional = true) var outlineService: OutlineFormatService? = null - @set:Inject(optional = true) lateinit var formatService: FormatService - @set:Inject(optional = true) lateinit var dokkaConfiguration: DokkaConfiguration - @set:Inject(optional = true) var packageListService: PackageListService? = null - - private val createdFiles = mutableMapOf<File, List<String>>() - - private fun File.writeFileAndAssert(context: String, action: (File) -> Unit) { - //TODO: there is a possible refactoring to drop FileLocation - //TODO: aad File from API, Location#path. - //TODO: turn [Location] into a final class, - //TODO: Use [Location] all over the place without full - //TODO: reference to the real target path, - //TODO: it opens the way to safely track all files created - //TODO: to make sure no files were overwritten by mistake - //TODO: also, the NodeLocationAwareGenerator should be removed - - val writes = createdFiles.getOrDefault(this, listOf()) + context - createdFiles[this] = writes - if (writes.size > 1) { - println("ERROR. An attempt to write ${this.relativeTo(root)} several times!") - return - } - - try { - parentFile?.mkdirsOrFail() - action(this) - } catch (e : Throwable) { - println("Failed to write $this. ${e.message}") - e.printStackTrace() - } - } - - private fun File.mkdirsOrFail() { - if (!mkdirs() && !exists()) { - throw IOException("Failed to create directory $this") - } - } - - override fun location(node: DocumentationNode): FileLocation { - return FileLocation(fileForNode(node, formatService.linkExtension)) - } - - private fun fileForNode(node: DocumentationNode, extension: String = ""): File { - return File(root, relativePathToNode(node)).appendExtension(extension) - } - - private fun locationWithoutExtension(node: DocumentationNode): FileLocation { - return FileLocation(fileForNode(node)) - } - - override fun buildPages(nodes: Iterable<DocumentationNode>) { - - for ((file, items) in nodes.groupBy { fileForNode(it, formatService.extension) }) { - file.writeFileAndAssert("pages") { it -> - it.writeText(formatService.format(location(items.first()), items)) - } - - buildPages(items.filterNot { it.kind == NodeKind.AllTypes }.flatMap { it.members }) - } - } - - override fun buildOutlines(nodes: Iterable<DocumentationNode>) { - val outlineService = this.outlineService ?: return - for ((location, items) in nodes.groupBy { locationWithoutExtension(it) }) { - outlineService.getOutlineFileName(location).writeFileAndAssert("outlines") { file -> - file.writeText(outlineService.formatOutline(location, items)) - } - } - } - - override fun buildSupportFiles() { - formatService.enumerateSupportFiles { resource, targetPath -> - File(root, relativePathToNode(listOf(targetPath), false)).writeFileAndAssert("support files") { file -> - file.outputStream().use { - javaClass.getResourceAsStream(resource).copyTo(it) - } - } - } - } - - override fun buildPackageList(nodes: Iterable<DocumentationNode>) { - if (packageListService == null) return - - for (module in nodes) { - - val moduleRoot = location(module).file.parentFile - val packageListFile = File(moduleRoot, "package-list") - - val text = "\$dokka.format:${dokkaConfiguration.format}\n" + packageListService!!.formatPackageList(module as DocumentationModule) - - packageListFile.writeFileAndAssert("packages-list") { file -> - file.writeText(text) - } - } - } -} diff --git a/core/src/main/kotlin/Generation/Generator.kt b/core/src/main/kotlin/Generation/Generator.kt deleted file mode 100644 index 23286e29..00000000 --- a/core/src/main/kotlin/Generation/Generator.kt +++ /dev/null @@ -1,29 +0,0 @@ -package org.jetbrains.dokka - -import java.io.File - -interface Generator { - fun buildPages(nodes: Iterable<DocumentationNode>) - fun buildOutlines(nodes: Iterable<DocumentationNode>) - fun buildSupportFiles() - fun buildPackageList(nodes: Iterable<DocumentationNode>) -} - -fun Generator.buildAll(nodes: Iterable<DocumentationNode>) { - buildPages(nodes) - buildOutlines(nodes) - buildSupportFiles() - buildPackageList(nodes) -} - -fun Generator.buildPage(node: DocumentationNode): Unit = buildPages(listOf(node)) - -fun Generator.buildOutline(node: DocumentationNode): Unit = buildOutlines(listOf(node)) - -fun Generator.buildAll(node: DocumentationNode): Unit = buildAll(listOf(node)) - - -interface NodeLocationAwareGenerator: Generator { - fun location(node: DocumentationNode): Location - val root: File -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt b/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt deleted file mode 100644 index d6743e60..00000000 --- a/core/src/main/kotlin/Java/JavaPsiDocumentationBuilder.kt +++ /dev/null @@ -1,361 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import com.intellij.openapi.util.text.StringUtil -import com.intellij.psi.* -import com.intellij.psi.impl.JavaConstantExpressionEvaluator -import com.intellij.psi.util.InheritanceUtil -import com.intellij.psi.util.PsiTreeUtil -import org.jetbrains.kotlin.asJava.elements.KtLightAbstractAnnotation -import org.jetbrains.kotlin.asJava.elements.KtLightDeclaration -import org.jetbrains.kotlin.asJava.elements.KtLightElement -import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag -import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag -import org.jetbrains.kotlin.lexer.KtTokens -import org.jetbrains.kotlin.psi.KtDeclaration -import org.jetbrains.kotlin.psi.KtModifierListOwner - -fun getSignature(element: PsiElement?) = when(element) { - is PsiPackage -> element.qualifiedName - is PsiClass -> element.qualifiedName - is PsiField -> element.containingClass!!.qualifiedName + "$" + element.name - is PsiMethod -> - methodSignature(element) - is PsiParameter -> { - val method = (element.parent.parent as PsiMethod) - methodSignature(method) - } - else -> null -} - -private fun methodSignature(method: PsiMethod): String { - return method.containingClass?.qualifiedName + "$" + method.name + "(" + - method.parameterList.parameters.map { it.type.typeSignature() }.joinToString(",") + ")" -} - -private fun PsiType.typeSignature(): String = when(this) { - is PsiArrayType -> "Array((${componentType.typeSignature()}))" - is PsiPrimitiveType -> "kotlin." + canonicalText.capitalize() - else -> mapTypeName(this) -} - -private fun mapTypeName(psiType: PsiType): String = when (psiType) { - is PsiPrimitiveType -> psiType.canonicalText - is PsiClassType -> psiType.resolve()?.qualifiedName ?: psiType.className - is PsiEllipsisType -> mapTypeName(psiType.componentType) - is PsiArrayType -> "kotlin.Array" - else -> psiType.canonicalText -} - -interface JavaDocumentationBuilder { - fun appendFile(file: PsiJavaFile, module: DocumentationModule, packageContent: Map<String, Content>) -} - -class JavaPsiDocumentationBuilder : JavaDocumentationBuilder { - private val passConfiguration: DokkaConfiguration.PassConfiguration - private val refGraph: NodeReferenceGraph - private val docParser: JavaDocumentationParser - - @Inject constructor( - passConfiguration: DokkaConfiguration.PassConfiguration, - refGraph: NodeReferenceGraph, - logger: DokkaLogger, - signatureProvider: ElementSignatureProvider, - externalDocumentationLinkResolver: ExternalDocumentationLinkResolver - ) { - this.passConfiguration = passConfiguration - this.refGraph = refGraph - this.docParser = JavadocParser(refGraph, logger, signatureProvider, externalDocumentationLinkResolver) - } - - constructor(passConfiguration: DokkaConfiguration.PassConfiguration, refGraph: NodeReferenceGraph, docParser: JavaDocumentationParser) { - this.passConfiguration = passConfiguration - this.refGraph = refGraph - this.docParser = docParser - } - - override fun appendFile(file: PsiJavaFile, module: DocumentationModule, packageContent: Map<String, Content>) { - if (skipFile(file) || file.classes.all { skipElement(it) }) { - return - } - val packageNode = findOrCreatePackageNode(module, file.packageName, emptyMap(), refGraph) - appendClasses(packageNode, file.classes) - } - - fun appendClasses(packageNode: DocumentationNode, classes: Array<PsiClass>) { - packageNode.appendChildren(classes) { build() } - } - - fun register(element: PsiElement, node: DocumentationNode) { - val signature = getSignature(element) - if (signature != null) { - refGraph.register(signature, node) - } - } - - fun link(node: DocumentationNode, element: PsiElement?) { - val qualifiedName = getSignature(element) - if (qualifiedName != null) { - refGraph.link(node, qualifiedName, RefKind.Link) - } - } - - fun link(element: PsiElement?, node: DocumentationNode, kind: RefKind) { - val qualifiedName = getSignature(element) - if (qualifiedName != null) { - refGraph.link(qualifiedName, node, kind) - } - } - - fun nodeForElement(element: PsiNamedElement, - kind: NodeKind, - name: String = element.name ?: "<anonymous>", - register: Boolean = false): DocumentationNode { - val (docComment, deprecatedContent) = docParser.parseDocumentation(element) - val node = DocumentationNode(name, docComment, kind) - if (register) register(element, node) - if (element is PsiModifierListOwner) { - node.appendModifiers(element) - val modifierList = element.modifierList - if (modifierList != null) { - modifierList.annotations.filter { !ignoreAnnotation(it) }.forEach { - val annotation = it.build() - node.append(annotation, - if (it.qualifiedName == "java.lang.Deprecated") RefKind.Deprecation else RefKind.Annotation) - } - } - } - if (deprecatedContent != null) { - val deprecationNode = DocumentationNode("", deprecatedContent, NodeKind.Modifier) - node.append(deprecationNode, RefKind.Deprecation) - } - if (element is PsiDocCommentOwner && element.isDeprecated && node.deprecation == null) { - val deprecationNode = DocumentationNode("", Content.of(ContentText("Deprecated")), NodeKind.Modifier) - node.append(deprecationNode, RefKind.Deprecation) - } - return node - } - - fun ignoreAnnotation(annotation: PsiAnnotation) = when(annotation.qualifiedName) { - "java.lang.SuppressWarnings" -> true - else -> false - } - - fun <T : Any> DocumentationNode.appendChildren(elements: Array<T>, - kind: RefKind = RefKind.Member, - buildFn: T.() -> DocumentationNode) { - elements.forEach { - if (!skipElement(it)) { - append(it.buildFn(), kind) - } - } - } - - private fun skipFile(javaFile: PsiJavaFile): Boolean = passConfiguration.effectivePackageOptions(javaFile.packageName).suppress - - private fun skipElement(element: Any) = - skipElementByVisibility(element) || - hasSuppressDocTag(element) || - skipElementBySuppressedFiles(element) - - private fun skipElementByVisibility(element: Any): Boolean = - element is PsiModifierListOwner && - element !is PsiParameter && - !(passConfiguration.effectivePackageOptions((element.containingFile as? PsiJavaFile)?.packageName ?: "").includeNonPublic) && - (element.hasModifierProperty(PsiModifier.PRIVATE) || - element.hasModifierProperty(PsiModifier.PACKAGE_LOCAL) || - element.isInternal()) - - private fun skipElementBySuppressedFiles(element: Any): Boolean = - element is PsiElement && element.containingFile.virtualFile?.path in passConfiguration.suppressedFiles - - private fun PsiElement.isInternal(): Boolean { - val ktElement = (this as? KtLightElement<*, *>)?.kotlinOrigin ?: return false - return (ktElement as? KtModifierListOwner)?.hasModifier(KtTokens.INTERNAL_KEYWORD) ?: false - } - - fun <T : Any> DocumentationNode.appendMembers(elements: Array<T>, buildFn: T.() -> DocumentationNode) = - appendChildren(elements, RefKind.Member, buildFn) - - fun <T : Any> DocumentationNode.appendDetails(elements: Array<T>, buildFn: T.() -> DocumentationNode) = - appendChildren(elements, RefKind.Detail, buildFn) - - fun PsiClass.build(): DocumentationNode { - val kind = when { - isAnnotationType -> NodeKind.AnnotationClass - isInterface -> NodeKind.Interface - isEnum -> NodeKind.Enum - isException() -> NodeKind.Exception - else -> NodeKind.Class - } - val node = nodeForElement(this, kind, register = isAnnotationType) - superTypes.filter { !ignoreSupertype(it) }.forEach { - node.appendType(it, NodeKind.Supertype) - val superClass = it.resolve() - if (superClass != null) { - link(superClass, node, RefKind.Inheritor) - } - } - - var methodsAndConstructors = methods - if (constructors.isEmpty()) { - // Having no constructor represents a class that only has an implicit/default constructor - // so we create one synthetically for documentation - val factory = JavaPsiFacade.getElementFactory(this.project) - methodsAndConstructors += factory.createMethodFromText("public $name() {}", this) - } - node.appendDetails(typeParameters) { build() } - node.appendMembers(methodsAndConstructors) { build() } - node.appendMembers(fields) { build() } - node.appendMembers(innerClasses) { build() } - register(this, node) - return node - } - - fun PsiClass.isException() = InheritanceUtil.isInheritor(this, "java.lang.Throwable") - - fun ignoreSupertype(psiType: PsiClassType): Boolean = - psiType.isClass("java.lang.Enum") || psiType.isClass("java.lang.Object") - - fun PsiClassType.isClass(qName: String): Boolean { - val shortName = qName.substringAfterLast('.') - if (className == shortName) { - val psiClass = resolve() - return psiClass?.qualifiedName == qName - } - return false - } - - fun PsiField.build(): DocumentationNode { - val node = nodeForElement(this, nodeKind()) - node.appendType(type) - - node.appendConstantValueIfAny(this) - register(this, node) - return node - } - - private fun DocumentationNode.appendConstantValueIfAny(field: PsiField) { - val modifierList = field.modifierList ?: return - val initializer = field.initializer ?: return - if (modifierList.hasExplicitModifier(PsiModifier.FINAL) && - modifierList.hasExplicitModifier(PsiModifier.STATIC)) { - val value = JavaConstantExpressionEvaluator.computeConstantExpression(initializer, false) - val text = when(value) { - null -> return // No value found - is String -> - "\"" + StringUtil.escapeStringCharacters(value) + "\"" - else -> value.toString() - } - append(DocumentationNode(text, Content.Empty, NodeKind.Value), RefKind.Detail) - } - } - - private fun PsiField.nodeKind(): NodeKind = when { - this is PsiEnumConstant -> NodeKind.EnumItem - else -> NodeKind.Field - } - - fun PsiMethod.build(): DocumentationNode { - val node = nodeForElement(this, nodeKind(), - if (isConstructor) "<init>" else name) - - if (!isConstructor) { - node.appendType(returnType) - } - node.appendDetails(parameterList.parameters) { build() } - node.appendDetails(typeParameters) { build() } - register(this, node) - return node - } - - private fun PsiMethod.nodeKind(): NodeKind = when { - isConstructor -> NodeKind.Constructor - else -> NodeKind.Function - } - - fun PsiParameter.build(): DocumentationNode { - val node = nodeForElement(this, NodeKind.Parameter) - node.appendType(type) - if (type is PsiEllipsisType) { - node.appendTextNode("vararg", NodeKind.Modifier, RefKind.Detail) - } - return node - } - - fun PsiTypeParameter.build(): DocumentationNode { - val node = nodeForElement(this, NodeKind.TypeParameter) - extendsListTypes.forEach { node.appendType(it, NodeKind.UpperBound) } - implementsListTypes.forEach { node.appendType(it, NodeKind.UpperBound) } - return node - } - - fun DocumentationNode.appendModifiers(element: PsiModifierListOwner) { - val modifierList = element.modifierList ?: return - - PsiModifier.MODIFIERS.forEach { - if (modifierList.hasExplicitModifier(it)) { - appendTextNode(it, NodeKind.Modifier) - } - } - } - - fun DocumentationNode.appendType(psiType: PsiType?, kind: NodeKind = NodeKind.Type) { - if (psiType == null) { - return - } - append(psiType.build(kind), RefKind.Detail) - } - - fun PsiType.build(kind: NodeKind = NodeKind.Type): DocumentationNode { - val name = mapTypeName(this) - val node = DocumentationNode(name, Content.Empty, kind) - if (this is PsiClassType) { - node.appendDetails(parameters) { build(NodeKind.Type) } - link(node, resolve()) - } - if (this is PsiArrayType && this !is PsiEllipsisType) { - node.append(componentType.build(NodeKind.Type), RefKind.Detail) - } - return node - } - - private fun lookupOrBuildClass(psiClass: PsiClass): DocumentationNode { - val existing = refGraph.lookup(getSignature(psiClass)!!) - if (existing != null) return existing - val new = psiClass.build() - val packageNode = findOrCreatePackageNode(null, (psiClass.containingFile as PsiJavaFile).packageName, emptyMap(), refGraph) - packageNode.append(new, RefKind.Member) - return new - } - - fun PsiAnnotation.build(): DocumentationNode { - - val original = when (this) { - is KtLightAbstractAnnotation -> clsDelegate - else -> this - } - val node = DocumentationNode(qualifiedName?.substringAfterLast(".") ?: "<?>", Content.Empty, NodeKind.Annotation) - val psiClass = original.nameReferenceElement?.resolve() as? PsiClass - if (psiClass != null && psiClass.isAnnotationType) { - node.append(lookupOrBuildClass(psiClass), RefKind.Link) - } - parameterList.attributes.forEach { - val parameter = DocumentationNode(it.name ?: "value", Content.Empty, NodeKind.Parameter) - val value = it.value - if (value != null) { - val valueText = (value as? PsiLiteralExpression)?.value as? String ?: value.text - val valueNode = DocumentationNode(valueText, Content.Empty, NodeKind.Value) - parameter.append(valueNode, RefKind.Detail) - } - node.append(parameter, RefKind.Detail) - } - return node - } -} - -fun hasSuppressDocTag(element: Any?): Boolean { - val declaration = (element as? KtLightDeclaration<*, *>)?.kotlinOrigin ?: return false - return PsiTreeUtil.findChildrenOfType(declaration.docComment, KDocTag::class.java).any { it.knownTag == KDocKnownTag.SUPPRESS } -} - diff --git a/core/src/main/kotlin/Java/JavadocParser.kt b/core/src/main/kotlin/Java/JavadocParser.kt deleted file mode 100644 index 318bad28..00000000 --- a/core/src/main/kotlin/Java/JavadocParser.kt +++ /dev/null @@ -1,401 +0,0 @@ -package org.jetbrains.dokka - -import com.intellij.psi.* -import com.intellij.psi.impl.source.tree.JavaDocElementType -import com.intellij.psi.javadoc.* -import com.intellij.psi.util.PsiTreeUtil -import org.jetbrains.kotlin.utils.keysToMap -import org.jsoup.Jsoup -import org.jsoup.nodes.Element -import org.jsoup.nodes.Node -import org.jsoup.nodes.TextNode -import java.net.URI - -data class JavadocParseResult(val content: Content, val deprecatedContent: Content?) { - companion object { - val Empty = JavadocParseResult(Content.Empty, null) - } -} - -interface JavaDocumentationParser { - fun parseDocumentation(element: PsiNamedElement): JavadocParseResult -} - -class JavadocParser( - private val refGraph: NodeReferenceGraph, - private val logger: DokkaLogger, - private val signatureProvider: ElementSignatureProvider, - private val externalDocumentationLinkResolver: ExternalDocumentationLinkResolver -) : JavaDocumentationParser { - - private fun ContentSection.appendTypeElement(signature: String, selector: (DocumentationNode) -> DocumentationNode?) { - append(LazyContentBlock { - val node = refGraph.lookupOrWarn(signature, logger)?.let(selector) ?: return@LazyContentBlock emptyList() - listOf(ContentBlock().apply { - append(NodeRenderContent(node, LanguageService.RenderMode.SUMMARY)) - symbol(":") - text(" ") - }) - }) - } - - override fun parseDocumentation(element: PsiNamedElement): JavadocParseResult { - val docComment = (element as? PsiDocCommentOwner)?.docComment ?: return JavadocParseResult.Empty - val result = MutableContent() - var deprecatedContent: Content? = null - - val nodes = convertJavadocElements(docComment.descriptionElements.dropWhile { it.text.trim().isEmpty() }, element) - val firstParagraphContents = nodes.takeWhile { it !is ContentParagraph } - val firstParagraph = ContentParagraph() - if (firstParagraphContents.isNotEmpty()) { - firstParagraphContents.forEach { firstParagraph.append(it) } - result.append(firstParagraph) - } - - result.appendAll(nodes.drop(firstParagraphContents.size)) - - if (element is PsiMethod) { - val tagsByName = element.searchInheritedTags() - for ((tagName, tags) in tagsByName) { - for ((tag, context) in tags) { - val section = result.addSection(javadocSectionDisplayName(tagName), tag.getSubjectName()) - val signature = signatureProvider.signature(element) - when (tagName) { - "param" -> { - section.appendTypeElement(signature) { - it.details - .find { node -> node.kind == NodeKind.Parameter && node.name == tag.getSubjectName() } - ?.detailOrNull(NodeKind.Type) - } - } - "return" -> { - section.appendTypeElement(signature) { it.detailOrNull(NodeKind.Type) } - } - } - section.appendAll(convertJavadocElements(tag.contentElements(), context)) - } - } - } - - docComment.tags.forEach { tag -> - when (tag.name) { - "see" -> result.convertSeeTag(tag) - "deprecated" -> { - deprecatedContent = Content().apply { - appendAll(convertJavadocElements(tag.contentElements(), element)) - } - } - in tagsToInherit -> {} - else -> { - val subjectName = tag.getSubjectName() - val section = result.addSection(javadocSectionDisplayName(tag.name), subjectName) - - section.appendAll(convertJavadocElements(tag.contentElements(), element)) - } - } - } - return JavadocParseResult(result, deprecatedContent) - } - - private val tagsToInherit = setOf("param", "return", "throws") - - private data class TagWithContext(val tag: PsiDocTag, val context: PsiNamedElement) - - private fun PsiMethod.searchInheritedTags(): Map<String, Collection<TagWithContext>> { - - val output = tagsToInherit.keysToMap { mutableMapOf<String?, TagWithContext>() } - - fun recursiveSearch(methods: Array<PsiMethod>) { - for (method in methods) { - recursiveSearch(method.findSuperMethods()) - } - for (method in methods) { - for (tag in method.docComment?.tags.orEmpty()) { - if (tag.name in tagsToInherit) { - output[tag.name]!![tag.getSubjectName()] = TagWithContext(tag, method) - } - } - } - } - - recursiveSearch(arrayOf(this)) - return output.mapValues { it.value.values } - } - - - private fun PsiDocTag.contentElements(): Iterable<PsiElement> { - val tagValueElements = children - .dropWhile { it.node?.elementType == JavaDocTokenType.DOC_TAG_NAME } - .dropWhile { it is PsiWhiteSpace } - .filterNot { it.node?.elementType == JavaDocTokenType.DOC_COMMENT_LEADING_ASTERISKS } - return if (getSubjectName() != null) tagValueElements.dropWhile { it is PsiDocTagValue } else tagValueElements - } - - private fun convertJavadocElements(elements: Iterable<PsiElement>, element: PsiNamedElement): List<ContentNode> { - val doc = Jsoup.parse(expandAllForElements(elements, element)) - return doc.body().childNodes().mapNotNull { - convertHtmlNode(it) - } - } - - private fun ContentBlock.appendAll(nodes: List<ContentNode>) { - nodes.forEach { append(it) } - } - - private fun expandAllForElements(elements: Iterable<PsiElement>, element: PsiNamedElement): String { - val htmlBuilder = StringBuilder() - elements.forEach { - if (it is PsiInlineDocTag) { - htmlBuilder.append(convertInlineDocTag(it, element)) - } else { - htmlBuilder.append(it.text) - } - } - return htmlBuilder.toString().trim() - } - - private fun convertHtmlNode(node: Node, insidePre: Boolean = false): ContentNode? { - if (node is TextNode) { - val text = if (insidePre) node.wholeText else node.text() - return ContentText(text) - } else if (node is Element) { - val childBlock = createBlock(node, insidePre) - - node.childNodes().forEach { - val child = convertHtmlNode(it, insidePre || childBlock is ContentBlockCode) - if (child != null) { - childBlock.append(child) - } - } - return childBlock - } - return null - } - - private fun createBlock(element: Element, insidePre: Boolean): ContentBlock = when (element.tagName()) { - "p" -> ContentParagraph() - "b", "strong" -> ContentStrong() - "i", "em" -> ContentEmphasis() - "s", "del" -> ContentStrikethrough() - "code" -> if (insidePre) ContentBlock() else ContentCode() - "pre" -> ContentBlockCode() - "ul" -> ContentUnorderedList() - "ol" -> ContentOrderedList() - "li" -> ContentListItem() - "a" -> createLink(element) - "br" -> ContentBlock().apply { hardLineBreak() } - else -> ContentBlock() - } - - private fun createLink(element: Element): ContentBlock { - return when { - element.hasAttr("docref") -> { - val docref = element.attr("docref") - ContentNodeLazyLink(docref) { refGraph.lookupOrWarn(docref, logger)} - } - element.hasAttr("href") -> { - val href = element.attr("href") - - val uri = try { - URI(href) - } catch (_: Exception) { - null - } - - if (uri?.isAbsolute == false) { - ContentLocalLink(href) - } else { - ContentExternalLink(href) - } - } - element.hasAttr("name") -> { - ContentBookmark(element.attr("name")) - } - else -> ContentBlock() - } - } - - private fun MutableContent.convertSeeTag(tag: PsiDocTag) { - val linkElement = tag.linkElement() ?: return - val seeSection = findSectionByTag(ContentTags.SeeAlso) ?: addSection(ContentTags.SeeAlso, null) - - val valueElement = tag.referenceElement() - val externalLink = resolveExternalLink(valueElement) - val text = ContentText(linkElement.text) - - val linkSignature by lazy { resolveInternalLink(valueElement) } - val node = when { - externalLink != null -> { - val linkNode = ContentExternalLink(externalLink) - linkNode.append(text) - linkNode - } - linkSignature != null -> { - val linkNode = - ContentNodeLazyLink( - (tag.valueElement ?: linkElement).text - ) { refGraph.lookupOrWarn(linkSignature!!, logger) } - linkNode.append(text) - linkNode - } - else -> text - } - seeSection.append(node) - } - - private fun convertInlineDocTag(tag: PsiInlineDocTag, element: PsiNamedElement) = when (tag.name) { - "link", "linkplain" -> { - val valueElement = tag.referenceElement() - val externalLink = resolveExternalLink(valueElement) - val linkSignature by lazy { resolveInternalLink(valueElement) } - if (externalLink != null || linkSignature != null) { - val labelText = tag.dataElements.firstOrNull { it is PsiDocToken }?.text ?: valueElement!!.text - val linkTarget = if (externalLink != null) "href=\"$externalLink\"" else "docref=\"$linkSignature\"" - val link = "<a $linkTarget>${labelText.htmlEscape()}</a>" - if (tag.name == "link") "<code>$link</code>" else link - } else if (valueElement != null) { - valueElement.text - } else { - "" - } - } - "code", "literal" -> { - val text = StringBuilder() - tag.dataElements.forEach { text.append(it.text) } - val escaped = text.toString().trimStart().htmlEscape() - if (tag.name == "code") "<code>$escaped</code>" else escaped - } - "inheritDoc" -> { - val result = (element as? PsiMethod)?.let { - // @{inheritDoc} is only allowed on functions - val parent = tag.parent - when (parent) { - is PsiDocComment -> element.findSuperDocCommentOrWarn() - is PsiDocTag -> element.findSuperDocTagOrWarn(parent) - else -> null - } - } - result ?: tag.text - } - else -> tag.text - } - - private fun PsiDocTag.referenceElement(): PsiElement? = - linkElement()?.let { - if (it.node.elementType == JavaDocElementType.DOC_REFERENCE_HOLDER) { - PsiTreeUtil.findChildOfType(it, PsiJavaCodeReferenceElement::class.java) - } else { - it - } - } - - private fun PsiDocTag.linkElement(): PsiElement? = - valueElement ?: dataElements.firstOrNull { it !is PsiWhiteSpace } - - private fun resolveExternalLink(valueElement: PsiElement?): String? { - val target = valueElement?.reference?.resolve() - if (target != null) { - return externalDocumentationLinkResolver.buildExternalDocumentationLink(target) - } - return null - } - - private fun resolveInternalLink(valueElement: PsiElement?): String? { - val target = valueElement?.reference?.resolve() - if (target != null) { - return signatureProvider.signature(target) - } - return null - } - - fun PsiDocTag.getSubjectName(): String? { - if (name == "param" || name == "throws" || name == "exception") { - return valueElement?.text - } - return null - } - - private fun PsiMethod.findSuperDocCommentOrWarn(): String { - val method = findFirstSuperMethodWithDocumentation(this) - if (method != null) { - val descriptionElements = method.docComment?.descriptionElements?.dropWhile { - it.text.trim().isEmpty() - } ?: return "" - - return expandAllForElements(descriptionElements, method) - } - logger.warn("No docs found on supertype with {@inheritDoc} method ${this.name} in ${this.containingFile.name}:${this.lineNumber()}") - return "" - } - - - private fun PsiMethod.findSuperDocTagOrWarn(elementToExpand: PsiDocTag): String { - val result = findFirstSuperMethodWithDocumentationforTag(elementToExpand, this) - - if (result != null) { - val (method, tag) = result - - val contentElements = tag.contentElements().dropWhile { it.text.trim().isEmpty() } - - val expandedString = expandAllForElements(contentElements, method) - - return expandedString - } - logger.warn("No docs found on supertype for @${elementToExpand.name} ${elementToExpand.getSubjectName()} with {@inheritDoc} method ${this.name} in ${this.containingFile.name}:${this.lineNumber()}") - return "" - } - - private fun findFirstSuperMethodWithDocumentation(current: PsiMethod): PsiMethod? { - val superMethods = current.findSuperMethods() - for (method in superMethods) { - val docs = method.docComment?.descriptionElements?.dropWhile { it.text.trim().isEmpty() } - if (!docs.isNullOrEmpty()) { - return method - } - } - for (method in superMethods) { - val result = findFirstSuperMethodWithDocumentation(method) - if (result != null) { - return result - } - } - - return null - } - - private fun findFirstSuperMethodWithDocumentationforTag(elementToExpand: PsiDocTag, current: PsiMethod): Pair<PsiMethod, PsiDocTag>? { - val superMethods = current.findSuperMethods() - val mappedFilteredTags = superMethods.map { - it to it.docComment?.tags?.filter { it.name == elementToExpand.name } - } - - for ((method, tags) in mappedFilteredTags) { - tags ?: continue - for (tag in tags) { - val (tagSubject, elementSubject) = when (tag.name) { - "throws" -> { - // match class names only for throws, ignore possibly fully qualified path - // TODO: Always match exactly here - tag.getSubjectName()?.split(".")?.last() to elementToExpand.getSubjectName()?.split(".")?.last() - } - else -> { - tag.getSubjectName() to elementToExpand.getSubjectName() - } - } - - if (tagSubject == elementSubject) { - return method to tag - } - } - } - - for (method in superMethods) { - val result = findFirstSuperMethodWithDocumentationforTag(elementToExpand, method) - if (result != null) { - return result - } - } - return null - } - -} diff --git a/core/src/main/kotlin/Kotlin/ContentBuilder.kt b/core/src/main/kotlin/Kotlin/ContentBuilder.kt deleted file mode 100644 index 573b41b6..00000000 --- a/core/src/main/kotlin/Kotlin/ContentBuilder.kt +++ /dev/null @@ -1,188 +0,0 @@ -package org.jetbrains.dokka - -import org.intellij.markdown.MarkdownElementTypes -import org.intellij.markdown.MarkdownTokenTypes -import org.intellij.markdown.html.entities.EntityConverter -import org.intellij.markdown.parser.LinkMap -import java.util.* - -class LinkResolver(private val linkMap: LinkMap, private val contentFactory: (String) -> ContentBlock) { - fun getLinkInfo(refLabel: String) = linkMap.getLinkInfo(refLabel) - fun resolve(href: String): ContentBlock = contentFactory(href) -} - -fun buildContent(tree: MarkdownNode, linkResolver: LinkResolver, inline: Boolean = false): MutableContent { - val result = MutableContent() - if (inline) { - buildInlineContentTo(tree, result, linkResolver) - } else { - buildContentTo(tree, result, linkResolver) - } - return result -} - -fun buildContentTo(tree: MarkdownNode, target: ContentBlock, linkResolver: LinkResolver) { -// println(tree.toTestString()) - val nodeStack = ArrayDeque<ContentBlock>() - nodeStack.push(target) - - tree.visit { node, processChildren -> - val parent = nodeStack.peek() - - fun appendNodeWithChildren(content: ContentBlock) { - nodeStack.push(content) - processChildren() - parent.append(nodeStack.pop()) - } - - when (node.type) { - MarkdownElementTypes.ATX_1 -> appendNodeWithChildren(ContentHeading(1)) - MarkdownElementTypes.ATX_2 -> appendNodeWithChildren(ContentHeading(2)) - MarkdownElementTypes.ATX_3 -> appendNodeWithChildren(ContentHeading(3)) - MarkdownElementTypes.ATX_4 -> appendNodeWithChildren(ContentHeading(4)) - MarkdownElementTypes.ATX_5 -> appendNodeWithChildren(ContentHeading(5)) - MarkdownElementTypes.ATX_6 -> appendNodeWithChildren(ContentHeading(6)) - MarkdownElementTypes.UNORDERED_LIST -> appendNodeWithChildren(ContentUnorderedList()) - MarkdownElementTypes.ORDERED_LIST -> appendNodeWithChildren(ContentOrderedList()) - MarkdownElementTypes.LIST_ITEM -> appendNodeWithChildren(ContentListItem()) - MarkdownElementTypes.EMPH -> appendNodeWithChildren(ContentEmphasis()) - MarkdownElementTypes.STRONG -> appendNodeWithChildren(ContentStrong()) - MarkdownElementTypes.CODE_SPAN -> { - val startDelimiter = node.child(MarkdownTokenTypes.BACKTICK)?.text - if (startDelimiter != null) { - val text = node.text.substring(startDelimiter.length).removeSuffix(startDelimiter) - val codeSpan = ContentCode().apply { append(ContentText(text)) } - parent.append(codeSpan) - } - } - MarkdownElementTypes.CODE_BLOCK, - MarkdownElementTypes.CODE_FENCE -> { - val language = node.child(MarkdownTokenTypes.FENCE_LANG)?.text?.trim() ?: "" - appendNodeWithChildren(ContentBlockCode(language)) - } - MarkdownElementTypes.PARAGRAPH -> appendNodeWithChildren(ContentParagraph()) - - MarkdownElementTypes.INLINE_LINK -> { - val linkTextNode = node.child(MarkdownElementTypes.LINK_TEXT) - val destination = node.child(MarkdownElementTypes.LINK_DESTINATION) - if (linkTextNode != null) { - if (destination != null) { - val link = ContentExternalLink(destination.text) - renderLinkTextTo(linkTextNode, link, linkResolver) - parent.append(link) - } else { - val link = ContentExternalLink(linkTextNode.getLabelText()) - renderLinkTextTo(linkTextNode, link, linkResolver) - parent.append(link) - } - } - } - MarkdownElementTypes.SHORT_REFERENCE_LINK, - MarkdownElementTypes.FULL_REFERENCE_LINK -> { - val labelElement = node.child(MarkdownElementTypes.LINK_LABEL) - if (labelElement != null) { - val linkInfo = linkResolver.getLinkInfo(labelElement.text) - val labelText = labelElement.getLabelText() - val link = linkInfo?.let { linkResolver.resolve(it.destination.toString()) } ?: linkResolver.resolve(labelText) - val linkText = node.child(MarkdownElementTypes.LINK_TEXT) - if (linkText != null) { - renderLinkTextTo(linkText, link, linkResolver) - } else { - link.append(ContentText(labelText)) - } - parent.append(link) - } - } - MarkdownTokenTypes.WHITE_SPACE -> { - // Don't append first space if start of header (it is added during formatting later) - // v - // #### Some Heading - if (nodeStack.peek() !is ContentHeading || node.parent?.children?.first() != node) { - parent.append(ContentText(node.text)) - } - } - MarkdownTokenTypes.EOL -> { - if ((keepEol(nodeStack.peek()) && node.parent?.children?.last() != node) || - // Keep extra blank lines when processing lists (affects Markdown formatting) - (processingList(nodeStack.peek()) && node.previous?.type == MarkdownTokenTypes.EOL)) { - parent.append(ContentText(node.text)) - } - } - - MarkdownTokenTypes.CODE_LINE -> { - val content = ContentText(node.text) - if (parent is ContentBlockCode) { - parent.append(content) - } else { - parent.append(ContentBlockCode().apply { append(content) }) - } - } - - MarkdownTokenTypes.TEXT -> { - fun createEntityOrText(text: String): ContentNode { - if (text == "&" || text == """ || text == "<" || text == ">") { - return ContentEntity(text) - } - if (text == "&") { - return ContentEntity("&") - } - val decodedText = EntityConverter.replaceEntities(text, true, true) - if (decodedText != text) { - return ContentEntity(text) - } - return ContentText(text) - } - - parent.append(createEntityOrText(node.text)) - } - - MarkdownTokenTypes.EMPH -> { - val parentNodeType = node.parent?.type - if (parentNodeType != MarkdownElementTypes.EMPH && parentNodeType != MarkdownElementTypes.STRONG) { - parent.append(ContentText(node.text)) - } - } - - MarkdownTokenTypes.COLON, - MarkdownTokenTypes.SINGLE_QUOTE, - MarkdownTokenTypes.DOUBLE_QUOTE, - MarkdownTokenTypes.LT, - MarkdownTokenTypes.GT, - MarkdownTokenTypes.LPAREN, - MarkdownTokenTypes.RPAREN, - MarkdownTokenTypes.LBRACKET, - MarkdownTokenTypes.RBRACKET, - MarkdownTokenTypes.EXCLAMATION_MARK, - MarkdownTokenTypes.BACKTICK, - MarkdownTokenTypes.CODE_FENCE_CONTENT -> { - parent.append(ContentText(node.text)) - } - - MarkdownElementTypes.LINK_DEFINITION -> { - } - - else -> { - processChildren() - } - } - } -} - -private fun MarkdownNode.getLabelText() = children.filter { it.type == MarkdownTokenTypes.TEXT || it.type == MarkdownTokenTypes.EMPH || it.type == MarkdownTokenTypes.COLON }.joinToString("") { it.text } - -private fun keepEol(node: ContentNode) = node is ContentParagraph || node is ContentSection || node is ContentBlockCode -private fun processingList(node: ContentNode) = node is ContentOrderedList || node is ContentUnorderedList - -fun buildInlineContentTo(tree: MarkdownNode, target: ContentBlock, linkResolver: LinkResolver) { - val inlineContent = tree.children.singleOrNull { it.type == MarkdownElementTypes.PARAGRAPH }?.children ?: listOf(tree) - inlineContent.forEach { - buildContentTo(it, target, linkResolver) - } -} - -fun renderLinkTextTo(tree: MarkdownNode, target: ContentBlock, linkResolver: LinkResolver) { - val linkTextNodes = tree.children.drop(1).dropLast(1) - linkTextNodes.forEach { - buildContentTo(it, target, linkResolver) - } -} diff --git a/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt b/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt deleted file mode 100644 index 88494581..00000000 --- a/core/src/main/kotlin/Kotlin/DeclarationLinkResolver.kt +++ /dev/null @@ -1,73 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.TypeAliasDescriptor -import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink - -class DeclarationLinkResolver - @Inject constructor(val resolutionFacade: DokkaResolutionFacade, - val refGraph: NodeReferenceGraph, - val logger: DokkaLogger, - val passConfiguration: DokkaConfiguration.PassConfiguration, - val externalDocumentationLinkResolver: ExternalDocumentationLinkResolver, - val elementSignatureProvider: ElementSignatureProvider) { - - - fun tryResolveContentLink(fromDescriptor: DeclarationDescriptor, href: String): ContentBlock? { - val symbol = try { - val symbols = resolveKDocLink(resolutionFacade.resolveSession.bindingContext, - resolutionFacade, fromDescriptor, null, href.split('.').toList()) - findTargetSymbol(symbols) - } catch(e: Exception) { - null - } - - // don't include unresolved links in generated doc - // assume that if an href doesn't contain '/', it's not an attempt to reference an external file - if (symbol != null) { - val externalHref = externalDocumentationLinkResolver.buildExternalDocumentationLink(symbol) - if (externalHref != null) { - return ContentExternalLink(externalHref) - } - val signature = elementSignatureProvider.signature(symbol) - val referencedAt = fromDescriptor.signatureWithSourceLocation() - - return ContentNodeLazyLink(href) { - val target = refGraph.lookup(signature) - - if (target == null) { - logger.warn("Can't find node by signature `$signature`, referenced at $referencedAt. " + - "This is probably caused by invalid configuration of cross-module dependencies") - } - target - } - } - if ("/" in href) { - return ContentExternalLink(href) - } - return null - } - - fun resolveContentLink(fromDescriptor: DeclarationDescriptor, href: String) = - tryResolveContentLink(fromDescriptor, href) ?: run { - logger.warn("Unresolved link to $href in doc comment of ${fromDescriptor.signatureWithSourceLocation()}") - ContentExternalLink("#") - } - - fun findTargetSymbol(symbols: Collection<DeclarationDescriptor>): DeclarationDescriptor? { - if (symbols.isEmpty()) { - return null - } - val symbol = symbols.first() - if (symbol is CallableMemberDescriptor && symbol.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) { - return symbol.overriddenDescriptors.firstOrNull() - } - if (symbol is TypeAliasDescriptor && !symbol.isDocumented(passConfiguration)) { - return symbol.classDescriptor - } - return symbol - } - -} diff --git a/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt b/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt deleted file mode 100644 index ce20aeec..00000000 --- a/core/src/main/kotlin/Kotlin/DescriptorDocumentationParser.kt +++ /dev/null @@ -1,177 +0,0 @@ -package org.jetbrains.dokka.Kotlin - -import com.google.inject.Inject -import com.intellij.psi.PsiDocCommentOwner -import com.intellij.psi.PsiNamedElement -import com.intellij.psi.util.PsiTreeUtil -import org.intellij.markdown.parser.LinkMap -import org.jetbrains.dokka.* -import org.jetbrains.dokka.Samples.SampleProcessingService -import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny -import org.jetbrains.kotlin.idea.kdoc.findKDoc -import org.jetbrains.kotlin.incremental.components.NoLookupLocation -import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag -import org.jetbrains.kotlin.kdoc.psi.api.KDoc -import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection -import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag -import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor -import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.psi.KtDeclaration -import org.jetbrains.kotlin.psi.KtElement -import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils -import org.jetbrains.kotlin.resolve.DescriptorUtils -import org.jetbrains.kotlin.resolve.annotations.argumentValue -import org.jetbrains.kotlin.resolve.constants.StringValue -import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe -import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter -import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered -import org.jetbrains.kotlin.resolve.source.PsiSourceElement - -class DescriptorDocumentationParser - @Inject constructor(val options: DokkaConfiguration.PassConfiguration, - val logger: DokkaLogger, - val linkResolver: DeclarationLinkResolver, - val resolutionFacade: DokkaResolutionFacade, - val refGraph: NodeReferenceGraph, - val sampleService: SampleProcessingService, - val signatureProvider: KotlinElementSignatureProvider, - val externalDocumentationLinkResolver: ExternalDocumentationLinkResolver -) -{ - fun parseDocumentation(descriptor: DeclarationDescriptor, inline: Boolean = false, isDefaultNoArgConstructor: Boolean = false): Content = - parseDocumentationAndDetails(descriptor, inline, isDefaultNoArgConstructor).first - - fun parseDocumentationAndDetails(descriptor: DeclarationDescriptor, inline: Boolean = false, isDefaultNoArgConstructor: Boolean = false): Pair<Content, (DocumentationNode) -> Unit> { - if (descriptor is JavaClassDescriptor || descriptor is JavaCallableMemberDescriptor) { - return parseJavadoc(descriptor) - } - - val kdoc = descriptor.findKDoc() ?: findStdlibKDoc(descriptor) - if (kdoc == null) { - if (options.effectivePackageOptions(descriptor.fqNameSafe).reportUndocumented && !descriptor.isDeprecated() && - descriptor !is ValueParameterDescriptor && descriptor !is TypeParameterDescriptor && - descriptor !is PropertyAccessorDescriptor && !descriptor.isSuppressWarning()) { - logger.warn("No documentation for ${descriptor.signatureWithSourceLocation()}") - } - return Content.Empty to { node -> } - } - - val contextDescriptor = - (PsiTreeUtil.getParentOfType(kdoc, KDoc::class.java)?.context as? KtDeclaration) - ?.takeIf { it != descriptor.original.sourcePsi() } - ?.resolveToDescriptorIfAny() - ?: descriptor - - var kdocText = if (isDefaultNoArgConstructor) { - getConstructorTagContent(descriptor) ?: kdoc.getContent() - } else kdoc.getContent() - - // workaround for code fence parsing problem in IJ markdown parser - if (kdocText.endsWith("```") || kdocText.endsWith("~~~")) { - kdocText += "\n" - } - val tree = parseMarkdown(kdocText) - val linkMap = LinkMap.buildLinkMap(tree.node, kdocText) - val content = buildContent(tree, LinkResolver(linkMap) { href -> linkResolver.resolveContentLink(contextDescriptor, href) }, inline) - if (kdoc is KDocSection) { - val tags = kdoc.getTags() - tags.forEach { - when (it.knownTag) { - KDocKnownTag.SAMPLE -> - content.append(sampleService.resolveSample(contextDescriptor, it.getSubjectName(), it)) - KDocKnownTag.SEE -> - content.addTagToSeeAlso(contextDescriptor, it) - else -> { - val section = content.addSection(javadocSectionDisplayName(it.name), it.getSubjectName()) - val sectionContent = it.getContent() - val markdownNode = parseMarkdown(sectionContent) - buildInlineContentTo(markdownNode, section, LinkResolver(linkMap) { href -> linkResolver.resolveContentLink(contextDescriptor, href) }) - } - } - } - } - return content to { node -> } - } - - private fun getConstructorTagContent(descriptor: DeclarationDescriptor): String? { - return ((DescriptorToSourceUtils.descriptorToDeclaration(descriptor)?.navigationElement as? KtElement) as KtDeclaration).docComment?.findSectionByTag( - KDocKnownTag.CONSTRUCTOR - )?.getContent() - } - - - private fun DeclarationDescriptor.isSuppressWarning() : Boolean { - val suppressAnnotation = annotations.findAnnotation(FqName(Suppress::class.qualifiedName!!)) - return if (suppressAnnotation != null) { - @Suppress("UNCHECKED_CAST") - (suppressAnnotation.argumentValue("names")?.value as List<StringValue>).any { it.value == "NOT_DOCUMENTED" } - } else containingDeclaration?.isSuppressWarning() ?: false - } - - /** - * Special case for generating stdlib documentation (the Any class to which the override chain will resolve - * is not the same one as the Any class included in the source scope). - */ - fun findStdlibKDoc(descriptor: DeclarationDescriptor): KDocTag? { - if (descriptor !is CallableMemberDescriptor) { - return null - } - val name = descriptor.name.asString() - if (name == "equals" || name == "hashCode" || name == "toString") { - var deepestDescriptor: CallableMemberDescriptor = descriptor - while (!deepestDescriptor.overriddenDescriptors.isEmpty()) { - deepestDescriptor = deepestDescriptor.overriddenDescriptors.first() - } - if (DescriptorUtils.getFqName(deepestDescriptor.containingDeclaration).asString() == "kotlin.Any") { - val anyClassDescriptors = resolutionFacade.resolveSession.getTopLevelClassifierDescriptors( - FqName.fromSegments(listOf("kotlin", "Any")), NoLookupLocation.FROM_IDE) - anyClassDescriptors.forEach { - val anyMethod = (it as ClassDescriptor).getMemberScope(listOf()) - .getDescriptorsFiltered(DescriptorKindFilter.FUNCTIONS) { it == descriptor.name } - .single() - val kdoc = anyMethod.findKDoc() - if (kdoc != null) { - return kdoc - } - } - } - } - return null - } - - fun parseJavadoc(descriptor: DeclarationDescriptor): Pair<Content, (DocumentationNode) -> Unit> { - val psi = ((descriptor as? DeclarationDescriptorWithSource)?.source as? PsiSourceElement)?.psi - if (psi is PsiDocCommentOwner) { - val parseResult = JavadocParser( - refGraph, - logger, - signatureProvider, - externalDocumentationLinkResolver - ).parseDocumentation(psi as PsiNamedElement) - return parseResult.content to { node -> - parseResult.deprecatedContent?.let { - val deprecationNode = DocumentationNode("", it, NodeKind.Modifier) - node.append(deprecationNode, RefKind.Deprecation) - } - } - } - return Content.Empty to { node -> } - } - - fun KDocSection.getTags(): Array<KDocTag> = PsiTreeUtil.getChildrenOfType(this, KDocTag::class.java) ?: arrayOf() - - private fun MutableContent.addTagToSeeAlso(descriptor: DeclarationDescriptor, seeTag: KDocTag) { - val subjectName = seeTag.getSubjectName() - if (subjectName != null) { - val seeSection = findSectionByTag("See Also") ?: addSection("See Also", null) - val link = linkResolver.resolveContentLink(descriptor, subjectName) - link.append(ContentText(subjectName)) - val para = ContentParagraph() - para.append(link) - seeSection.append(para) - } - } - -} diff --git a/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt b/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt deleted file mode 100644 index 13bbbb11..00000000 --- a/core/src/main/kotlin/Kotlin/DocumentationBuilder.kt +++ /dev/null @@ -1,1166 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import com.intellij.openapi.util.text.StringUtil -import com.intellij.psi.PsiJavaFile -import org.jetbrains.dokka.DokkaConfiguration.PassConfiguration -import org.jetbrains.dokka.Kotlin.DescriptorDocumentationParser -import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.coroutines.hasFunctionOrSuspendFunctionType -import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.descriptors.annotations.Annotated -import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor -import org.jetbrains.kotlin.descriptors.impl.EnumEntrySyntheticClassDescriptor -import org.jetbrains.kotlin.idea.kdoc.findKDoc -import org.jetbrains.kotlin.idea.util.fuzzyExtensionReceiverType -import org.jetbrains.kotlin.idea.util.makeNotNullable -import org.jetbrains.kotlin.idea.util.toFuzzyType -import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi -import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection -import org.jetbrains.kotlin.lexer.KtTokens -import org.jetbrains.kotlin.name.ClassId -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.psi.KtModifierListOwner -import org.jetbrains.kotlin.psi.KtParameter -import org.jetbrains.kotlin.psi.addRemoveModifier.MODIFIERS_ORDER -import org.jetbrains.kotlin.resolve.DescriptorUtils -import org.jetbrains.kotlin.resolve.constants.ConstantValue -import org.jetbrains.kotlin.resolve.descriptorUtil.* -import org.jetbrains.kotlin.resolve.findTopMostOverriddenDescriptors -import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter -import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered -import org.jetbrains.kotlin.resolve.source.PsiSourceElement -import org.jetbrains.kotlin.resolve.source.getPsi -import org.jetbrains.kotlin.types.* -import org.jetbrains.kotlin.types.typeUtil.immediateSupertypes -import org.jetbrains.kotlin.types.typeUtil.isAnyOrNullableAny -import org.jetbrains.kotlin.types.typeUtil.isTypeParameter -import org.jetbrains.kotlin.types.typeUtil.supertypes -import org.jetbrains.kotlin.util.supertypesWithAny -import com.google.inject.name.Named as GuiceNamed - -private fun isExtensionForExternalClass(extensionFunctionDescriptor: DeclarationDescriptor, - extensionReceiverDescriptor: DeclarationDescriptor, - allFqNames: Collection<FqName>): Boolean { - val extensionFunctionPackage = DescriptorUtils.getParentOfType(extensionFunctionDescriptor, PackageFragmentDescriptor::class.java) - val extensionReceiverPackage = DescriptorUtils.getParentOfType(extensionReceiverDescriptor, PackageFragmentDescriptor::class.java) - return extensionFunctionPackage != null && extensionReceiverPackage != null && - extensionFunctionPackage.fqName != extensionReceiverPackage.fqName && - extensionReceiverPackage.fqName !in allFqNames -} - -interface PackageDocumentationBuilder { - fun buildPackageDocumentation(documentationBuilder: DocumentationBuilder, - packageName: FqName, - packageNode: DocumentationNode, - declarations: List<DeclarationDescriptor>, - allFqNames: Collection<FqName>) -} - -interface DefaultPlatformsProvider { - fun getDefaultPlatforms(descriptor: DeclarationDescriptor): List<String> -} - -val ignoredSupertypes = setOf( - "kotlin.Annotation", "kotlin.Enum", "kotlin.Any" -) - -class DocumentationBuilder -@Inject constructor(val resolutionFacade: DokkaResolutionFacade, - val descriptorDocumentationParser: DescriptorDocumentationParser, - val passConfiguration: DokkaConfiguration.PassConfiguration, - val refGraph: NodeReferenceGraph, - val platformNodeRegistry: PlatformNodeRegistry, - val logger: DokkaLogger, - val linkResolver: DeclarationLinkResolver, - val defaultPlatformsProvider: DefaultPlatformsProvider) { - val boringBuiltinClasses = setOf( - "kotlin.Unit", "kotlin.Byte", "kotlin.Short", "kotlin.Int", "kotlin.Long", "kotlin.Char", "kotlin.Boolean", - "kotlin.Float", "kotlin.Double", "kotlin.String", "kotlin.Array", "kotlin.Any") - val knownModifiers = setOf( - KtTokens.PUBLIC_KEYWORD, KtTokens.PROTECTED_KEYWORD, KtTokens.INTERNAL_KEYWORD, KtTokens.PRIVATE_KEYWORD, - KtTokens.OPEN_KEYWORD, KtTokens.FINAL_KEYWORD, KtTokens.ABSTRACT_KEYWORD, KtTokens.SEALED_KEYWORD, - KtTokens.OVERRIDE_KEYWORD, KtTokens.INLINE_KEYWORD) - - fun link(node: DocumentationNode, descriptor: DeclarationDescriptor, kind: RefKind) { - refGraph.link(node, descriptor.signature(), kind) - } - - fun link(fromDescriptor: DeclarationDescriptor?, toDescriptor: DeclarationDescriptor?, kind: RefKind) { - if (fromDescriptor != null && toDescriptor != null) { - refGraph.link(fromDescriptor.signature(), toDescriptor.signature(), kind) - } - } - - fun register(descriptor: DeclarationDescriptor, node: DocumentationNode) { - refGraph.register(descriptor.signature(), node) - } - - fun <T> nodeForDescriptor( - descriptor: T, - kind: NodeKind, - external: Boolean = false - ): DocumentationNode where T : DeclarationDescriptor, T : Named { - val (doc, callback) = - if (external) { - Content.Empty to { node -> } - } else { - descriptorDocumentationParser.parseDocumentationAndDetails( - descriptor, - kind == NodeKind.Parameter - ) - } - val node = DocumentationNode(descriptor.name.asString(), doc, kind).withModifiers(descriptor) - node.appendSignature(descriptor) - callback(node) - return node - } - - private fun DocumentationNode.withModifiers(descriptor: DeclarationDescriptor): DocumentationNode { - if (descriptor is MemberDescriptor) { - appendVisibility(descriptor) - if (descriptor !is ConstructorDescriptor) { - appendModality(descriptor) - } - } - return this - } - - fun DocumentationNode.appendModality(descriptor: MemberDescriptor) { - var modality = descriptor.modality - if (modality == Modality.OPEN) { - val containingClass = descriptor.containingDeclaration as? ClassDescriptor - if (containingClass?.modality == Modality.FINAL) { - modality = Modality.FINAL - } - } - val modifier = modality.name.toLowerCase() - appendTextNode(modifier, NodeKind.Modifier) - } - - fun DocumentationNode.appendInline(descriptor: DeclarationDescriptor, psi: KtModifierListOwner) { - if (!psi.hasModifier(KtTokens.INLINE_KEYWORD)) return - if (descriptor is FunctionDescriptor - && descriptor.valueParameters.none { it.hasFunctionOrSuspendFunctionType }) return - appendTextNode(KtTokens.INLINE_KEYWORD.value, NodeKind.Modifier) - } - - fun DocumentationNode.appendVisibility(descriptor: DeclarationDescriptorWithVisibility) { - val modifier = descriptor.visibility.normalize().displayName - appendTextNode(modifier, NodeKind.Modifier) - } - - fun DocumentationNode.appendSupertype(descriptor: ClassDescriptor, superType: KotlinType, backref: Boolean) { - val unwrappedType = superType.unwrap() - if (unwrappedType is AbbreviatedType) { - appendSupertype(descriptor, unwrappedType.abbreviation, backref) - } else { - appendType(unwrappedType, NodeKind.Supertype) - val superclass = unwrappedType.constructor.declarationDescriptor - if (backref) { - link(superclass, descriptor, RefKind.Inheritor) - } - link(descriptor, superclass, RefKind.Superclass) - } - } - - fun DocumentationNode.appendProjection(projection: TypeProjection, kind: NodeKind = NodeKind.Type) { - if (projection.isStarProjection) { - appendTextNode("*", NodeKind.Type) - } else { - appendType(projection.type, kind, projection.projectionKind.label) - } - } - - fun DocumentationNode.appendType(kotlinType: KotlinType?, kind: NodeKind = NodeKind.Type, prefix: String = "") { - if (kotlinType == null) - return - (kotlinType.unwrap() as? AbbreviatedType)?.let { - return appendType(it.abbreviation) - } - - if (kotlinType.isDynamic()) { - append(DocumentationNode("dynamic", Content.Empty, kind), RefKind.Detail) - return - } - - val classifierDescriptor = kotlinType.constructor.declarationDescriptor - val name = when (classifierDescriptor) { - is ClassDescriptor -> { - if (classifierDescriptor.isCompanionObject) { - classifierDescriptor.containingDeclaration.name.asString() + - "." + classifierDescriptor.name.asString() - } else { - classifierDescriptor.name.asString() - } - } - is Named -> classifierDescriptor.name.asString() - else -> "<anonymous>" - } - val node = DocumentationNode(name, Content.Empty, kind) - if (prefix != "") { - node.appendTextNode(prefix, NodeKind.Modifier) - } - if (kotlinType.isNullabilityFlexible()) { - node.appendTextNode("!", NodeKind.NullabilityModifier) - } else if (kotlinType.isMarkedNullable) { - node.appendTextNode("?", NodeKind.NullabilityModifier) - } - if (classifierDescriptor != null) { - val externalLink = - linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(classifierDescriptor) - if (externalLink != null) { - if (classifierDescriptor !is TypeParameterDescriptor) { - val targetNode = - refGraph.lookup(classifierDescriptor.signature()) ?: classifierDescriptor.build(true) - node.append(targetNode, RefKind.ExternalType) - node.append(DocumentationNode(externalLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link) - } - } else { - link( - node, classifierDescriptor, - if (classifierDescriptor.isBoringBuiltinClass()) RefKind.HiddenLink else RefKind.Link - ) - } - if (classifierDescriptor !is TypeParameterDescriptor) { - node.append( - DocumentationNode( - classifierDescriptor.fqNameUnsafe.asString(), - Content.Empty, - NodeKind.QualifiedName - ), RefKind.Detail - ) - } - } - - - append(node, RefKind.Detail) - node.appendAnnotations(kotlinType) - for (typeArgument in kotlinType.arguments) { - node.appendProjection(typeArgument) - } - } - - fun ClassifierDescriptor.isBoringBuiltinClass(): Boolean = - DescriptorUtils.getFqName(this).asString() in boringBuiltinClasses - - fun DocumentationNode.appendAnnotations(annotated: Annotated) { - annotated.annotations.forEach { - it.build()?.let { annotationNode -> - if (annotationNode.isSinceKotlin()) { - appendSinceKotlin(annotationNode) - } - else { - val refKind = when { - it.isDocumented() -> - when { - annotationNode.isDeprecation() -> RefKind.Deprecation - else -> RefKind.Annotation - } - it.isHiddenInDocumentation() -> RefKind.HiddenAnnotation - else -> return@forEach - } - append(annotationNode, refKind) - } - - } - } - } - - fun DocumentationNode.appendExternalLink(externalLink: String) { - append(DocumentationNode(externalLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link) - } - - fun DocumentationNode.appendExternalLink(descriptor: DeclarationDescriptor) { - val target = linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(descriptor) - if (target != null) { - appendExternalLink(target) - } - } - - fun DocumentationNode.appendSinceKotlin(annotation: DocumentationNode) { - val kotlinVersion = annotation - .detail(NodeKind.Parameter) - .detail(NodeKind.Value) - .name.removeSurrounding("\"") - - sinceKotlin = kotlinVersion - } - - fun DocumentationNode.appendDefaultSinceKotlin() { - if (sinceKotlin == null) { - sinceKotlin = passConfiguration.sinceKotlin - } - } - - fun DocumentationNode.appendModifiers(descriptor: DeclarationDescriptor) { - val psi = (descriptor as DeclarationDescriptorWithSource).source.getPsi() as? KtModifierListOwner ?: return - KtTokens.MODIFIER_KEYWORDS_ARRAY.filter { - it !in knownModifiers - }.sortedBy { - MODIFIERS_ORDER.indexOf(it) - }.forEach { - if (psi.hasModifier(it)) { - appendTextNode(it.value, NodeKind.Modifier) - } - } - appendInline(descriptor, psi) - } - - fun DocumentationNode.appendDefaultPlatforms(descriptor: DeclarationDescriptor) { - for (platform in defaultPlatformsProvider.getDefaultPlatforms(descriptor)) { - append(platformNodeRegistry[platform], RefKind.Platform) - } - } - - fun DocumentationNode.isDeprecation() = name == "Deprecated" || name == "deprecated" - - fun DocumentationNode.isSinceKotlin() = name == "SinceKotlin" && kind == NodeKind.Annotation - - fun DocumentationNode.appendSourceLink(sourceElement: SourceElement) { - appendSourceLink(sourceElement.getPsi(), passConfiguration.sourceLinks) - } - - fun DocumentationNode.appendSignature(descriptor: DeclarationDescriptor) { - appendTextNode(descriptor.signature(), NodeKind.Signature, RefKind.Detail) - } - - fun DocumentationNode.appendChild(descriptor: DeclarationDescriptor, kind: RefKind): DocumentationNode? { - if (!descriptor.isGenerated() && descriptor.isDocumented(passConfiguration)) { - val node = descriptor.build() - append(node, kind) - return node - } - return null - } - - fun createGroupNode(signature: String, nodes: List<DocumentationNode>) = (nodes.find { it.kind == NodeKind.GroupNode } ?: - DocumentationNode(nodes.first().name, Content.Empty, NodeKind.GroupNode).apply { - appendTextNode(signature, NodeKind.Signature, RefKind.Detail) - }) - .also { groupNode -> - nodes.forEach { node -> - if (node != groupNode) { - node.owner?.let { owner -> - node.dropReferences { it.to == owner && it.kind == RefKind.Owner } - owner.dropReferences { it.to == node && it.kind == RefKind.Member } - owner.append(groupNode, RefKind.Member) - } - groupNode.append(node, RefKind.Member) - } - } - } - - - fun DocumentationNode.appendOrUpdateMember(descriptor: DeclarationDescriptor) { - if (descriptor.isGenerated() || !descriptor.isDocumented(passConfiguration)) return - - val existingNode = refGraph.lookup(descriptor.signature()) - if (existingNode != null) { - if (existingNode.kind == NodeKind.TypeAlias && descriptor is ClassDescriptor - || existingNode.kind == NodeKind.Class && descriptor is TypeAliasDescriptor) { - val node = createGroupNode(descriptor.signature(), listOf(existingNode, descriptor.build())) - register(descriptor, node) - return - } - - existingNode.updatePlatforms(descriptor) - - if (descriptor is ClassDescriptor) { - val membersToDocument = descriptor.collectMembersToDocument() - for ((memberDescriptor, inheritedLinkKind, extraModifier) in membersToDocument) { - if (memberDescriptor is ClassDescriptor) { - existingNode.appendOrUpdateMember(memberDescriptor) // recurse into nested classes - } - else { - val existingMemberNode = refGraph.lookup(memberDescriptor.signature()) - if (existingMemberNode != null) { - existingMemberNode.updatePlatforms(memberDescriptor) - } - else { - existingNode.appendClassMember(memberDescriptor, inheritedLinkKind, extraModifier) - } - } - } - } - } - else { - appendChild(descriptor, RefKind.Member) - } - } - - private fun DocumentationNode.updatePlatforms(descriptor: DeclarationDescriptor) { - for (platform in defaultPlatformsProvider.getDefaultPlatforms(descriptor) - platforms) { - append(platformNodeRegistry[platform], RefKind.Platform) - } - } - - fun DocumentationNode.appendClassMember(descriptor: DeclarationDescriptor, - inheritedLinkKind: RefKind = RefKind.InheritedMember, - extraModifier: String?) { - if (descriptor is CallableMemberDescriptor && descriptor.kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE) { - val baseDescriptor = descriptor.overriddenDescriptors.firstOrNull() - if (baseDescriptor != null) { - link(this, baseDescriptor, inheritedLinkKind) - } - } else { - val descriptorToUse = if (descriptor is ConstructorDescriptor) descriptor else descriptor.original - val child = appendChild(descriptorToUse, RefKind.Member) - if (extraModifier != null) { - child?.appendTextNode("static", NodeKind.Modifier) - } - } - } - - fun DocumentationNode.appendInPageChildren(descriptors: Iterable<DeclarationDescriptor>, kind: RefKind) { - descriptors.forEach { descriptor -> - val node = appendChild(descriptor, kind) - node?.addReferenceTo(this, RefKind.TopLevelPage) - } - } - - fun DocumentationModule.appendFragments(fragments: Collection<PackageFragmentDescriptor>, - packageContent: Map<String, Content>, - packageDocumentationBuilder: PackageDocumentationBuilder) { - val allFqNames = fragments.filter { it.isDocumented(passConfiguration) }.map { it.fqName }.distinct() - - for (packageName in allFqNames) { - if (packageName.isRoot && !passConfiguration.includeRootPackage) continue - val declarations = fragments.filter { it.fqName == packageName }.flatMap { it.getMemberScope().getContributedDescriptors() } - - if (passConfiguration.skipEmptyPackages && declarations.none { it.isDocumented(passConfiguration) }) continue - logger.info(" package $packageName: ${declarations.count()} declarations") - val packageNode = findOrCreatePackageNode(this, packageName.asString(), packageContent, this@DocumentationBuilder.refGraph) - packageDocumentationBuilder.buildPackageDocumentation(this@DocumentationBuilder, packageName, packageNode, - declarations, allFqNames) - } - - } - - fun propagateExtensionFunctionsToSubclasses( - fragments: Collection<PackageFragmentDescriptor>, - resolutionFacade: DokkaResolutionFacade - ) { - - val moduleDescriptor = resolutionFacade.moduleDescriptor - - // Wide-collect all view descriptors - val allPackageViewDescriptors = generateSequence(listOf(moduleDescriptor.getPackage(FqName.ROOT))) { packages -> - packages - .flatMap { pkg -> - moduleDescriptor.getSubPackagesOf(pkg.fqName) { true } - }.map { fqName -> - moduleDescriptor.getPackage(fqName) - }.takeUnless { it.isEmpty() } - }.flatten() - - val allDescriptors = - if (passConfiguration.collectInheritedExtensionsFromLibraries) { - allPackageViewDescriptors.map { it.memberScope } - } else { - fragments.asSequence().map { it.getMemberScope() } - }.flatMap { - it.getDescriptorsFiltered( - DescriptorKindFilter.CALLABLES - ).asSequence() - } - - - val documentingDescriptors = fragments.flatMap { it.getMemberScope().getContributedDescriptors() } - val documentingClasses = documentingDescriptors.filterIsInstance<ClassDescriptor>() - - val classHierarchy = buildClassHierarchy(documentingClasses) - - val allExtensionFunctions = - allDescriptors - .filterIsInstance<CallableMemberDescriptor>() - .filter { it.extensionReceiverParameter != null } - val extensionFunctionsByName = allExtensionFunctions.groupBy { it.name } - - fun isIgnoredReceiverType(type: KotlinType) = - type.isDynamic() || - type.isAnyOrNullableAny() || - (type.isTypeParameter() && type.immediateSupertypes().all { it.isAnyOrNullableAny() }) - - - for (extensionFunction in allExtensionFunctions) { - val extensionReceiverParameter = extensionFunction.extensionReceiverParameter!! - if (extensionFunction.dispatchReceiverParameter != null) continue - val possiblyShadowingFunctions = extensionFunctionsByName[extensionFunction.name] - ?.filter { fn -> fn.canShadow(extensionFunction) } - ?: emptyList() - - if (isIgnoredReceiverType(extensionReceiverParameter.type)) continue - val subclasses = - classHierarchy.filter { (key) -> key.isExtensionApplicable(extensionFunction) } - if (subclasses.isEmpty()) continue - subclasses.values.flatten().forEach { subclass -> - if (subclass.isExtensionApplicable(extensionFunction) && - possiblyShadowingFunctions.none { subclass.isExtensionApplicable(it) }) { - - val hasExternalLink = - linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink( - extensionFunction - ) != null - if (hasExternalLink) { - val containerDesc = - extensionFunction.containingDeclaration as? PackageFragmentDescriptor - if (containerDesc != null) { - val container = refGraph.lookup(containerDesc.signature()) - ?: containerDesc.buildExternal() - container.append(extensionFunction.buildExternal(), RefKind.Member) - } - } - - refGraph.link(subclass.signature(), extensionFunction.signature(), RefKind.Extension) - } - } - } - } - - private fun ClassDescriptor.isExtensionApplicable(extensionFunction: CallableMemberDescriptor): Boolean { - val receiverType = extensionFunction.fuzzyExtensionReceiverType()?.makeNotNullable() - val classType = defaultType.toFuzzyType(declaredTypeParameters) - return receiverType != null && classType.checkIsSubtypeOf(receiverType) != null - } - - private fun buildClassHierarchy(classes: List<ClassDescriptor>): Map<ClassDescriptor, List<ClassDescriptor>> { - val result = hashMapOf<ClassDescriptor, MutableList<ClassDescriptor>>() - classes.forEach { cls -> - TypeUtils.getAllSupertypes(cls.defaultType).forEach { supertype -> - val classDescriptor = supertype.constructor.declarationDescriptor as? ClassDescriptor - if (classDescriptor != null) { - val subtypesList = result.getOrPut(classDescriptor) { arrayListOf() } - subtypesList.add(cls) - } - } - } - return result - } - - private fun CallableMemberDescriptor.canShadow(other: CallableMemberDescriptor): Boolean { - if (this == other) return false - if (this is PropertyDescriptor && other is PropertyDescriptor) { - return true - } - if (this is FunctionDescriptor && other is FunctionDescriptor) { - val parameters1 = valueParameters - val parameters2 = other.valueParameters - if (parameters1.size != parameters2.size) { - return false - } - for ((p1, p2) in parameters1 zip parameters2) { - if (p1.type != p2.type) { - return false - } - } - return true - } - return false - } - - fun DeclarationDescriptor.build(): DocumentationNode = when (this) { - is ClassifierDescriptor -> build() - is ConstructorDescriptor -> build() - is PropertyDescriptor -> build() - is FunctionDescriptor -> build() - is ValueParameterDescriptor -> build() - is ReceiverParameterDescriptor -> build() - else -> throw IllegalStateException("Descriptor $this is not known") - } - - fun PackageFragmentDescriptor.buildExternal(): DocumentationNode { - val node = DocumentationNode(fqName.asString(), Content.Empty, NodeKind.Package) - - val externalLink = linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(this) - if (externalLink != null) { - node.append(DocumentationNode(externalLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link) - } - register(this, node) - return node - } - - fun CallableDescriptor.buildExternal(): DocumentationNode = when(this) { - is FunctionDescriptor -> build(true) - is PropertyDescriptor -> build(true) - else -> throw IllegalStateException("Descriptor $this is not known") - } - - - fun ClassifierDescriptor.build(external: Boolean = false): DocumentationNode = when (this) { - is ClassDescriptor -> build(external) - is TypeAliasDescriptor -> build(external) - is TypeParameterDescriptor -> build() - else -> throw IllegalStateException("Descriptor $this is not known") - } - - fun TypeAliasDescriptor.build(external: Boolean = false): DocumentationNode { - val node = nodeForDescriptor(this, NodeKind.TypeAlias) - - if (!external) { - node.appendDefaultSinceKotlin() - node.appendAnnotations(this) - } - node.appendModifiers(this) - node.appendInPageChildren(typeConstructor.parameters, RefKind.Detail) - - node.appendType(underlyingType, NodeKind.TypeAliasUnderlyingType) - - if (!external) { - node.appendSourceLink(source) - node.appendDefaultPlatforms(this) - } - register(this, node) - return node - } - - fun ClassDescriptor.build(external: Boolean = false): DocumentationNode { - val kind = when { - kind == ClassKind.OBJECT -> NodeKind.Object - kind == ClassKind.INTERFACE -> NodeKind.Interface - kind == ClassKind.ENUM_CLASS -> NodeKind.Enum - kind == ClassKind.ANNOTATION_CLASS -> NodeKind.AnnotationClass - kind == ClassKind.ENUM_ENTRY -> NodeKind.EnumItem - isSubclassOfThrowable() -> NodeKind.Exception - else -> NodeKind.Class - } - val node = nodeForDescriptor(this, kind, external) - register(this, node) - supertypesWithAnyPrecise().forEach { - node.appendSupertype(this, it, !external) - } - if (getKind() != ClassKind.OBJECT && getKind() != ClassKind.ENUM_ENTRY) { - node.appendInPageChildren(typeConstructor.parameters, RefKind.Detail) - } - if (!external) { - for ((descriptor, inheritedLinkKind, extraModifier) in collectMembersToDocument()) { - node.appendClassMember(descriptor, inheritedLinkKind, extraModifier) - } - node.appendDefaultSinceKotlin() - node.appendAnnotations(this) - } - node.appendModifiers(this) - if (!external) { - node.appendSourceLink(source) - node.appendDefaultPlatforms(this) - } - return node - } - - data class ClassMember(val descriptor: DeclarationDescriptor, - val inheritedLinkKind: RefKind = RefKind.InheritedMember, - val extraModifier: String? = null) - - fun ClassDescriptor.collectMembersToDocument(): List<ClassMember> { - val result = arrayListOf<ClassMember>() - if (kind != ClassKind.OBJECT && kind != ClassKind.ENUM_ENTRY) { - val constructorsToDocument = if (kind == ClassKind.ENUM_CLASS) - constructors.filter { it.valueParameters.size > 0 } - else - constructors - constructorsToDocument.mapTo(result) { ClassMember(it) } - } - - defaultType.memberScope.getContributedDescriptors() - .filter { it != companionObjectDescriptor } - .mapTo(result) { ClassMember(it) } - - staticScope.getContributedDescriptors() - .mapTo(result) { ClassMember(it, extraModifier = "static") } - - val companionObjectDescriptor = companionObjectDescriptor - if (companionObjectDescriptor != null && companionObjectDescriptor.isDocumented(passConfiguration)) { - val descriptors = companionObjectDescriptor.defaultType.memberScope.getContributedDescriptors() - val descriptorsToDocument = descriptors.filter { it !is CallableDescriptor || !it.isInheritedFromAny() } - descriptorsToDocument.mapTo(result) { - ClassMember(it, inheritedLinkKind = RefKind.InheritedCompanionObjectMember) - } - - if (companionObjectDescriptor.getAllSuperclassesWithoutAny().isNotEmpty() - || companionObjectDescriptor.getSuperInterfaces().isNotEmpty()) { - result += ClassMember(companionObjectDescriptor) - } - } - return result - } - - fun CallableDescriptor.isInheritedFromAny(): Boolean { - return findTopMostOverriddenDescriptors().any { - DescriptorUtils.getFqNameSafe(it.containingDeclaration).asString() == "kotlin.Any" - } - } - - fun ClassDescriptor.isSubclassOfThrowable(): Boolean = - defaultType.supertypes().any { it.constructor.declarationDescriptor == builtIns.throwable } - - fun ConstructorDescriptor.build(): DocumentationNode { - val node = nodeForDescriptor(this, NodeKind.Constructor) - node.appendInPageChildren(valueParameters, RefKind.Detail) - node.appendDefaultPlatforms(this) - node.appendDefaultSinceKotlin() - register(this, node) - return node - } - - private fun CallableMemberDescriptor.inCompanionObject(): Boolean { - val containingDeclaration = containingDeclaration - if ((containingDeclaration as? ClassDescriptor)?.isCompanionObject ?: false) { - return true - } - val receiver = extensionReceiverParameter - return (receiver?.type?.constructor?.declarationDescriptor as? ClassDescriptor)?.isCompanionObject ?: false - } - - fun FunctionDescriptor.build(external: Boolean = false): DocumentationNode { - if (ErrorUtils.containsErrorTypeInParameters(this) || ErrorUtils.containsErrorType(this.returnType)) { - logger.warn("Found an unresolved type in ${signatureWithSourceLocation()}") - } - - val node = nodeForDescriptor(this, if (inCompanionObject()) NodeKind.CompanionObjectFunction else NodeKind.Function, external) - - node.appendInPageChildren(typeParameters, RefKind.Detail) - extensionReceiverParameter?.let { node.appendChild(it, RefKind.Detail) } - node.appendInPageChildren(valueParameters, RefKind.Detail) - node.appendType(returnType) - if (!external) { - node.appendDefaultSinceKotlin() - } - node.appendAnnotations(this) - node.appendModifiers(this) - if (!external) { - node.appendSourceLink(source) - node.appendDefaultPlatforms(this) - } else { - node.appendExternalLink(this) - } - - overriddenDescriptors.forEach { - addOverrideLink(it, this) - } - - register(this, node) - return node - } - - fun addOverrideLink(baseClassFunction: CallableMemberDescriptor, overridingFunction: CallableMemberDescriptor) { - val source = baseClassFunction.original.source.getPsi() - if (source != null) { - link(overridingFunction, baseClassFunction, RefKind.Override) - } else { - baseClassFunction.overriddenDescriptors.forEach { - addOverrideLink(it, overridingFunction) - } - } - } - - fun PropertyDescriptor.build(external: Boolean = false): DocumentationNode { - val node = nodeForDescriptor( - this, - if (inCompanionObject()) NodeKind.CompanionObjectProperty else NodeKind.Property, - external - ) - node.appendInPageChildren(typeParameters, RefKind.Detail) - extensionReceiverParameter?.let { node.appendChild(it, RefKind.Detail) } - node.appendType(returnType) - if (!external) { - node.appendDefaultSinceKotlin() - } - node.appendAnnotations(this) - node.appendModifiers(this) - if (!external) { - node.appendSourceLink(source) - if (isVar) { - node.appendTextNode("var", NodeKind.Modifier) - } - - if (isConst) { - this.compileTimeInitializer?.toDocumentationNode()?.let { node.append(it, RefKind.Detail) } - } - - - getter?.let { - if (!it.isDefault) { - node.addAccessorDocumentation(descriptorDocumentationParser.parseDocumentation(it), "Getter") - } - } - setter?.let { - if (!it.isDefault) { - node.addAccessorDocumentation(descriptorDocumentationParser.parseDocumentation(it), "Setter") - } - } - node.appendDefaultPlatforms(this) - } - if (external) { - node.appendExternalLink(this) - } - - overriddenDescriptors.forEach { - addOverrideLink(it, this) - } - - register(this, node) - return node - } - - fun DocumentationNode.addAccessorDocumentation(documentation: Content, prefix: String) { - if (documentation == Content.Empty) return - updateContent { - if (!documentation.children.isEmpty()) { - val section = addSection(prefix, null) - documentation.children.forEach { section.append(it) } - } - documentation.sections.forEach { - val section = addSection("$prefix ${it.tag}", it.subjectName) - it.children.forEach { section.append(it) } - } - } - } - - fun ValueParameterDescriptor.build(): DocumentationNode { - val node = nodeForDescriptor(this, NodeKind.Parameter) - node.appendType(varargElementType ?: type) - if (declaresDefaultValue()) { - val psi = source.getPsi() as? KtParameter - if (psi != null) { - val defaultValueText = psi.defaultValue?.text - if (defaultValueText != null) { - node.appendTextNode(defaultValueText, NodeKind.Value) - } - } - } - node.appendDefaultSinceKotlin() - node.appendAnnotations(this) - node.appendModifiers(this) - if (varargElementType != null && node.details(NodeKind.Modifier).none { it.name == "vararg" }) { - node.appendTextNode("vararg", NodeKind.Modifier) - } - register(this, node) - return node - } - - fun TypeParameterDescriptor.build(): DocumentationNode { - val doc = descriptorDocumentationParser.parseDocumentation(this) - val name = name.asString() - val prefix = variance.label - - val node = DocumentationNode(name, doc, NodeKind.TypeParameter) - if (prefix != "") { - node.appendTextNode(prefix, NodeKind.Modifier) - } - if (isReified) { - node.appendTextNode("reified", NodeKind.Modifier) - } - - for (constraint in upperBounds) { - if (KotlinBuiltIns.isDefaultBound(constraint)) { - continue - } - node.appendType(constraint, NodeKind.UpperBound) - } - register(this, node) - return node - } - - fun ReceiverParameterDescriptor.build(): DocumentationNode { - var receiverClass: DeclarationDescriptor = type.constructor.declarationDescriptor!! - if ((receiverClass as? ClassDescriptor)?.isCompanionObject ?: false) { - receiverClass = receiverClass.containingDeclaration!! - } else if (receiverClass is TypeParameterDescriptor) { - val upperBoundClass = receiverClass.upperBounds.singleOrNull()?.constructor?.declarationDescriptor - if (upperBoundClass != null) { - receiverClass = upperBoundClass - } - } - - if ((containingDeclaration as? FunctionDescriptor)?.dispatchReceiverParameter == null) { - link(receiverClass, containingDeclaration, RefKind.Extension) - } - - val node = DocumentationNode(name.asString(), Content.Empty, NodeKind.Receiver) - node.appendType(type) - register(this, node) - return node - } - - fun AnnotationDescriptor.build(): DocumentationNode? { - val annotationClass = type.constructor.declarationDescriptor - if (annotationClass == null || ErrorUtils.isError(annotationClass)) { - return null - } - val node = DocumentationNode(annotationClass.name.asString(), Content.Empty, NodeKind.Annotation) - allValueArguments.forEach { (name, value) -> - val valueNode = value.toDocumentationNode() - if (valueNode != null) { - val paramNode = DocumentationNode(name.asString(), Content.Empty, NodeKind.Parameter) - paramNode.append(valueNode, RefKind.Detail) - node.append(paramNode, RefKind.Detail) - } - } - return node - } - - fun ConstantValue<*>.toDocumentationNode(): DocumentationNode? = value.let { value -> - val text = when (value) { - is String -> - "\"" + StringUtil.escapeStringCharacters(value) + "\"" - is EnumEntrySyntheticClassDescriptor -> - value.containingDeclaration.name.asString() + "." + value.name.asString() - is Pair<*, *> -> { - val (classId, name) = value - if (classId is ClassId && name is Name) { - classId.shortClassName.asString() + "." + name.asString() - } else { - value.toString() - } - } - else -> "$value" - } - DocumentationNode(text, Content.Empty, NodeKind.Value) - } - - - fun DocumentationNode.getParentForPackageMember( - descriptor: DeclarationDescriptor, - externalClassNodes: MutableMap<FqName, DocumentationNode>, - allFqNames: Collection<FqName>, - packageName: FqName - ): DocumentationNode { - if (descriptor is CallableMemberDescriptor) { - val extensionClassDescriptor = descriptor.getExtensionClassDescriptor() - if (extensionClassDescriptor != null && isExtensionForExternalClass(descriptor, extensionClassDescriptor, allFqNames) && - !ErrorUtils.isError(extensionClassDescriptor)) { - val fqName = DescriptorUtils.getFqNameSafe(extensionClassDescriptor) - return externalClassNodes.getOrPut(fqName) { - val newNode = DocumentationNode(fqName.asString(), Content.Empty, NodeKind.ExternalClass) - val externalLink = linkResolver.externalDocumentationLinkResolver.buildExternalDocumentationLink(extensionClassDescriptor) - if (externalLink != null) { - newNode.append(DocumentationNode(externalLink, Content.Empty, NodeKind.ExternalLink), RefKind.Link) - } - append(newNode, RefKind.Member) - refGraph.register("${packageName.asString()}:${extensionClassDescriptor.signature()}", newNode) - newNode - } - } - } - return this - } - -} - -fun DeclarationDescriptor.isDocumented(passConfiguration: DokkaConfiguration.PassConfiguration): Boolean { - return (passConfiguration.effectivePackageOptions(fqNameSafe).includeNonPublic - || this !is MemberDescriptor - || this.visibility.isPublicAPI) - && !isDocumentationSuppressed(passConfiguration) - && (!passConfiguration.effectivePackageOptions(fqNameSafe).skipDeprecated || !isDeprecated()) -} - -private fun DeclarationDescriptor.isGenerated() = this is CallableMemberDescriptor && kind != CallableMemberDescriptor.Kind.DECLARATION - -class KotlinPackageDocumentationBuilder : PackageDocumentationBuilder { - override fun buildPackageDocumentation(documentationBuilder: DocumentationBuilder, - packageName: FqName, - packageNode: DocumentationNode, - declarations: List<DeclarationDescriptor>, - allFqNames: Collection<FqName>) { - val externalClassNodes = hashMapOf<FqName, DocumentationNode>() - declarations.forEach { descriptor -> - with(documentationBuilder) { - if (descriptor.isDocumented(passConfiguration)) { - val parent = packageNode.getParentForPackageMember( - descriptor, - externalClassNodes, - allFqNames, - packageName - ) - parent.appendOrUpdateMember(descriptor) - } - } - } - } -} - -class KotlinJavaDocumentationBuilder -@Inject constructor(val resolutionFacade: DokkaResolutionFacade, - val documentationBuilder: DocumentationBuilder, - val passConfiguration: DokkaConfiguration.PassConfiguration, - val logger: DokkaLogger) : JavaDocumentationBuilder { - override fun appendFile(file: PsiJavaFile, module: DocumentationModule, packageContent: Map<String, Content>) { - val classDescriptors = file.classes.map { - it.getJavaClassDescriptor(resolutionFacade) - } - - if (classDescriptors.any { it != null && it.isDocumented(passConfiguration) }) { - val packageNode = findOrCreatePackageNode(module, file.packageName, packageContent, documentationBuilder.refGraph) - - for (descriptor in classDescriptors.filterNotNull()) { - with(documentationBuilder) { - packageNode.appendChild(descriptor, RefKind.Member) - } - } - } - } -} - -private val hiddenAnnotations = setOf( - KotlinBuiltIns.FQ_NAMES.parameterName.asString() -) - -private fun AnnotationDescriptor.isHiddenInDocumentation() = - type.constructor.declarationDescriptor?.fqNameSafe?.asString() in hiddenAnnotations - -private fun AnnotationDescriptor.isDocumented(): Boolean { - if (source.getPsi() != null && mustBeDocumented()) return true - val annotationClassName = type.constructor.declarationDescriptor?.fqNameSafe?.asString() - return annotationClassName == KotlinBuiltIns.FQ_NAMES.extensionFunctionType.asString() -} - -fun AnnotationDescriptor.mustBeDocumented(): Boolean { - val annotationClass = type.constructor.declarationDescriptor as? Annotated ?: return false - return annotationClass.isDocumentedAnnotation() -} - -fun DeclarationDescriptor.isDocumentationSuppressed(passConfiguration: DokkaConfiguration.PassConfiguration): Boolean { - - if (passConfiguration.effectivePackageOptions(fqNameSafe).suppress) return true - - val path = this.findPsi()?.containingFile?.virtualFile?.path - if (path != null) { - if (path in passConfiguration.suppressedFiles) return true - } - - val doc = findKDoc() - if (doc is KDocSection && doc.findTagByName("suppress") != null) return true - - return hasSuppressDocTag(sourcePsi()) -} - -fun DeclarationDescriptor.sourcePsi() = - ((original as? DeclarationDescriptorWithSource)?.source as? PsiSourceElement)?.psi - -fun DeclarationDescriptor.isDeprecated(): Boolean = annotations.any { - DescriptorUtils.getFqName(it.type.constructor.declarationDescriptor!!).asString() == "kotlin.Deprecated" -} || (this is ConstructorDescriptor && containingDeclaration.isDeprecated()) - -fun CallableMemberDescriptor.getExtensionClassDescriptor(): ClassifierDescriptor? { - val extensionReceiver = extensionReceiverParameter - if (extensionReceiver != null) { - val type = extensionReceiver.type - val receiverClass = type.constructor.declarationDescriptor as? ClassDescriptor - if (receiverClass?.isCompanionObject ?: false) { - return receiverClass?.containingDeclaration as? ClassifierDescriptor - } - return receiverClass - } - return null -} - -fun DeclarationDescriptor.signature(): String { - if (this != original) return original.signature() - return when (this) { - is ClassDescriptor, - is PackageFragmentDescriptor, - is PackageViewDescriptor, - is TypeAliasDescriptor -> DescriptorUtils.getFqName(this).asString() - - is PropertyDescriptor -> containingDeclaration.signature() + "$" + name + receiverSignature() - is FunctionDescriptor -> containingDeclaration.signature() + "$" + name + parameterSignature() - is ValueParameterDescriptor -> containingDeclaration.signature() + "/" + name - is TypeParameterDescriptor -> containingDeclaration.signature() + "*" + name - is ReceiverParameterDescriptor -> containingDeclaration.signature() + "/" + name - else -> throw UnsupportedOperationException("Don't know how to calculate signature for $this") - } -} - -fun PropertyDescriptor.receiverSignature(): String { - val receiver = extensionReceiverParameter - if (receiver != null) { - return "#" + receiver.type.signature() - } - return "" -} - -fun CallableMemberDescriptor.parameterSignature(): String { - val params = valueParameters.map { it.type }.toMutableList() - val extensionReceiver = extensionReceiverParameter - if (extensionReceiver != null) { - params.add(0, extensionReceiver.type) - } - return params.joinToString(prefix = "(", postfix = ")") { it.signature() } -} - -fun KotlinType.signature(): String { - val visited = hashSetOf<KotlinType>() - - fun KotlinType.signatureRecursive(): String { - if (this in visited) { - return "" - } - visited.add(this) - - val declarationDescriptor = constructor.declarationDescriptor ?: return "<null>" - val typeName = DescriptorUtils.getFqName(declarationDescriptor).asString() - if (arguments.isEmpty()) { - return typeName - } - return typeName + arguments.joinToString(prefix = "((", postfix = "))") { it.type.signatureRecursive() } - } - - return signatureRecursive() -} - -fun DeclarationDescriptor.signatureWithSourceLocation(): String { - val signature = signature() - val sourceLocation = sourceLocation() - return if (sourceLocation != null) "$signature ($sourceLocation)" else signature -} - -fun DeclarationDescriptor.sourceLocation(): String? { - val psi = sourcePsi() - if (psi != null) { - val fileName = psi.containingFile.name - val lineNumber = psi.lineNumber() - return if (lineNumber != null) "$fileName:$lineNumber" else fileName - } - return null -} - -fun DocumentationModule.prepareForGeneration(configuration: DokkaConfiguration) { - if (configuration.generateIndexPages) { - generateAllTypesNode() - } - nodeRefGraph.resolveReferences() -} - -fun DocumentationNode.generateAllTypesNode() { - val allTypes = members(NodeKind.Package) - .flatMap { it.members.filter { - it.kind in NodeKind.classLike || it.kind == NodeKind.ExternalClass - || (it.kind == NodeKind.GroupNode && it.origins.all { it.kind in NodeKind.classLike }) } } - .sortedBy { if (it.kind == NodeKind.ExternalClass) it.name.substringAfterLast('.').toLowerCase() else it.name.toLowerCase() } - - val allTypesNode = DocumentationNode("alltypes", Content.Empty, NodeKind.AllTypes) - for (typeNode in allTypes) { - allTypesNode.addReferenceTo(typeNode, RefKind.Member) - } - - append(allTypesNode, RefKind.Member) -} - -fun ClassDescriptor.supertypesWithAnyPrecise(): Collection<KotlinType> { - if (KotlinBuiltIns.isAny(this)) { - return emptyList() - } - return typeConstructor.supertypesWithAny() -} - -fun PassConfiguration.effectivePackageOptions(pack: String): DokkaConfiguration.PackageOptions { - val rootPackageOptions = PackageOptionsImpl("", includeNonPublic, reportUndocumented, skipDeprecated, false) - return perPackageOptions.firstOrNull { pack == it.prefix } - ?: perPackageOptions.firstOrNull { pack.startsWith(it.prefix + ".") } - ?: rootPackageOptions -} - -fun PassConfiguration.effectivePackageOptions(pack: FqName): DokkaConfiguration.PackageOptions = effectivePackageOptions(pack.asString()) - diff --git a/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt b/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt deleted file mode 100644 index bd8b9cf5..00000000 --- a/core/src/main/kotlin/Kotlin/ExternalDocumentationLinkResolver.kt +++ /dev/null @@ -1,305 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import com.google.inject.Singleton -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiMethod -import com.intellij.util.io.* -import org.jetbrains.dokka.Formats.FileGeneratorBasedFormatDescriptor -import org.jetbrains.dokka.Formats.FormatDescriptor -import org.jetbrains.dokka.Utilities.ServiceLocator -import org.jetbrains.dokka.Utilities.lookup -import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.descriptors.impl.EnumEntrySyntheticClassDescriptor -import org.jetbrains.kotlin.load.java.descriptors.* -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.resolve.DescriptorUtils -import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe -import org.jetbrains.kotlin.resolve.descriptorUtil.parents -import java.io.ByteArrayOutputStream -import java.io.PrintWriter -import java.net.HttpURLConnection -import java.net.URL -import java.net.URLConnection -import java.nio.file.Path -import java.nio.file.Paths -import java.security.MessageDigest -import javax.inject.Named -import kotlin.reflect.full.findAnnotation - -fun ByteArray.toHexString() = this.joinToString(separator = "") { "%02x".format(it) } - -typealias PackageFqNameToLocation = MutableMap<FqName, PackageListProvider.ExternalDocumentationRoot> - -@Singleton -class PackageListProvider @Inject constructor( - val configuration: DokkaConfiguration, - val logger: DokkaLogger -) { - val storage = mutableMapOf<DokkaConfiguration.ExternalDocumentationLink, PackageFqNameToLocation>() - - val cacheDir: Path? = when { - configuration.cacheRoot == "default" -> Paths.get(System.getProperty("user.home"), ".cache", "dokka") - configuration.cacheRoot != null -> Paths.get(configuration.cacheRoot) - else -> null - }?.resolve("packageListCache")?.apply { toFile().mkdirs() } - - val cachedProtocols = setOf("http", "https", "ftp") - - init { - for (conf in configuration.passesConfigurations) { - for (link in conf.externalDocumentationLinks) { - if (link in storage) { - continue - } - - try { - loadPackageList(link) - } catch (e: Exception) { - throw RuntimeException("Exception while loading package-list from $link", e) - } - } - - } - } - - - - fun URL.doOpenConnectionToReadContent(timeout: Int = 10000, redirectsAllowed: Int = 16): URLConnection { - val connection = this.openConnection() - connection.connectTimeout = timeout - connection.readTimeout = timeout - - when (connection) { - is HttpURLConnection -> { - return when (connection.responseCode) { - in 200..299 -> { - connection - } - HttpURLConnection.HTTP_MOVED_PERM, - HttpURLConnection.HTTP_MOVED_TEMP, - HttpURLConnection.HTTP_SEE_OTHER -> { - if (redirectsAllowed > 0) { - val newUrl = connection.getHeaderField("Location") - URL(newUrl).doOpenConnectionToReadContent(timeout, redirectsAllowed - 1) - } else { - throw RuntimeException("Too many redirects") - } - } - else -> { - throw RuntimeException("Unhandled http code: ${connection.responseCode}") - } - } - } - else -> return connection - } - } - - fun loadPackageList(link: DokkaConfiguration.ExternalDocumentationLink) { - - val packageListUrl = link.packageListUrl - val needsCache = packageListUrl.protocol in cachedProtocols - - val packageListStream = if (cacheDir != null && needsCache) { - val packageListLink = packageListUrl.toExternalForm() - - val digest = MessageDigest.getInstance("SHA-256") - val hash = digest.digest(packageListLink.toByteArray(Charsets.UTF_8)).toHexString() - val cacheEntry = cacheDir.resolve(hash).toFile() - - if (cacheEntry.exists()) { - try { - val connection = packageListUrl.doOpenConnectionToReadContent() - val originModifiedDate = connection.date - val cacheDate = cacheEntry.lastModified() - if (originModifiedDate > cacheDate || originModifiedDate == 0L) { - if (originModifiedDate == 0L) - logger.warn("No date header for $packageListUrl, downloading anyway") - else - logger.info("Renewing package-list from $packageListUrl") - connection.getInputStream().copyTo(cacheEntry.outputStream()) - } - } catch (e: Exception) { - logger.error("Failed to update package-list cache for $link") - val baos = ByteArrayOutputStream() - PrintWriter(baos).use { - e.printStackTrace(it) - } - baos.flush() - logger.error(baos.toString()) - } - } else { - logger.info("Downloading package-list from $packageListUrl") - packageListUrl.openStream().copyTo(cacheEntry.outputStream()) - } - cacheEntry.inputStream() - } else { - packageListUrl.doOpenConnectionToReadContent().getInputStream() - } - - val (params, packages) = - packageListStream - .bufferedReader() - .useLines { lines -> lines.partition { it.startsWith(ExternalDocumentationLinkResolver.DOKKA_PARAM_PREFIX) } } - - val paramsMap = params.asSequence() - .map { it.removePrefix(ExternalDocumentationLinkResolver.DOKKA_PARAM_PREFIX).split(":", limit = 2) } - .groupBy({ (key, _) -> key }, { (_, value) -> value }) - - val format = paramsMap["format"]?.singleOrNull() ?: "javadoc" - - val locations = paramsMap["location"].orEmpty() - .map { it.split("\u001f", limit = 2) } - .map { (key, value) -> key to value } - .toMap() - - - val defaultResolverDesc = ExternalDocumentationLinkResolver.services.getValue("dokka-default") - val resolverDesc = ExternalDocumentationLinkResolver.services[format] - ?: defaultResolverDesc.takeIf { format in formatsWithDefaultResolver } - ?: defaultResolverDesc.also { - logger.warn("Couldn't find InboundExternalLinkResolutionService(format = `$format`) for $link, using Dokka default") - } - - - val resolverClass = javaClass.classLoader.loadClass(resolverDesc.className).kotlin - - val constructors = resolverClass.constructors - - val constructor = constructors.singleOrNull() - ?: constructors.first { it.findAnnotation<Inject>() != null } - val resolver = constructor.call(paramsMap) as InboundExternalLinkResolutionService - - val rootInfo = ExternalDocumentationRoot(link.url, resolver, locations) - - val packageFqNameToLocation = mutableMapOf<FqName, ExternalDocumentationRoot>() - storage[link] = packageFqNameToLocation - - val fqNames = packages.map { FqName(it) } - for(name in fqNames) { - packageFqNameToLocation[name] = rootInfo - } - } - - - - class ExternalDocumentationRoot(val rootUrl: URL, val resolver: InboundExternalLinkResolutionService, val locations: Map<String, String>) { - override fun toString(): String = rootUrl.toString() - } - - companion object { - private val formatsWithDefaultResolver = - ServiceLocator - .allServices("format") - .filter { - val desc = ServiceLocator.lookup<FormatDescriptor>(it) as? FileGeneratorBasedFormatDescriptor - desc?.generatorServiceClass == FileGenerator::class - }.map { it.name } - .toSet() - - } - -} - -class ExternalDocumentationLinkResolver @Inject constructor( - val configuration: DokkaConfiguration, - val passConfiguration: DokkaConfiguration.PassConfiguration, - @Named("libraryResolutionFacade") val libraryResolutionFacade: DokkaResolutionFacade, - val logger: DokkaLogger, - val packageListProvider: PackageListProvider -) { - - val formats = mutableMapOf<String, InboundExternalLinkResolutionService>() - val packageFqNameToLocation = mutableMapOf<FqName, PackageListProvider.ExternalDocumentationRoot>() - - init { - val fqNameToLocationMaps = passConfiguration.externalDocumentationLinks - .mapNotNull { packageListProvider.storage[it] } - - for(map in fqNameToLocationMaps) { - packageFqNameToLocation.putAll(map) - } - } - - fun buildExternalDocumentationLink(element: PsiElement): String? { - return element.extractDescriptor(libraryResolutionFacade)?.let { - buildExternalDocumentationLink(it) - } - } - - fun buildExternalDocumentationLink(symbol: DeclarationDescriptor): String? { - val packageFqName: FqName = - when (symbol) { - is PackageFragmentDescriptor -> symbol.fqName - is DeclarationDescriptorNonRoot -> symbol.parents.firstOrNull { it is PackageFragmentDescriptor }?.fqNameSafe ?: return null - else -> return null - } - - val externalLocation = packageFqNameToLocation[packageFqName] ?: return null - - val path = externalLocation.locations[symbol.signature()] ?: - externalLocation.resolver.getPath(symbol) ?: return null - - return URL(externalLocation.rootUrl, path).toExternalForm() - } - - companion object { - const val DOKKA_PARAM_PREFIX = "\$dokka." - val services = ServiceLocator.allServices("inbound-link-resolver").associateBy { it.name } - } -} - - -interface InboundExternalLinkResolutionService { - fun getPath(symbol: DeclarationDescriptor): String? - - class Javadoc(paramsMap: Map<String, List<String>>) : InboundExternalLinkResolutionService { - override fun getPath(symbol: DeclarationDescriptor): String? { - if (symbol is EnumEntrySyntheticClassDescriptor) { - return getPath(symbol.containingDeclaration)?.let { it + "#" + symbol.name.asString() } - } else if (symbol is JavaClassDescriptor) { - return DescriptorUtils.getFqName(symbol).asString().replace(".", "/") + ".html" - } else if (symbol is JavaCallableMemberDescriptor) { - val containingClass = symbol.containingDeclaration as? JavaClassDescriptor ?: return null - val containingClassLink = getPath(containingClass) - if (containingClassLink != null) { - if (symbol is JavaMethodDescriptor || symbol is JavaClassConstructorDescriptor) { - val psi = symbol.sourcePsi() as? PsiMethod - if (psi != null) { - val params = psi.parameterList.parameters.joinToString { it.type.canonicalText } - return containingClassLink + "#" + symbol.name + "(" + params + ")" - } - } else if (symbol is JavaPropertyDescriptor) { - return "$containingClassLink#${symbol.name}" - } - } - } - // TODO Kotlin javadoc - return null - } - } - - class Dokka(val paramsMap: Map<String, List<String>>) : InboundExternalLinkResolutionService { - val extension = paramsMap["linkExtension"]?.singleOrNull() ?: error("linkExtension not provided for Dokka resolver") - - override fun getPath(symbol: DeclarationDescriptor): String? { - val leafElement = when (symbol) { - is CallableDescriptor, is TypeAliasDescriptor -> true - else -> false - } - val path = getPathWithoutExtension(symbol) - if (leafElement) return "$path.$extension" - else return "$path/index.$extension" - } - - private fun getPathWithoutExtension(symbol: DeclarationDescriptor): String { - return when { - symbol.containingDeclaration == null -> identifierToFilename(symbol.name.asString()) - symbol is PackageFragmentDescriptor -> identifierToFilename(symbol.fqName.asString()) - else -> getPathWithoutExtension(symbol.containingDeclaration!!) + '/' + identifierToFilename(symbol.name.asString()) - } - } - - } -} - diff --git a/core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt b/core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt deleted file mode 100644 index ee9d8c51..00000000 --- a/core/src/main/kotlin/Kotlin/KotlinAsJavaDocumentationBuilder.kt +++ /dev/null @@ -1,68 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import com.intellij.psi.JavaPsiFacade -import com.intellij.psi.PsiClass -import com.intellij.psi.PsiNamedElement -import org.jetbrains.dokka.Kotlin.DescriptorDocumentationParser -import org.jetbrains.kotlin.asJava.elements.KtLightElement -import org.jetbrains.kotlin.asJava.elements.KtLightMethod -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.lexer.KtTokens -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.psi.KtClass -import org.jetbrains.kotlin.psi.KtDeclaration -import org.jetbrains.kotlin.psi.KtParameter -import org.jetbrains.kotlin.psi.KtPropertyAccessor - -class KotlinAsJavaDocumentationBuilder - @Inject constructor(val kotlinAsJavaDocumentationParser: KotlinAsJavaDocumentationParser) : PackageDocumentationBuilder -{ - override fun buildPackageDocumentation(documentationBuilder: DocumentationBuilder, - packageName: FqName, - packageNode: DocumentationNode, - declarations: List<DeclarationDescriptor>, - allFqNames: Collection<FqName>) { - val project = documentationBuilder.resolutionFacade.project - val psiPackage = JavaPsiFacade.getInstance(project).findPackage(packageName.asString()) - if (psiPackage == null) { - documentationBuilder.logger.error("Cannot find Java package by qualified name: ${packageName.asString()}") - return - } - - val javaDocumentationBuilder = JavaPsiDocumentationBuilder(documentationBuilder.passConfiguration, - documentationBuilder.refGraph, - kotlinAsJavaDocumentationParser) - - psiPackage.classes.filter { it is KtLightElement<*, *> }.filter { it.isVisibleInDocumentation() }.forEach { - javaDocumentationBuilder.appendClasses(packageNode, arrayOf(it)) - } - } - - fun PsiClass.isVisibleInDocumentation(): Boolean { - val origin: KtDeclaration = (this as KtLightElement<*, *>).kotlinOrigin as? KtDeclaration ?: return true - - return !origin.hasModifier(KtTokens.INTERNAL_KEYWORD) && !origin.hasModifier(KtTokens.PRIVATE_KEYWORD) - } -} - -class KotlinAsJavaDocumentationParser - @Inject constructor(val resolutionFacade: DokkaResolutionFacade, - val descriptorDocumentationParser: DescriptorDocumentationParser) : JavaDocumentationParser -{ - override fun parseDocumentation(element: PsiNamedElement): JavadocParseResult { - val kotlinLightElement = element as? KtLightElement<*, *> ?: return JavadocParseResult.Empty - val origin = kotlinLightElement.kotlinOrigin as? KtDeclaration ?: return JavadocParseResult.Empty - if (origin is KtParameter) { - // LazyDeclarationResolver does not support setter parameters - val grandFather = origin.parent?.parent - if (grandFather is KtPropertyAccessor) { - return JavadocParseResult.Empty - } - } - val isDefaultNoArgConstructor = kotlinLightElement is KtLightMethod && origin is KtClass - val descriptor = resolutionFacade.resolveToDescriptor(origin) - val content = descriptorDocumentationParser.parseDocumentation(descriptor, origin is KtParameter, isDefaultNoArgConstructor) - return JavadocParseResult(content, null) - } -} diff --git a/core/src/main/kotlin/Kotlin/KotlinAsJavaElementSignatureProvider.kt b/core/src/main/kotlin/Kotlin/KotlinAsJavaElementSignatureProvider.kt deleted file mode 100644 index 20ea179e..00000000 --- a/core/src/main/kotlin/Kotlin/KotlinAsJavaElementSignatureProvider.kt +++ /dev/null @@ -1,25 +0,0 @@ -package org.jetbrains.dokka - -import com.intellij.psi.PsiElement -import org.jetbrains.kotlin.asJava.toLightElements -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.psi.KtElement - -class KotlinAsJavaElementSignatureProvider : ElementSignatureProvider { - - private fun PsiElement.javaLikePsi() = when { - this is KtElement -> toLightElements().firstOrNull() - else -> this - } - - override fun signature(forPsi: PsiElement): String { - return getSignature(forPsi.javaLikePsi()) ?: - "not implemented for $forPsi" - } - - override fun signature(forDesc: DeclarationDescriptor): String { - val sourcePsi = forDesc.sourcePsi() - return getSignature(sourcePsi?.javaLikePsi()) ?: - "not implemented for $forDesc with psi: $sourcePsi" - } -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Kotlin/KotlinElementSignatureProvider.kt b/core/src/main/kotlin/Kotlin/KotlinElementSignatureProvider.kt deleted file mode 100644 index c7187b23..00000000 --- a/core/src/main/kotlin/Kotlin/KotlinElementSignatureProvider.kt +++ /dev/null @@ -1,33 +0,0 @@ -package org.jetbrains.dokka - -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiMember -import com.intellij.psi.PsiPackage -import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade -import org.jetbrains.kotlin.asJava.elements.KtLightElement -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.resolve.BindingContext -import javax.inject.Inject - -class KotlinElementSignatureProvider @Inject constructor( - val resolutionFacade: DokkaResolutionFacade -) : ElementSignatureProvider { - override fun signature(forPsi: PsiElement): String { - return forPsi.extractDescriptor(resolutionFacade) - ?.let { signature(it) } - ?: run { "no desc for $forPsi in ${(forPsi as? PsiMember)?.containingClass}" } - } - - override fun signature(forDesc: DeclarationDescriptor): String = forDesc.signature() -} - - -fun PsiElement.extractDescriptor(resolutionFacade: DokkaResolutionFacade): DeclarationDescriptor? = - when (val forPsi = this) { - is KtLightClassForFacade -> resolutionFacade.moduleDescriptor.getPackage(forPsi.fqName) - is KtLightElement<*, *> -> (forPsi.kotlinOrigin!!).extractDescriptor(resolutionFacade) - is PsiPackage -> resolutionFacade.moduleDescriptor.getPackage(FqName(forPsi.qualifiedName)) - is PsiMember -> forPsi.getJavaOrKotlinMemberDescriptor(resolutionFacade) - else -> resolutionFacade.resolveSession.bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, forPsi] - } diff --git a/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt b/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt deleted file mode 100644 index 7310610f..00000000 --- a/core/src/main/kotlin/Kotlin/KotlinLanguageService.kt +++ /dev/null @@ -1,473 +0,0 @@ -package org.jetbrains.dokka - -import org.jetbrains.dokka.LanguageService.RenderMode - -/** - * Implements [LanguageService] and provides rendering of symbols in Kotlin language - */ -class KotlinLanguageService : CommonLanguageService() { - override fun showModifierInSummary(node: DocumentationNode): Boolean { - return node.name !in fullOnlyModifiers - } - - private val fullOnlyModifiers = - setOf("public", "protected", "private", "internal", "inline", "noinline", "crossinline", "reified") - - override fun render(node: DocumentationNode, renderMode: RenderMode): ContentNode { - return content { - when (node.kind) { - NodeKind.Package -> if (renderMode == RenderMode.FULL) renderPackage(node) - in NodeKind.classLike -> renderClass(node, renderMode) - - NodeKind.EnumItem, - NodeKind.ExternalClass -> if (renderMode == RenderMode.FULL) identifier(node.name) - - NodeKind.Parameter -> renderParameter(node, renderMode) - NodeKind.TypeParameter -> renderTypeParameter(node, renderMode) - NodeKind.Type, - NodeKind.UpperBound -> renderType(node, renderMode) - - NodeKind.Modifier -> renderModifier(this, node, renderMode) - NodeKind.Constructor, - NodeKind.Function, - NodeKind.CompanionObjectFunction -> renderFunction(node, renderMode) - NodeKind.Property, - NodeKind.CompanionObjectProperty -> renderProperty(node, renderMode) - else -> identifier(node.name) - } - } - } - - - override fun summarizeSignatures(nodes: List<DocumentationNode>): ContentNode? { - if (nodes.size < 2) return null - val receiverKind = nodes.getReceiverKind() ?: return null - val functionWithTypeParameter = nodes.firstOrNull { it.details(NodeKind.TypeParameter).any() } ?: return null - return content { - val typeParameter = functionWithTypeParameter.details(NodeKind.TypeParameter).first() - if (functionWithTypeParameter.kind == NodeKind.Function) { - renderFunction( - functionWithTypeParameter, - RenderMode.SUMMARY, - SummarizingMapper(receiverKind, typeParameter.name) - ) - } else { - renderProperty( - functionWithTypeParameter, - RenderMode.SUMMARY, - SummarizingMapper(receiverKind, typeParameter.name) - ) - } - } - } - - private fun List<DocumentationNode>.getReceiverKind(): ReceiverKind? { - val qNames = mapNotNull { it.getReceiverQName() } - if (qNames.size != size) - return null - - return ReceiverKind.values().firstOrNull { kind -> qNames.all { it in kind.classes } } - } - - private fun DocumentationNode.getReceiverQName(): String? { - if (kind != NodeKind.Function && kind != NodeKind.Property) return null - val receiver = details(NodeKind.Receiver).singleOrNull() ?: return null - return receiver.detail(NodeKind.Type).qualifiedNameFromType() - } - - companion object { - private val arrayClasses = setOf( - "kotlin.Array", - "kotlin.BooleanArray", - "kotlin.ByteArray", - "kotlin.CharArray", - "kotlin.ShortArray", - "kotlin.IntArray", - "kotlin.LongArray", - "kotlin.FloatArray", - "kotlin.DoubleArray" - ) - - private val arrayOrListClasses = setOf("kotlin.List") + arrayClasses - - private val iterableClasses = setOf( - "kotlin.Collection", - "kotlin.Sequence", - "kotlin.Iterable", - "kotlin.Map", - "kotlin.String", - "kotlin.CharSequence" - ) + arrayOrListClasses - } - - private enum class ReceiverKind(val receiverName: String, val classes: Collection<String>) { - ARRAY("any_array", arrayClasses), - ARRAY_OR_LIST("any_array_or_list", arrayOrListClasses), - ITERABLE("any_iterable", iterableClasses), - } - - interface SignatureMapper { - fun renderReceiver(receiver: DocumentationNode, to: ContentBlock) - } - - private class SummarizingMapper(val kind: ReceiverKind, val typeParameterName: String) : SignatureMapper { - override fun renderReceiver(receiver: DocumentationNode, to: ContentBlock) { - to.append(ContentIdentifier(kind.receiverName, IdentifierKind.SummarizedTypeName)) - to.text("<$typeParameterName>") - } - } - - private fun ContentBlock.renderFunctionalTypeParameterName(node: DocumentationNode, renderMode: RenderMode) { - node.references(RefKind.HiddenAnnotation).map { it.to } - .find { it.name == "ParameterName" }?.let { - val parameterNameValue = it.detail(NodeKind.Parameter).detail(NodeKind.Value) - identifier(parameterNameValue.name.removeSurrounding("\""), IdentifierKind.ParameterName) - symbol(":") - nbsp() - } - } - - private fun ContentBlock.renderFunctionalType(node: DocumentationNode, renderMode: RenderMode) { - var typeArguments = node.details(NodeKind.Type) - - if (node.name.startsWith("Suspend")) { - keyword("suspend ") - } - - // lambda - val isExtension = node.annotations.any { it.name == "ExtensionFunctionType" } - if (isExtension) { - renderType(typeArguments.first(), renderMode) - symbol(".") - typeArguments = typeArguments.drop(1) - } - symbol("(") - renderList(typeArguments.take(typeArguments.size - 1), noWrap = true) { - renderFunctionalTypeParameterName(it, renderMode) - renderType(it, renderMode) - } - symbol(")") - nbsp() - symbol("->") - nbsp() - renderType(typeArguments.last(), renderMode) - - } - - private fun DocumentationNode.isFunctionalType(): Boolean { - val typeArguments = details(NodeKind.Type) - val functionalTypeName = "Function${typeArguments.count() - 1}" - val suspendFunctionalTypeName = "Suspend$functionalTypeName" - return name == functionalTypeName || name == suspendFunctionalTypeName - } - - private fun ContentBlock.renderType(node: DocumentationNode, renderMode: RenderMode) { - if (node.name == "dynamic") { - keyword("dynamic") - return - } - - val nullabilityModifier = node.detailOrNull(NodeKind.NullabilityModifier) - - if (node.isFunctionalType()) { - if (nullabilityModifier != null) { - symbol("(") - renderFunctionalType(node, renderMode) - symbol(")") - symbol(nullabilityModifier.name) - } else { - renderFunctionalType(node, renderMode) - } - return - } - if (renderMode == RenderMode.FULL) { - renderAnnotationsForNode(node) - } - renderModifiersForNode(node, renderMode, true) - renderLinked(this, node) { - identifier(it.typeDeclarationClass?.classNodeNameWithOuterClass() ?: it.name, IdentifierKind.TypeName) - } - val typeArguments = node.details(NodeKind.Type) - if (typeArguments.isNotEmpty()) { - symbol("<") - renderList(typeArguments, noWrap = true) { - renderType(it, renderMode) - } - symbol(">") - } - - nullabilityModifier ?.apply { - symbol(nullabilityModifier.name) - } - } - - override fun renderModifier( - block: ContentBlock, - node: DocumentationNode, - renderMode: RenderMode, - nowrap: Boolean - ) { - when (node.name) { - "final", "public", "var", "expect", "actual", "external" -> { - } - else -> { - if (showModifierInSummary(node) || renderMode == RenderMode.FULL) { - super.renderModifier(block, node, renderMode, nowrap) - } - } - } - } - - private fun ContentBlock.renderTypeParameter(node: DocumentationNode, renderMode: RenderMode) { - renderModifiersForNode(node, renderMode, true) - - identifier(node.name) - - val constraints = node.details(NodeKind.UpperBound) - if (constraints.size == 1) { - nbsp() - symbol(":") - nbsp() - renderList(constraints, noWrap = true) { - renderType(it, renderMode) - } - } - } - - private fun ContentBlock.renderParameter(node: DocumentationNode, renderMode: RenderMode) { - if (renderMode == RenderMode.FULL) { - renderAnnotationsForNode(node) - } - renderModifiersForNode(node, renderMode) - identifier(node.name, IdentifierKind.ParameterName, node.detailOrNull(NodeKind.Signature)?.name) - symbol(":") - nbsp() - val parameterType = node.detail(NodeKind.Type) - renderType(parameterType, renderMode) - val valueNode = node.details(NodeKind.Value).firstOrNull() - if (valueNode != null) { - nbsp() - symbol("=") - nbsp() - text(valueNode.name) - } - } - - private fun ContentBlock.renderTypeParametersForNode(node: DocumentationNode, renderMode: RenderMode) { - val typeParameters = node.details(NodeKind.TypeParameter) - if (typeParameters.any()) { - symbol("<") - renderList(typeParameters) { - renderTypeParameter(it, renderMode) - } - symbol(">") - } - } - - private fun ContentBlock.renderExtraTypeParameterConstraints(node: DocumentationNode, renderMode: RenderMode) { - val parametersWithMultipleConstraints = - node.details(NodeKind.TypeParameter).filter { it.details(NodeKind.UpperBound).size > 1 } - val parametersWithConstraints = parametersWithMultipleConstraints - .flatMap { parameter -> - parameter.details(NodeKind.UpperBound).map { constraint -> parameter to constraint } - } - if (parametersWithMultipleConstraints.isNotEmpty()) { - keyword(" where ") - renderList(parametersWithConstraints) { - identifier(it.first.name) - nbsp() - symbol(":") - nbsp() - renderType(it.second, renderMode) - } - } - } - - private fun ContentBlock.renderSupertypesForNode(node: DocumentationNode, renderMode: RenderMode) { - val supertypes = node.details(NodeKind.Supertype).filterNot { it.qualifiedNameFromType() in ignoredSupertypes } - if (supertypes.any()) { - nbsp() - symbol(":") - nbsp() - renderList(supertypes) { - indentedSoftLineBreak() - renderType(it, renderMode) - } - } - } - - private fun ContentBlock.renderAnnotationsForNode(node: DocumentationNode) { - node.annotations.forEach { - renderAnnotation(it) - } - } - - private fun ContentBlock.renderAnnotation(node: DocumentationNode) { - identifier("@" + node.name, IdentifierKind.AnnotationName) - val parameters = node.details(NodeKind.Parameter) - if (!parameters.isEmpty()) { - symbol("(") - renderList(parameters) { - text(it.detail(NodeKind.Value).name) - } - symbol(")") - } - text(" ") - } - - private fun ContentBlock.renderClass(node: DocumentationNode, renderMode: RenderMode) { - if (renderMode == RenderMode.FULL) { - renderAnnotationsForNode(node) - } - renderModifiersForNode(node, renderMode) - when (node.kind) { - NodeKind.Class, - NodeKind.AnnotationClass, - NodeKind.Exception, - NodeKind.Enum -> keyword("class ") - NodeKind.Interface -> keyword("interface ") - NodeKind.EnumItem -> keyword("enum val ") - NodeKind.Object -> keyword("object ") - NodeKind.TypeAlias -> keyword("typealias ") - else -> throw IllegalArgumentException("Node $node is not a class-like object") - } - - identifierOrDeprecated(node) - renderTypeParametersForNode(node, renderMode) - renderSupertypesForNode(node, renderMode) - renderExtraTypeParameterConstraints(node, renderMode) - - if (node.kind == NodeKind.TypeAlias) { - nbsp() - symbol("=") - nbsp() - renderType(node.detail(NodeKind.TypeAliasUnderlyingType), renderMode) - } - } - - private fun ContentBlock.renderFunction( - node: DocumentationNode, - renderMode: RenderMode, - signatureMapper: SignatureMapper? = null - ) { - if (renderMode == RenderMode.FULL) { - renderAnnotationsForNode(node) - } - renderModifiersForNode(node, renderMode) - when (node.kind) { - NodeKind.Constructor -> identifier(node.owner!!.name) - NodeKind.Function, - NodeKind.CompanionObjectFunction -> keyword("fun ") - else -> throw IllegalArgumentException("Node $node is not a function-like object") - } - renderTypeParametersForNode(node, renderMode) - if (node.details(NodeKind.TypeParameter).any()) { - text(" ") - } - - renderReceiver(node, renderMode, signatureMapper) - - if (node.kind != NodeKind.Constructor) - identifierOrDeprecated(node) - - symbol("(") - val parameters = node.details(NodeKind.Parameter) - renderList(parameters) { - indentedSoftLineBreak() - renderParameter(it, renderMode) - } - if (needReturnType(node)) { - if (parameters.isNotEmpty()) { - softLineBreak() - } - symbol(")") - symbol(": ") - renderType(node.detail(NodeKind.Type), renderMode) - } else { - symbol(")") - } - renderExtraTypeParameterConstraints(node, renderMode) - } - - private fun ContentBlock.renderReceiver( - node: DocumentationNode, - renderMode: RenderMode, - signatureMapper: SignatureMapper? - ) { - val receiver = node.details(NodeKind.Receiver).singleOrNull() - if (receiver != null) { - if (signatureMapper != null) { - signatureMapper.renderReceiver(receiver, this) - } else { - val type = receiver.detail(NodeKind.Type) - - if (type.isFunctionalType()) { - symbol("(") - renderFunctionalType(type, renderMode) - symbol(")") - } else { - renderType(type, renderMode) - } - } - symbol(".") - } - } - - private fun needReturnType(node: DocumentationNode) = when (node.kind) { - NodeKind.Constructor -> false - else -> !node.isUnitReturnType() - } - - fun DocumentationNode.isUnitReturnType(): Boolean = - detail(NodeKind.Type).hiddenLinks.firstOrNull()?.qualifiedName() == "kotlin.Unit" - - private fun ContentBlock.renderProperty( - node: DocumentationNode, - renderMode: RenderMode, - signatureMapper: SignatureMapper? = null - ) { - if (renderMode == RenderMode.FULL) { - renderAnnotationsForNode(node) - } - renderModifiersForNode(node, renderMode) - when (node.kind) { - NodeKind.Property, - NodeKind.CompanionObjectProperty -> keyword("${node.getPropertyKeyword()} ") - else -> throw IllegalArgumentException("Node $node is not a property") - } - renderTypeParametersForNode(node, renderMode) - if (node.details(NodeKind.TypeParameter).any()) { - text(" ") - } - - renderReceiver(node, renderMode, signatureMapper) - - identifierOrDeprecated(node) - symbol(": ") - renderType(node.detail(NodeKind.Type), renderMode) - renderExtraTypeParameterConstraints(node, renderMode) - } - - fun DocumentationNode.getPropertyKeyword() = - if (details(NodeKind.Modifier).any { it.name == "var" }) "var" else "val" - - fun ContentBlock.identifierOrDeprecated(node: DocumentationNode) { - if (node.deprecation != null) { - val strike = ContentStrikethrough() - strike.identifier(node.name) - append(strike) - } else { - identifier(node.name) - } - } -} - -fun DocumentationNode.qualifiedNameFromType(): String { - return details.firstOrNull { it.kind == NodeKind.QualifiedName }?.name - ?: (links.firstOrNull() ?: hiddenLinks.firstOrNull())?.qualifiedName() - ?: name -} - - -val DocumentationNode.typeDeclarationClass - get() = (links.firstOrNull { it.kind in NodeKind.classLike } ?: externalType) diff --git a/core/src/main/kotlin/Languages/CommonLanguageService.kt b/core/src/main/kotlin/Languages/CommonLanguageService.kt deleted file mode 100644 index ddc95d32..00000000 --- a/core/src/main/kotlin/Languages/CommonLanguageService.kt +++ /dev/null @@ -1,84 +0,0 @@ -package org.jetbrains.dokka - - -abstract class CommonLanguageService : LanguageService { - - protected fun ContentBlock.renderPackage(node: DocumentationNode) { - keyword("package") - nbsp() - identifier(node.name) - } - - override fun renderName(node: DocumentationNode): String { - return when (node.kind) { - NodeKind.Constructor -> node.owner!!.name - else -> node.name - } - } - - open fun renderModifier( - block: ContentBlock, - node: DocumentationNode, - renderMode: LanguageService.RenderMode, - nowrap: Boolean = false - ) = with(block) { - keyword(node.name) - if (nowrap) { - nbsp() - } else { - text(" ") - } - } - - protected fun renderLinked( - block: ContentBlock, - node: DocumentationNode, - body: ContentBlock.(DocumentationNode) -> Unit - ) = with(block) { - val to = node.links.firstOrNull() - if (to == null) - body(node) - else - link(to) { - this.body(node) - } - } - - protected fun <T> ContentBlock.renderList( - nodes: List<T>, separator: String = ", ", - noWrap: Boolean = false, renderItem: (T) -> Unit - ) { - if (nodes.none()) - return - renderItem(nodes.first()) - nodes.drop(1).forEach { - if (noWrap) { - symbol(separator.removeSuffix(" ")) - nbsp() - } else { - symbol(separator) - } - renderItem(it) - } - } - - abstract fun showModifierInSummary(node: DocumentationNode): Boolean - - protected fun ContentBlock.renderModifiersForNode( - node: DocumentationNode, - renderMode: LanguageService.RenderMode, - nowrap: Boolean = false - ) { - val modifiers = node.details(NodeKind.Modifier) - for (it in modifiers) { - if (node.kind == NodeKind.Interface && it.name == "abstract") - continue - if (renderMode == LanguageService.RenderMode.SUMMARY && !showModifierInSummary(it)) { - continue - } - renderModifier(this, it, renderMode, nowrap) - } - } - - -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Languages/JavaLanguageService.kt b/core/src/main/kotlin/Languages/JavaLanguageService.kt deleted file mode 100644 index ad66123b..00000000 --- a/core/src/main/kotlin/Languages/JavaLanguageService.kt +++ /dev/null @@ -1,171 +0,0 @@ -package org.jetbrains.dokka - -import org.jetbrains.dokka.LanguageService.RenderMode - -/** - * Implements [LanguageService] and provides rendering of symbols in Java language - */ -class JavaLanguageService : LanguageService { - override fun render(node: DocumentationNode, renderMode: RenderMode): ContentNode { - return ContentText(when (node.kind) { - NodeKind.Package -> renderPackage(node) - in NodeKind.classLike -> renderClass(node) - - NodeKind.TypeParameter -> renderTypeParameter(node) - NodeKind.Type, - NodeKind.UpperBound -> renderType(node) - - NodeKind.Constructor, - NodeKind.Function -> renderFunction(node) - NodeKind.Property -> renderProperty(node) - else -> "${node.kind}: ${node.name}" - }) - } - - override fun renderName(node: DocumentationNode): String { - return when (node.kind) { - NodeKind.Constructor -> node.owner!!.name - else -> node.name - } - } - - override fun summarizeSignatures(nodes: List<DocumentationNode>): ContentNode? = null - - private fun renderPackage(node: DocumentationNode): String { - return "package ${node.name}" - } - - private fun renderModifier(node: DocumentationNode): String { - return when (node.name) { - "open" -> "" - "internal" -> "" - else -> node.name - } - } - - fun getArrayElementType(node: DocumentationNode): DocumentationNode? = when (node.qualifiedName()) { - "kotlin.Array" -> - node.details(NodeKind.Type).singleOrNull()?.let { et -> getArrayElementType(et) ?: et } ?: - DocumentationNode("Object", node.content, NodeKind.ExternalClass) - - "kotlin.IntArray", "kotlin.LongArray", "kotlin.ShortArray", "kotlin.ByteArray", - "kotlin.CharArray", "kotlin.DoubleArray", "kotlin.FloatArray", "kotlin.BooleanArray" -> - DocumentationNode(node.name.removeSuffix("Array").toLowerCase(), node.content, NodeKind.Type) - - else -> null - } - - fun getArrayDimension(node: DocumentationNode): Int = when (node.qualifiedName()) { - "kotlin.Array" -> - 1 + (node.details(NodeKind.Type).singleOrNull()?.let { getArrayDimension(it) } ?: 0) - - "kotlin.IntArray", "kotlin.LongArray", "kotlin.ShortArray", "kotlin.ByteArray", - "kotlin.CharArray", "kotlin.DoubleArray", "kotlin.FloatArray", "kotlin.BooleanArray" -> - 1 - else -> 0 - } - - fun renderType(node: DocumentationNode): String { - return when (node.name) { - "Unit" -> "void" - "Int" -> "int" - "Long" -> "long" - "Double" -> "double" - "Float" -> "float" - "Char" -> "char" - "Boolean" -> "bool" - // TODO: render arrays - else -> node.name - } - } - - private fun renderTypeParameter(node: DocumentationNode): String { - val constraints = node.details(NodeKind.UpperBound) - return if (constraints.none()) - node.name - else { - node.name + " extends " + constraints.joinToString { renderType(node) } - } - } - - private fun renderParameter(node: DocumentationNode): String { - return "${renderType(node.detail(NodeKind.Type))} ${node.name}" - } - - private fun renderTypeParametersForNode(node: DocumentationNode): String { - return StringBuilder().apply { - val typeParameters = node.details(NodeKind.TypeParameter) - if (typeParameters.any()) { - append("<") - append(typeParameters.joinToString { renderTypeParameter(it) }) - append("> ") - } - }.toString() - } - - private fun renderModifiersForNode(node: DocumentationNode): String { - val modifiers = node.details(NodeKind.Modifier).map { renderModifier(it) }.filter { it != "" } - if (modifiers.none()) - return "" - return modifiers.joinToString(" ", postfix = " ") - } - - private fun renderClass(node: DocumentationNode): String { - return StringBuilder().apply { - when (node.kind) { - NodeKind.Class -> append("class ") - NodeKind.Interface -> append("interface ") - NodeKind.Enum -> append("enum ") - NodeKind.EnumItem -> append("enum value ") - NodeKind.Object -> append("class ") - else -> throw IllegalArgumentException("Node $node is not a class-like object") - } - - append(node.name) - append(renderTypeParametersForNode(node)) - }.toString() - } - - private fun renderFunction(node: DocumentationNode): String { - return StringBuilder().apply { - when (node.kind) { - NodeKind.Constructor -> append(node.owner?.name) - NodeKind.Function -> { - append(renderTypeParametersForNode(node)) - append(renderType(node.detail(NodeKind.Type))) - append(" ") - append(node.name) - } - else -> throw IllegalArgumentException("Node $node is not a function-like object") - } - - val receiver = node.details(NodeKind.Receiver).singleOrNull() - append("(") - if (receiver != null) - (listOf(receiver) + node.details(NodeKind.Parameter)).joinTo(this) { renderParameter(it) } - else - node.details(NodeKind.Parameter).joinTo(this) { renderParameter(it) } - - append(")") - }.toString() - } - - private fun renderProperty(node: DocumentationNode): String { - return StringBuilder().apply { - when (node.kind) { - NodeKind.Property -> append("val ") - else -> throw IllegalArgumentException("Node $node is not a property") - } - append(renderTypeParametersForNode(node)) - val receiver = node.details(NodeKind.Receiver).singleOrNull() - if (receiver != null) { - append(renderType(receiver.detail(NodeKind.Type))) - append(".") - } - - append(node.name) - append(": ") - append(renderType(node.detail(NodeKind.Type))) - }.toString() - } -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Languages/LanguageService.kt b/core/src/main/kotlin/Languages/LanguageService.kt deleted file mode 100644 index b0f4bbc9..00000000 --- a/core/src/main/kotlin/Languages/LanguageService.kt +++ /dev/null @@ -1,41 +0,0 @@ -package org.jetbrains.dokka - -/** - * Provides facility for rendering [DocumentationNode] as a language-dependent declaration - */ -interface LanguageService { - enum class RenderMode { - /** Brief signature (used in a list of all members of the class). */ - SUMMARY, - /** Full signature (used in the page describing the member itself */ - FULL - } - - /** - * Renders a [node] as a class, function, property or other signature in a target language. - * @param node A [DocumentationNode] to render - * @return [ContentNode] which is a root for a rich content tree suitable for formatting with [FormatService] - */ - fun render(node: DocumentationNode, renderMode: RenderMode = RenderMode.FULL): ContentNode - - /** - * Tries to summarize the signatures of the specified documentation nodes in a compact representation. - * Returns the representation if successful, or null if the signatures could not be summarized. - */ - fun summarizeSignatures(nodes: List<DocumentationNode>): ContentNode? - - /** - * Renders [node] as a named representation in the target language - * - * For example: - * ${code org.jetbrains.dokka.example} - * - * $node: A [DocumentationNode] to render - * $returns: [String] which is a string representation of the node's name - */ - fun renderName(node: DocumentationNode): String -} - -fun example(service: LanguageService, node: DocumentationNode) { - println("Node name: ${service.renderName(node)}") -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Languages/NewJavaLanguageService.kt b/core/src/main/kotlin/Languages/NewJavaLanguageService.kt deleted file mode 100644 index 992cd090..00000000 --- a/core/src/main/kotlin/Languages/NewJavaLanguageService.kt +++ /dev/null @@ -1,197 +0,0 @@ -package org.jetbrains.dokka - -import org.jetbrains.dokka.LanguageService.RenderMode - -/** - * Implements [LanguageService] and provides rendering of symbols in Java language - */ -class NewJavaLanguageService : CommonLanguageService() { - override fun showModifierInSummary(node: DocumentationNode): Boolean { - return true - } - - override fun render(node: DocumentationNode, renderMode: RenderMode): ContentNode { - return content { - (when (node.kind) { - NodeKind.Package -> renderPackage(node) - in NodeKind.classLike -> renderClass(node, renderMode) - - NodeKind.Modifier -> renderModifier(this, node, renderMode) - NodeKind.TypeParameter -> renderTypeParameter(node) - NodeKind.Type, - NodeKind.UpperBound -> renderType(node) - NodeKind.Parameter -> renderParameter(node) - NodeKind.Constructor, - NodeKind.Function -> renderFunction(node) - NodeKind.Property -> renderProperty(node) - else -> "${node.kind}: ${node.name}" - }) - } - } - - override fun summarizeSignatures(nodes: List<DocumentationNode>): ContentNode? = null - - - override fun renderModifier(block: ContentBlock, node: DocumentationNode, renderMode: RenderMode, nowrap: Boolean) { - when (node.name) { - "open", "internal" -> { - } - else -> super.renderModifier(block, node, renderMode, nowrap) - } - } - - fun getArrayElementType(node: DocumentationNode): DocumentationNode? = when (node.qualifiedName()) { - "kotlin.Array" -> - node.details(NodeKind.Type).singleOrNull()?.let { et -> getArrayElementType(et) ?: et } - ?: DocumentationNode("Object", node.content, NodeKind.ExternalClass) - - "kotlin.IntArray", "kotlin.LongArray", "kotlin.ShortArray", "kotlin.ByteArray", - "kotlin.CharArray", "kotlin.DoubleArray", "kotlin.FloatArray", "kotlin.BooleanArray" -> - DocumentationNode(node.name.removeSuffix("Array").toLowerCase(), node.content, NodeKind.Type) - - else -> null - } - - fun getArrayDimension(node: DocumentationNode): Int = when (node.qualifiedName()) { - "kotlin.Array" -> - 1 + (node.details(NodeKind.Type).singleOrNull()?.let { getArrayDimension(it) } ?: 0) - - "kotlin.IntArray", "kotlin.LongArray", "kotlin.ShortArray", "kotlin.ByteArray", - "kotlin.CharArray", "kotlin.DoubleArray", "kotlin.FloatArray", "kotlin.BooleanArray" -> - 1 - else -> 0 - } - - fun ContentBlock.renderType(node: DocumentationNode) { - when (node.name) { - "Unit" -> identifier("void") - "Int" -> identifier("int") - "Long" -> identifier("long") - "Double" -> identifier("double") - "Float" -> identifier("float") - "Char" -> identifier("char") - "Boolean" -> identifier("bool") - // TODO: render arrays - else -> renderLinked(this, node) { - identifier(node.name) - } - } - } - - private fun ContentBlock.renderTypeParameter(node: DocumentationNode) { - val constraints = node.details(NodeKind.UpperBound) - if (constraints.none()) - identifier(node.name) - else { - identifier(node.name) - text(" ") - keyword("extends") - text(" ") - constraints.forEach { renderType(node) } - } - } - - private fun ContentBlock.renderParameter(node: DocumentationNode) { - renderType(node.detail(NodeKind.Type)) - text(" ") - identifier(node.name) - } - - private fun ContentBlock.renderTypeParametersForNode(node: DocumentationNode) { - val typeParameters = node.details(NodeKind.TypeParameter) - if (typeParameters.any()) { - symbol("<") - renderList(typeParameters, noWrap = true) { - renderTypeParameter(it) - } - symbol(">") - text(" ") - } - } - -// private fun renderModifiersForNode(node: DocumentationNode): String { -// val modifiers = node.details(NodeKind.Modifier).map { renderModifier(it) }.filter { it != "" } -// if (modifiers.none()) -// return "" -// return modifiers.joinToString(" ", postfix = " ") -// } - - private fun ContentBlock.renderClassKind(node: DocumentationNode) { - when (node.kind) { - NodeKind.Interface -> { - keyword("interface") - } - NodeKind.EnumItem -> { - keyword("enum value") - } - NodeKind.Enum -> { - keyword("enum") - } - NodeKind.Class, NodeKind.Exception, NodeKind.Object -> { - keyword("class") - } - else -> throw IllegalArgumentException("Node $node is not a class-like object") - } - text(" ") - } - - private fun ContentBlock.renderClass(node: DocumentationNode, renderMode: RenderMode) { - renderModifiersForNode(node, renderMode) - renderClassKind(node) - - identifier(node.name) - renderTypeParametersForNode(node) - } - - private fun ContentBlock.renderParameters(nodes: List<DocumentationNode>) { - renderList(nodes) { - renderParameter(it) - } - } - - private fun ContentBlock.renderFunction(node: DocumentationNode) { - when (node.kind) { - NodeKind.Constructor -> identifier(node.owner?.name ?: "") - NodeKind.Function -> { - renderTypeParametersForNode(node) - renderType(node.detail(NodeKind.Type)) - text(" ") - identifier(node.name) - - } - else -> throw IllegalArgumentException("Node $node is not a function-like object") - } - - val receiver = node.details(NodeKind.Receiver).singleOrNull() - symbol("(") - if (receiver != null) - renderParameters(listOf(receiver) + node.details(NodeKind.Parameter)) - else - renderParameters(node.details(NodeKind.Parameter)) - - symbol(")") - } - - private fun ContentBlock.renderProperty(node: DocumentationNode) { - - when (node.kind) { - NodeKind.Property -> { - keyword("val") - text(" ") - } - else -> throw IllegalArgumentException("Node $node is not a property") - } - renderTypeParametersForNode(node) - val receiver = node.details(NodeKind.Receiver).singleOrNull() - if (receiver != null) { - renderType(receiver.detail(NodeKind.Type)) - symbol(".") - } - - identifier(node.name) - symbol(":") - text(" ") - renderType(node.detail(NodeKind.Type)) - - } -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Locations/Location.kt b/core/src/main/kotlin/Locations/Location.kt deleted file mode 100644 index 63c9b913..00000000 --- a/core/src/main/kotlin/Locations/Location.kt +++ /dev/null @@ -1,76 +0,0 @@ -package org.jetbrains.dokka - -import java.io.File - -interface Location { - val path: String get - fun relativePathTo(other: Location, anchor: String? = null): String -} - -/** - * Represents locations in the documentation in the form of [path](File). - * - * $file: [File] for this location - * $path: [String] representing path of this location - */ -data class FileLocation(val file: File) : Location { - override val path: String - get() = file.path - - override fun relativePathTo(other: Location, anchor: String?): String { - if (other !is FileLocation) { - throw IllegalArgumentException("$other is not a FileLocation") - } - if (file.path.substringBeforeLast(".") == other.file.path.substringBeforeLast(".") && anchor == null) { - return "./${file.name}" - } - val ownerFolder = file.parentFile!! - val relativePath = ownerFolder.toPath().relativize(other.file.toPath()).toString().replace(File.separatorChar, '/') - return if (anchor == null) relativePath else "$relativePath#$anchor" - } -} - -fun relativePathToNode(qualifiedName: List<String>, hasMembers: Boolean): String { - val parts = qualifiedName.map { identifierToFilename(it) }.filterNot { it.isEmpty() } - return if (!hasMembers) { - // leaf node, use file in owner's folder - parts.joinToString("/") - } else { - parts.joinToString("/") + (if (parts.none()) "" else "/") + "index" - } -} - -fun nodeActualQualifier(node: DocumentationNode): List<DocumentationNode> { - val topLevelPage = node.references(RefKind.TopLevelPage).singleOrNull()?.to - if (topLevelPage != null) { - return nodeActualQualifier(topLevelPage) - } - return node.owner?.let { nodeActualQualifier(it) }.orEmpty() + node -} - -fun relativePathToNode(node: DocumentationNode): String { - val qualifier = nodeActualQualifier(node) - return relativePathToNode( - qualifier.map { it.name }, - qualifier.last().members.any() - ) -} - - -fun identifierToFilename(path: String): String { - if (path.isEmpty()) return "--root--" - val escaped = path.replace('<', '-').replace('>', '-') - val lowercase = escaped.replace("[A-Z]".toRegex()) { matchResult -> "-" + matchResult.value.toLowerCase() } - return if (lowercase == "index") "--index--" else lowercase -} - -fun NodeLocationAwareGenerator.relativePathToLocation(owner: DocumentationNode, node: DocumentationNode): String { - return location(owner).relativePathTo(location(node), null) -} - -fun NodeLocationAwareGenerator.relativePathToRoot(from: Location): File { - val file = File(from.path).parentFile - return root.relativeTo(file) -} - -fun File.toUnixString() = toString().replace(File.separatorChar, '/') diff --git a/core/src/main/kotlin/Markdown/MarkdownProcessor.kt b/core/src/main/kotlin/Markdown/MarkdownProcessor.kt deleted file mode 100644 index 2c8f7a73..00000000 --- a/core/src/main/kotlin/Markdown/MarkdownProcessor.kt +++ /dev/null @@ -1,53 +0,0 @@ -package org.jetbrains.dokka - -import org.intellij.markdown.IElementType -import org.intellij.markdown.MarkdownElementTypes -import org.intellij.markdown.ast.ASTNode -import org.intellij.markdown.ast.LeafASTNode -import org.intellij.markdown.ast.getTextInNode -import org.intellij.markdown.flavours.commonmark.CommonMarkFlavourDescriptor -import org.intellij.markdown.parser.MarkdownParser - -class MarkdownNode(val node: ASTNode, val parent: MarkdownNode?, val markdown: String) { - val children: List<MarkdownNode> = node.children.map { MarkdownNode(it, this, markdown) } - val type: IElementType get() = node.type - val text: String get() = node.getTextInNode(markdown).toString() - fun child(type: IElementType): MarkdownNode? = children.firstOrNull { it.type == type } - - val previous get() = parent?.children?.getOrNull(parent.children.indexOf(this) - 1) - - override fun toString(): String = StringBuilder().apply { presentTo(this) }.toString() -} - -fun MarkdownNode.visit(action: (MarkdownNode, () -> Unit) -> Unit) { - action(this) { - for (child in children) { - child.visit(action) - } - } -} - -fun MarkdownNode.toTestString(): String { - val sb = StringBuilder() - var level = 0 - visit { node, visitChildren -> - sb.append(" ".repeat(level * 2)) - node.presentTo(sb) - sb.appendln() - level++ - visitChildren() - level-- - } - return sb.toString() -} - -private fun MarkdownNode.presentTo(sb: StringBuilder) { - sb.append(type.toString()) - sb.append(":" + text.replace("\n", "\u23CE")) -} - -fun parseMarkdown(markdown: String): MarkdownNode { - if (markdown.isEmpty()) - return MarkdownNode(LeafASTNode(MarkdownElementTypes.MARKDOWN_FILE, 0, 0), null, markdown) - return MarkdownNode(MarkdownParser(CommonMarkFlavourDescriptor()).buildMarkdownTreeFromString(markdown), null, markdown) -} diff --git a/core/src/main/kotlin/Model/Content.kt b/core/src/main/kotlin/Model/Content.kt deleted file mode 100644 index 5530e1b4..00000000 --- a/core/src/main/kotlin/Model/Content.kt +++ /dev/null @@ -1,290 +0,0 @@ -package org.jetbrains.dokka - -interface ContentNode { - val textLength: Int -} - -object ContentEmpty : ContentNode { - override val textLength: Int get() = 0 -} - -open class ContentBlock() : ContentNode { - open val children = arrayListOf<ContentNode>() - - fun append(node: ContentNode) { - children.add(node) - } - - fun isEmpty() = children.isEmpty() - - override fun equals(other: Any?): Boolean = - other is ContentBlock && javaClass == other.javaClass && children == other.children - - override fun hashCode(): Int = - children.hashCode() - - override val textLength: Int - get() = children.sumBy { it.textLength } -} - -class NodeRenderContent( - val node: DocumentationNode, - val mode: LanguageService.RenderMode -): ContentNode { - override val textLength: Int - get() = 0 //TODO: Clarify? -} - -class LazyContentBlock(private val fillChildren: () -> List<ContentNode>) : ContentBlock() { - private var computed = false - override val children: ArrayList<ContentNode> - get() { - if (!computed) { - computed = true - children.addAll(fillChildren()) - } - return super.children - } - - override fun equals(other: Any?): Boolean { - return other is LazyContentBlock && other.fillChildren == fillChildren && super.equals(other) - } - - override fun hashCode(): Int { - return super.hashCode() + 31 * fillChildren.hashCode() - } -} - -enum class IdentifierKind { - TypeName, - ParameterName, - AnnotationName, - SummarizedTypeName, - Other -} - -data class ContentText(val text: String) : ContentNode { - override val textLength: Int - get() = text.length -} - -data class ContentKeyword(val text: String) : ContentNode { - override val textLength: Int - get() = text.length -} - -data class ContentIdentifier(val text: String, - val kind: IdentifierKind = IdentifierKind.Other, - val signature: String? = null) : ContentNode { - override val textLength: Int - get() = text.length -} - -data class ContentSymbol(val text: String) : ContentNode { - override val textLength: Int - get() = text.length -} - -data class ContentEntity(val text: String) : ContentNode { - override val textLength: Int - get() = text.length -} - -object ContentNonBreakingSpace: ContentNode { - override val textLength: Int - get() = 1 -} - -object ContentSoftLineBreak: ContentNode { - override val textLength: Int - get() = 0 -} - -object ContentIndentedSoftLineBreak: ContentNode { - override val textLength: Int - get() = 0 -} - -class ContentParagraph() : ContentBlock() -class ContentEmphasis() : ContentBlock() -class ContentStrong() : ContentBlock() -class ContentStrikethrough() : ContentBlock() -class ContentCode() : ContentBlock() -open class ContentBlockCode(val language: String = "") : ContentBlock() -class ContentBlockSampleCode(language: String = "kotlin", val importsBlock: ContentBlockCode = ContentBlockCode(language)) : ContentBlockCode(language) - -abstract class ContentNodeLink() : ContentBlock() { - abstract val node: DocumentationNode? - abstract val text: String? -} - -object ContentHardLineBreak : ContentNode { - override val textLength: Int - get() = 0 -} - -class ContentNodeDirectLink(override val node: DocumentationNode): ContentNodeLink() { - override fun equals(other: Any?): Boolean = - super.equals(other) && other is ContentNodeDirectLink && node.name == other.node.name - - override fun hashCode(): Int = - children.hashCode() * 31 + node.name.hashCode() - - override val text: String? = null -} - -class ContentNodeLazyLink(val linkText: String, val lazyNode: () -> DocumentationNode?): ContentNodeLink() { - override val node: DocumentationNode? get() = lazyNode() - - override fun equals(other: Any?): Boolean = - super.equals(other) && other is ContentNodeLazyLink && linkText == other.linkText - - override fun hashCode(): Int = - children.hashCode() * 31 + linkText.hashCode() - - override val text: String? = linkText -} - -class ContentExternalLink(val href : String) : ContentBlock() { - override fun equals(other: Any?): Boolean = - super.equals(other) && other is ContentExternalLink && href == other.href - - override fun hashCode(): Int = - children.hashCode() * 31 + href.hashCode() -} - -data class ContentBookmark(val name: String): ContentBlock() -data class ContentLocalLink(val href: String) : ContentBlock() - -class ContentUnorderedList() : ContentBlock() -class ContentOrderedList() : ContentBlock() -class ContentListItem() : ContentBlock() - -class ContentHeading(val level: Int) : ContentBlock() - -class ContentSection(val tag: String, val subjectName: String?) : ContentBlock() { - override fun equals(other: Any?): Boolean = - super.equals(other) && other is ContentSection && tag == other.tag && subjectName == other.subjectName - - override fun hashCode(): Int = - children.hashCode() * 31 * 31 + tag.hashCode() * 31 + (subjectName?.hashCode() ?: 0) -} - -object ContentTags { - const val Description = "Description" - const val SeeAlso = "See Also" - const val Return = "Return" - const val Exceptions = "Exceptions" -} - -fun content(body: ContentBlock.() -> Unit): ContentBlock { - val block = ContentBlock() - block.body() - return block -} - -fun ContentBlock.text(value: String) = append(ContentText(value)) -fun ContentBlock.keyword(value: String) = append(ContentKeyword(value)) -fun ContentBlock.symbol(value: String) = append(ContentSymbol(value)) - -fun ContentBlock.identifier(value: String, kind: IdentifierKind = IdentifierKind.Other, signature: String? = null) { - append(ContentIdentifier(value, kind, signature)) -} - -fun ContentBlock.nbsp() = append(ContentNonBreakingSpace) -fun ContentBlock.softLineBreak() = append(ContentSoftLineBreak) -fun ContentBlock.indentedSoftLineBreak() = append(ContentIndentedSoftLineBreak) -fun ContentBlock.hardLineBreak() = append(ContentHardLineBreak) - -fun ContentBlock.strong(body: ContentBlock.() -> Unit) { - val strong = ContentStrong() - strong.body() - append(strong) -} - -fun ContentBlock.code(body: ContentBlock.() -> Unit) { - val code = ContentCode() - code.body() - append(code) -} - -fun ContentBlock.link(to: DocumentationNode, body: ContentBlock.() -> Unit) { - val block = if (to.kind == NodeKind.ExternalLink) - ContentExternalLink(to.name) - else - ContentNodeDirectLink(to) - - block.body() - append(block) -} - -open class Content(): ContentBlock() { - open val sections: List<ContentSection> get() = emptyList() - open val summary: ContentNode get() = ContentEmpty - open val description: ContentNode get() = ContentEmpty - - fun findSectionByTag(tag: String): ContentSection? = - sections.firstOrNull { tag.equals(it.tag, ignoreCase = true) } - - companion object { - val Empty = object: Content() { - override fun toString(): String { - return "EMPTY_CONTENT" - } - } - - fun of(vararg child: ContentNode): Content { - val result = MutableContent() - child.forEach { result.append(it) } - return result - } - } -} - -open class MutableContent() : Content() { - private val sectionList = arrayListOf<ContentSection>() - override val sections: List<ContentSection> - get() = sectionList - - fun addSection(tag: String?, subjectName: String?): ContentSection { - val section = ContentSection(tag ?: "", subjectName) - sectionList.add(section) - return section - } - - override val summary: ContentNode get() = children.firstOrNull() ?: ContentEmpty - - override val description: ContentNode by lazy { - val descriptionNodes = children.drop(1) - if (descriptionNodes.isEmpty()) { - ContentEmpty - } else { - val result = ContentSection(ContentTags.Description, null) - result.children.addAll(descriptionNodes) - result - } - } - - override fun equals(other: Any?): Boolean { - if (other !is Content) - return false - return sections == other.sections && children == other.children - } - - override fun hashCode(): Int { - return sections.map { it.hashCode() }.sum() - } - - override fun toString(): String { - if (sections.isEmpty()) - return "<empty>" - return (listOf(summary, description) + sections).joinToString() - } -} - -fun javadocSectionDisplayName(sectionName: String?): String? = - when(sectionName) { - "param" -> "Parameters" - "throws", "exception" -> ContentTags.Exceptions - else -> sectionName?.capitalize() - } diff --git a/core/src/main/kotlin/Model/DocumentationNode.kt b/core/src/main/kotlin/Model/DocumentationNode.kt deleted file mode 100644 index 311b46e4..00000000 --- a/core/src/main/kotlin/Model/DocumentationNode.kt +++ /dev/null @@ -1,276 +0,0 @@ -package org.jetbrains.dokka - -import java.util.* - -enum class NodeKind { - Unknown, - - Package, - Class, - Interface, - Enum, - AnnotationClass, - Exception, - EnumItem, - Object, - TypeAlias, - - Constructor, - Function, - Property, - Field, - - CompanionObjectProperty, - CompanionObjectFunction, - - Parameter, - Receiver, - TypeParameter, - Type, - Supertype, - UpperBound, - LowerBound, - - TypeAliasUnderlyingType, - - Modifier, - NullabilityModifier, - - Module, - - ExternalClass, - Annotation, - - Value, - - SourceUrl, - SourcePosition, - Signature, - - ExternalLink, - QualifiedName, - Platform, - - AllTypes, - - /** - * A note which is rendered once on a page documenting a group of overloaded functions. - * Needs to be generated equally on all overloads. - */ - OverloadGroupNote, - - GroupNode; - - companion object { - val classLike = setOf(Class, Interface, Enum, AnnotationClass, Exception, Object, TypeAlias) - val memberLike = setOf(Function, Property, Field, Constructor, CompanionObjectFunction, CompanionObjectProperty, EnumItem) - } -} - -open class DocumentationNode(val name: String, - content: Content, - val kind: NodeKind) { - - private val references = LinkedHashSet<DocumentationReference>() - - var content: Content = content - private set - - val summary: ContentNode get() = when (kind) { - NodeKind.GroupNode -> this.origins - .map { it.content } - .firstOrNull { !it.isEmpty() } - ?.summary ?: ContentEmpty - else -> content.summary - } - - - val owner: DocumentationNode? - get() = references(RefKind.Owner).singleOrNull()?.to - val details: List<DocumentationNode> - get() = references(RefKind.Detail).map { it.to } - val members: List<DocumentationNode> - get() = references(RefKind.Member).map { it.to } - val origins: List<DocumentationNode> - get() = references(RefKind.Origin).map { it.to } - - val inheritedMembers: List<DocumentationNode> - get() = references(RefKind.InheritedMember).map { it.to } - val allInheritedMembers: List<DocumentationNode> - get() = recursiveInheritedMembers() - val inheritedCompanionObjectMembers: List<DocumentationNode> - get() = references(RefKind.InheritedCompanionObjectMember).map { it.to } - val extensions: List<DocumentationNode> - get() = references(RefKind.Extension).map { it.to } - val inheritors: List<DocumentationNode> - get() = references(RefKind.Inheritor).map { it.to } - val overrides: List<DocumentationNode> - get() = references(RefKind.Override).map { it.to } - val links: List<DocumentationNode> - get() = references(RefKind.Link).map { it.to } - val hiddenLinks: List<DocumentationNode> - get() = references(RefKind.HiddenLink).map { it.to } - val annotations: List<DocumentationNode> - get() = references(RefKind.Annotation).map { it.to } - val deprecation: DocumentationNode? - get() = references(RefKind.Deprecation).singleOrNull()?.to - val platforms: List<String> - get() = references(RefKind.Platform).map { it.to.name } - val externalType: DocumentationNode? - get() = references(RefKind.ExternalType).map { it.to }.firstOrNull() - - var sinceKotlin: String? - get() = references(RefKind.SinceKotlin).singleOrNull()?.to?.name - set(value) { - dropReferences { it.kind == RefKind.SinceKotlin } - if (value != null) { - append(DocumentationNode(value, Content.Empty, NodeKind.Value), RefKind.SinceKotlin) - } - } - - val supertypes: List<DocumentationNode> - get() = details(NodeKind.Supertype) - - val superclassType: DocumentationNode? - get() = when (kind) { - NodeKind.Supertype -> { - (links + listOfNotNull(externalType)).firstOrNull { it.kind in NodeKind.classLike }?.superclassType - } - NodeKind.Interface -> null - in NodeKind.classLike -> supertypes.firstOrNull { - (it.links + listOfNotNull(it.externalType)).any { it.isSuperclassFor(this) } - } - else -> null - } - - val superclassTypeSequence: Sequence<DocumentationNode> - get() = generateSequence(superclassType) { - it.superclassType - } - - // TODO: Should we allow node mutation? Model merge will copy by ref, so references are transparent, which could nice - fun addReferenceTo(to: DocumentationNode, kind: RefKind) { - references.add(DocumentationReference(this, to, kind)) - } - - fun addReference(reference: DocumentationReference) { - references.add(reference) - } - - fun dropReferences(predicate: (DocumentationReference) -> Boolean) { - references.removeAll(predicate) - } - - fun addAllReferencesFrom(other: DocumentationNode) { - references.addAll(other.references) - } - - fun updateContent(body: MutableContent.() -> Unit) { - if (content !is MutableContent) { - content = MutableContent() - } - (content as MutableContent).body() - } - fun details(kind: NodeKind): List<DocumentationNode> = details.filter { it.kind == kind } - fun members(kind: NodeKind): List<DocumentationNode> = members.filter { it.kind == kind } - fun inheritedMembers(kind: NodeKind): List<DocumentationNode> = inheritedMembers.filter { it.kind == kind } - fun inheritedCompanionObjectMembers(kind: NodeKind): List<DocumentationNode> = inheritedCompanionObjectMembers.filter { it.kind == kind } - fun links(kind: NodeKind): List<DocumentationNode> = links.filter { it.kind == kind } - - fun detail(kind: NodeKind): DocumentationNode = details.single { it.kind == kind } - fun detailOrNull(kind: NodeKind): DocumentationNode? = details.singleOrNull { it.kind == kind } - fun member(kind: NodeKind): DocumentationNode = members.single { it.kind == kind } - fun link(kind: NodeKind): DocumentationNode = links.single { it.kind == kind } - - - fun references(kind: RefKind): List<DocumentationReference> = references.filter { it.kind == kind } - fun allReferences(): Set<DocumentationReference> = references - - override fun toString(): String { - return "$kind:$name" - } -} - -class DocumentationModule(name: String, content: Content = Content.Empty, val nodeRefGraph: NodeReferenceGraph = NodeReferenceGraph()) - : DocumentationNode(name, content, NodeKind.Module) { - -} - -val DocumentationNode.path: List<DocumentationNode> - get() { - val parent = owner ?: return listOf(this) - return parent.path + this - } - -fun findOrCreatePackageNode(module: DocumentationNode?, packageName: String, packageContent: Map<String, Content>, refGraph: NodeReferenceGraph): DocumentationNode { - val node = refGraph.lookup(packageName) ?: run { - val newNode = DocumentationNode( - packageName, - packageContent.getOrElse(packageName) { Content.Empty }, - NodeKind.Package - ) - - refGraph.register(packageName, newNode) - newNode - } - if (module != null && node !in module.members) { - module.append(node, RefKind.Member) - } - return node -} - -fun DocumentationNode.append(child: DocumentationNode, kind: RefKind) { - addReferenceTo(child, kind) - when (kind) { - RefKind.Detail -> child.addReferenceTo(this, RefKind.Owner) - RefKind.Member -> child.addReferenceTo(this, RefKind.Owner) - RefKind.Owner -> child.addReferenceTo(this, RefKind.Member) - RefKind.Origin -> child.addReferenceTo(this, RefKind.Owner) - else -> { /* Do not add any links back for other types */ - } - } -} - -fun DocumentationNode.appendTextNode(text: String, - kind: NodeKind, - refKind: RefKind = RefKind.Detail) { - append(DocumentationNode(text, Content.Empty, kind), refKind) -} - -fun DocumentationNode.qualifiedName(): String { - if (kind == NodeKind.Type) { - return qualifiedNameFromType() - } else if (kind == NodeKind.Package) { - return name - } - return path.dropWhile { it.kind == NodeKind.Module }.map { it.name }.filter { it.isNotEmpty() }.joinToString(".") -} - -fun DocumentationNode.simpleName() = name.substringAfterLast('.') - -private fun DocumentationNode.recursiveInheritedMembers(): List<DocumentationNode> { - val allInheritedMembers = mutableListOf<DocumentationNode>() - recursiveInheritedMembers(allInheritedMembers) - return allInheritedMembers -} - -private fun DocumentationNode.recursiveInheritedMembers(allInheritedMembers: MutableList<DocumentationNode>) { - allInheritedMembers.addAll(inheritedMembers) - System.out.println(allInheritedMembers.size) - inheritedMembers.groupBy { it.owner!! } .forEach { (node, _) -> - node.recursiveInheritedMembers(allInheritedMembers) - } -} - -private fun DocumentationNode.isSuperclassFor(node: DocumentationNode): Boolean { - return when(node.kind) { - NodeKind.Object, NodeKind.Class, NodeKind.Enum -> kind == NodeKind.Class - NodeKind.Exception -> kind == NodeKind.Class || kind == NodeKind.Exception - else -> false - } -} - -fun DocumentationNode.classNodeNameWithOuterClass(): String { - assert(kind in NodeKind.classLike) - return path.dropWhile { it.kind == NodeKind.Package || it.kind == NodeKind.Module }.joinToString(separator = ".") { it.name } -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Model/DocumentationReference.kt b/core/src/main/kotlin/Model/DocumentationReference.kt deleted file mode 100644 index 0b890a78..00000000 --- a/core/src/main/kotlin/Model/DocumentationReference.kt +++ /dev/null @@ -1,126 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Singleton - -enum class RefKind { - Owner, - Member, - InheritedMember, - InheritedCompanionObjectMember, - Detail, - Link, - HiddenLink, - Extension, - Inheritor, - Superclass, - Override, - Annotation, - HiddenAnnotation, - Deprecation, - TopLevelPage, - Platform, - ExternalType, - Origin, - SinceKotlin -} - -data class DocumentationReference(val from: DocumentationNode, val to: DocumentationNode, val kind: RefKind) { -} - -sealed class NodeResolver { - abstract fun resolve(nodeRephGraph: NodeReferenceGraph): DocumentationNode? - class BySignature(var signature: String) : NodeResolver() { - override fun resolve(nodeRephGraph: NodeReferenceGraph): DocumentationNode? { - return nodeRephGraph.lookup(signature) - } - } - - class Exact(var exactNode: DocumentationNode) : NodeResolver() { - override fun resolve(nodeRephGraph: NodeReferenceGraph): DocumentationNode? { - return exactNode - } - } -} - -class PendingDocumentationReference(val lazyNodeFrom: NodeResolver, - val lazyNodeTo: NodeResolver, - val kind: RefKind) { - fun resolve(nodeRephGraph: NodeReferenceGraph) { - val fromNode = lazyNodeFrom.resolve(nodeRephGraph) - val toNode = lazyNodeTo.resolve(nodeRephGraph) - if (fromNode != null && toNode != null) { - fromNode.addReferenceTo(toNode, kind) - } - } -} - -class NodeReferenceGraph { - private val nodeMap = hashMapOf<String, DocumentationNode>() - val nodeMapView: Map<String, DocumentationNode> - get() = HashMap(nodeMap) - - val references = arrayListOf<PendingDocumentationReference>() - - fun register(signature: String, node: DocumentationNode) { - nodeMap[signature] = node - } - - fun link(fromNode: DocumentationNode, toSignature: String, kind: RefKind) { - references.add( - PendingDocumentationReference( - NodeResolver.Exact(fromNode), - NodeResolver.BySignature(toSignature), - kind - )) - } - - fun link(fromSignature: String, toNode: DocumentationNode, kind: RefKind) { - references.add( - PendingDocumentationReference( - NodeResolver.BySignature(fromSignature), - NodeResolver.Exact(toNode), - kind - ) - ) - } - - fun link(fromSignature: String, toSignature: String, kind: RefKind) { - references.add( - PendingDocumentationReference( - NodeResolver.BySignature(fromSignature), - NodeResolver.BySignature(toSignature), - kind - ) - ) - } - - fun addReference(reference: PendingDocumentationReference) { - references.add(reference) - } - - fun lookup(signature: String) = nodeMap[signature] - - fun lookupOrWarn(signature: String, logger: DokkaLogger): DocumentationNode? { - val result = nodeMap[signature] - if (result == null) { - logger.warn("Can't find node by signature `$signature`." + - "This is probably caused by invalid configuration of cross-module dependencies") - } - return result - } - - fun resolveReferences() { - references.forEach { it.resolve(this) } - } -} - -@Singleton -class PlatformNodeRegistry { - private val platformNodes = hashMapOf<String, DocumentationNode>() - - operator fun get(platform: String): DocumentationNode { - return platformNodes.getOrPut(platform) { - DocumentationNode(platform, Content.Empty, NodeKind.Platform) - } - } -} diff --git a/core/src/main/kotlin/Model/ElementSignatureProvider.kt b/core/src/main/kotlin/Model/ElementSignatureProvider.kt deleted file mode 100644 index e8fdde6e..00000000 --- a/core/src/main/kotlin/Model/ElementSignatureProvider.kt +++ /dev/null @@ -1,9 +0,0 @@ -package org.jetbrains.dokka - -import com.intellij.psi.PsiElement -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor - -interface ElementSignatureProvider { - fun signature(forDesc: DeclarationDescriptor): String - fun signature(forPsi: PsiElement): String -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Model/PackageDocs.kt b/core/src/main/kotlin/Model/PackageDocs.kt deleted file mode 100644 index b24efc5d..00000000 --- a/core/src/main/kotlin/Model/PackageDocs.kt +++ /dev/null @@ -1,136 +0,0 @@ -package org.jetbrains.dokka - -import com.google.inject.Inject -import com.google.inject.Singleton -import com.intellij.ide.highlighter.JavaFileType -import com.intellij.psi.PsiClass -import com.intellij.psi.PsiFileFactory -import com.intellij.psi.util.PsiTreeUtil -import com.intellij.util.LocalTimeCounter -import com.intellij.openapi.util.text.StringUtil -import org.intellij.markdown.MarkdownElementTypes -import org.intellij.markdown.MarkdownTokenTypes -import org.intellij.markdown.parser.LinkMap -import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment -import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor -import java.io.File - -@Singleton -class PackageDocs - @Inject constructor(val linkResolver: DeclarationLinkResolver?, - val logger: DokkaLogger, - val environment: KotlinCoreEnvironment, - val refGraph: NodeReferenceGraph, - val elementSignatureProvider: ElementSignatureProvider) -{ - val moduleContent: MutableContent = MutableContent() - private val _packageContent: MutableMap<String, MutableContent> = hashMapOf() - val packageContent: Map<String, Content> - get() = _packageContent - - fun parse(fileName: String, linkResolveContext: List<PackageFragmentDescriptor>) { - val file = File(fileName) - if (file.exists()) { - val text = StringUtil.convertLineSeparators(file.readText()) - val tree = parseMarkdown(text) - val linkMap = LinkMap.buildLinkMap(tree.node, text) - var targetContent: MutableContent = moduleContent - tree.children.forEach { - if (it.type == MarkdownElementTypes.ATX_1) { - val headingText = it.child(MarkdownTokenTypes.ATX_CONTENT)?.text - if (headingText != null) { - targetContent = findTargetContent(headingText.trimStart()) - } - } else { - buildContentTo(it, targetContent, LinkResolver(linkMap) { resolveContentLink(fileName, it, linkResolveContext) }) - } - } - } else { - logger.warn("Include file $file was not found.") - } - } - - private fun parseHtmlAsJavadoc(text: String, packageName: String, file: File) { - val javadocText = text - .replace("*/", "*/") - .removeSurrounding("<html>", "</html>", true).trim() - .removeSurrounding("<body>", "</body>", true) - .lineSequence() - .map { "* $it" } - .joinToString (separator = "\n", prefix = "/**\n", postfix = "\n*/") - parseJavadoc(javadocText, packageName, file) - } - - private fun CharSequence.removeSurrounding(prefix: CharSequence, suffix: CharSequence, ignoringCase: Boolean = false): CharSequence { - if ((length >= prefix.length + suffix.length) && startsWith(prefix, ignoringCase) && endsWith(suffix, ignoringCase)) { - return subSequence(prefix.length, length - suffix.length) - } - return subSequence(0, length) - } - - - private fun parseJavadoc(text: String, packageName: String, file: File) { - - val psiFileFactory = PsiFileFactory.getInstance(environment.project) - val psiFile = psiFileFactory.createFileFromText( - file.nameWithoutExtension + ".java", - JavaFileType.INSTANCE, - "package $packageName; $text\npublic class C {}", - LocalTimeCounter.currentTime(), - false, - true - ) - - val psiClass = PsiTreeUtil.getChildOfType(psiFile, PsiClass::class.java)!! - val parser = JavadocParser(refGraph, logger, elementSignatureProvider, linkResolver?.externalDocumentationLinkResolver!!) - findOrCreatePackageContent(packageName).apply { - val content = parser.parseDocumentation(psiClass).content - children.addAll(content.children) - content.sections.forEach { - addSection(it.tag, it.subjectName).children.addAll(it.children) - } - } - } - - - fun parseJava(fileName: String, packageName: String) { - val file = File(fileName) - if (file.exists()) { - val text = file.readText() - - val trimmedText = text.trim() - - if (trimmedText.startsWith("/**")) { - parseJavadoc(text, packageName, file) - } else if (trimmedText.toLowerCase().startsWith("<html>")) { - parseHtmlAsJavadoc(trimmedText, packageName, file) - } - } - } - - private fun findTargetContent(heading: String): MutableContent { - if (heading.startsWith("Module") || heading.startsWith("module")) { - return moduleContent - } - if (heading.startsWith("Package") || heading.startsWith("package")) { - return findOrCreatePackageContent(heading.substring("package".length).trim()) - } - return findOrCreatePackageContent(heading) - } - - private fun findOrCreatePackageContent(packageName: String) = - _packageContent.getOrPut(packageName) { MutableContent() } - - private fun resolveContentLink(fileName: String, href: String, linkResolveContext: List<PackageFragmentDescriptor>): ContentBlock { - if (linkResolver != null) { - linkResolveContext - .asSequence() - .map { p -> linkResolver.tryResolveContentLink(p, href) } - .filterNotNull() - .firstOrNull() - ?.let { return it } - } - logger.warn("Unresolved link to `$href` in include ($fileName)") - return ContentExternalLink("#") - } -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Model/SourceLinks.kt b/core/src/main/kotlin/Model/SourceLinks.kt deleted file mode 100644 index 99ee362e..00000000 --- a/core/src/main/kotlin/Model/SourceLinks.kt +++ /dev/null @@ -1,78 +0,0 @@ -package org.jetbrains.dokka - -import com.intellij.psi.PsiDocumentManager -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiNameIdentifierOwner -import org.jetbrains.dokka.DokkaConfiguration.SourceLinkDefinition -import org.jetbrains.kotlin.psi.psiUtil.startOffset -import java.io.File - - -fun DocumentationNode.appendSourceLink(psi: PsiElement?, sourceLinks: List<SourceLinkDefinition>) { - val path = psi?.containingFile?.virtualFile?.path ?: return - val canonicalPath = File(path).canonicalPath - - val target = if (psi is PsiNameIdentifierOwner) psi.nameIdentifier else psi - val pair = determineSourceLinkDefinition(canonicalPath, sourceLinks) - if (pair != null) { - val (sourceLinkDefinition, sourceLinkCanonicalPath) = pair - var url = determineUrl(canonicalPath, sourceLinkDefinition, sourceLinkCanonicalPath) - if (sourceLinkDefinition.lineSuffix != null) { - val line = target?.lineNumber() - if (line != null) { - url += sourceLinkDefinition.lineSuffix + line.toString() - } - } - append(DocumentationNode(url, Content.Empty, NodeKind.SourceUrl), RefKind.Detail) - } - - if (target != null) { - append(DocumentationNode(target.sourcePosition(), Content.Empty, NodeKind.SourcePosition), RefKind.Detail) - } -} - -private fun determineSourceLinkDefinition( - canonicalPath: String, - sourceLinks: List<SourceLinkDefinition> -): Pair<SourceLinkDefinition, String>? { - return sourceLinks - .asSequence() - .map { it to File(it.path).canonicalPath } - .firstOrNull { (_, sourceLinkCanonicalPath) -> - canonicalPath.startsWith(sourceLinkCanonicalPath) - } -} - -private fun determineUrl( - canonicalPath: String, - sourceLinkDefinition: SourceLinkDefinition, - sourceLinkCanonicalPath: String -): String { - val relativePath = canonicalPath.substring(sourceLinkCanonicalPath.length) - val relativeUrl = relativePath.replace('\\', '/').removePrefix("/") - return "${sourceLinkDefinition.url.removeSuffix("/")}/$relativeUrl" -} - -private fun PsiElement.sourcePosition(): String { - val path = containingFile.virtualFile.path - val lineNumber = lineNumber() - val columnNumber = columnNumber() - - return when { - lineNumber == null -> path - columnNumber == null -> "$path:$lineNumber" - else -> "$path:$lineNumber:$columnNumber" - } -} - -fun PsiElement.lineNumber(): Int? { - val doc = PsiDocumentManager.getInstance(project).getDocument(containingFile) - // IJ uses 0-based line-numbers; external source browsers use 1-based - return doc?.getLineNumber(textRange.startOffset)?.plus(1) -} - -fun PsiElement.columnNumber(): Int? { - val doc = PsiDocumentManager.getInstance(project).getDocument(containingFile) ?: return null - val lineNumber = doc.getLineNumber(textRange.startOffset) - return startOffset - doc.getLineStartOffset(lineNumber) -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Samples/DefaultSampleProcessingService.kt b/core/src/main/kotlin/Samples/DefaultSampleProcessingService.kt deleted file mode 100644 index da74495f..00000000 --- a/core/src/main/kotlin/Samples/DefaultSampleProcessingService.kt +++ /dev/null @@ -1,103 +0,0 @@ -package org.jetbrains.dokka.Samples - -import com.google.inject.Inject -import com.intellij.psi.PsiElement -import org.jetbrains.dokka.* -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.PackageViewDescriptor -import org.jetbrains.kotlin.idea.kdoc.getKDocLinkResolutionScope -import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink -import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag -import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.psi.KtBlockExpression -import org.jetbrains.kotlin.psi.KtDeclarationWithBody -import org.jetbrains.kotlin.psi.KtFile -import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils -import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter -import org.jetbrains.kotlin.resolve.scopes.ResolutionScope - - -open class DefaultSampleProcessingService -@Inject constructor(val configuration: DokkaConfiguration, - val logger: DokkaLogger, - val resolutionFacade: DokkaResolutionFacade) - : SampleProcessingService { - - override fun resolveSample(descriptor: DeclarationDescriptor, functionName: String?, kdocTag: KDocTag): ContentNode { - if (functionName == null) { - logger.warn("Missing function name in @sample in ${descriptor.signature()}") - return ContentBlockSampleCode().apply { append(ContentText("//Missing function name in @sample")) } - } - val bindingContext = BindingContext.EMPTY - val symbol = resolveKDocLink(bindingContext, resolutionFacade, descriptor, kdocTag, functionName.split(".")).firstOrNull() - if (symbol == null) { - logger.warn("Unresolved function $functionName in @sample in ${descriptor.signature()}") - return ContentBlockSampleCode().apply { append(ContentText("//Unresolved: $functionName")) } - } - val psiElement = DescriptorToSourceUtils.descriptorToDeclaration(symbol) - if (psiElement == null) { - logger.warn("Can't find source for function $functionName in @sample in ${descriptor.signature()}") - return ContentBlockSampleCode().apply { append(ContentText("//Source not found: $functionName")) } - } - - val text = processSampleBody(psiElement).trim { it == '\n' || it == '\r' }.trimEnd() - val lines = text.split("\n") - val indent = lines.filter(String::isNotBlank).map { it.takeWhile(Char::isWhitespace).count() }.min() ?: 0 - val finalText = lines.joinToString("\n") { it.drop(indent) } - - return ContentBlockSampleCode(importsBlock = processImports(psiElement)).apply { append(ContentText(finalText)) } - } - - protected open fun processSampleBody(psiElement: PsiElement): String = when (psiElement) { - is KtDeclarationWithBody -> { - val bodyExpression = psiElement.bodyExpression - when (bodyExpression) { - is KtBlockExpression -> bodyExpression.text.removeSurrounding("{", "}") - else -> bodyExpression!!.text - } - } - else -> psiElement.text - } - - protected open fun processImports(psiElement: PsiElement): ContentBlockCode { - val psiFile = psiElement.containingFile - if (psiFile is KtFile) { - return ContentBlockCode("kotlin").apply { - append(ContentText(psiFile.importList?.text ?: "")) - } - } else { - return ContentBlockCode("") - } - } - - private fun resolveInScope(functionName: String, scope: ResolutionScope): DeclarationDescriptor? { - var currentScope = scope - val parts = functionName.split('.') - - var symbol: DeclarationDescriptor? = null - - for (part in parts) { - // short name - val symbolName = Name.identifier(part) - val partSymbol = currentScope.getContributedDescriptors(DescriptorKindFilter.ALL) { it == symbolName }.firstOrNull { it.name == symbolName } - - if (partSymbol == null) { - symbol = null - break - } - @Suppress("IfThenToElvis") - currentScope = if (partSymbol is ClassDescriptor) - partSymbol.defaultType.memberScope - else if (partSymbol is PackageViewDescriptor) - partSymbol.memberScope - else - getKDocLinkResolutionScope(resolutionFacade, partSymbol) - symbol = partSymbol - } - - return symbol - } -} - diff --git a/core/src/main/kotlin/Samples/SampleProcessingService.kt b/core/src/main/kotlin/Samples/SampleProcessingService.kt deleted file mode 100644 index 86c917cf..00000000 --- a/core/src/main/kotlin/Samples/SampleProcessingService.kt +++ /dev/null @@ -1,9 +0,0 @@ -package org.jetbrains.dokka.Samples - -import org.jetbrains.dokka.ContentNode -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag - -interface SampleProcessingService { - fun resolveSample(descriptor: DeclarationDescriptor, functionName: String?, kdocTag: KDocTag): ContentNode -}
\ No newline at end of file diff --git a/core/src/main/kotlin/Utilities/DokkaLogging.kt b/core/src/main/kotlin/Utilities/DokkaLogging.kt deleted file mode 100644 index 1ef52837..00000000 --- a/core/src/main/kotlin/Utilities/DokkaLogging.kt +++ /dev/null @@ -1,27 +0,0 @@ -package org.jetbrains.dokka - -interface DokkaLogger { - fun info(message: String) - fun warn(message: String) - fun error(message: String) -} - -object DokkaConsoleLogger : DokkaLogger { - var warningCount: Int = 0 - - override fun info(message: String) = println(message) - override fun warn(message: String) { - println("WARN: $message") - warningCount++ - } - - override fun error(message: String) = println("ERROR: $message") - - fun report() { - if (warningCount > 0) { - println("generation completed with $warningCount warnings") - } else { - println("generation completed successfully") - } - } -} diff --git a/core/src/main/kotlin/Utilities/DokkaModules.kt b/core/src/main/kotlin/Utilities/DokkaModules.kt deleted file mode 100644 index 919ec30f..00000000 --- a/core/src/main/kotlin/Utilities/DokkaModules.kt +++ /dev/null @@ -1,85 +0,0 @@ -package org.jetbrains.dokka.Utilities - -import com.google.inject.Binder -import com.google.inject.Module -import com.google.inject.TypeLiteral -import com.google.inject.binder.AnnotatedBindingBuilder -import com.google.inject.name.Names -import org.jetbrains.dokka.* -import org.jetbrains.dokka.Formats.FormatDescriptor -import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment -import java.io.File -import kotlin.reflect.KClass - -const val impliedPlatformsName = "impliedPlatforms" - -class DokkaRunModule(val configuration: DokkaConfiguration) : Module { - override fun configure(binder: Binder) { - binder.bind<DokkaConfiguration>().toInstance(configuration) - binder.bind(StringListType).annotatedWith(Names.named(impliedPlatformsName)).toInstance(configuration.impliedPlatforms) - - binder.bind(File::class.java).annotatedWith(Names.named("outputDir")).toInstance(File(configuration.outputDir)) - } - -} - -class DokkaAnalysisModule(val environment: AnalysisEnvironment, - val configuration: DokkaConfiguration, - val defaultPlatformsProvider: DefaultPlatformsProvider, - val nodeReferenceGraph: NodeReferenceGraph, - val passConfiguration: DokkaConfiguration.PassConfiguration, - val logger: DokkaLogger) : Module { - override fun configure(binder: Binder) { - binder.bind<DokkaLogger>().toInstance(logger) - - val coreEnvironment = environment.createCoreEnvironment() - binder.bind<KotlinCoreEnvironment>().toInstance(coreEnvironment) - - val (dokkaResolutionFacade, libraryResolutionFacade) = environment.createResolutionFacade(coreEnvironment) - binder.bind<DokkaResolutionFacade>().toInstance(dokkaResolutionFacade) - binder.bind<DokkaResolutionFacade>().annotatedWith(Names.named("libraryResolutionFacade")).toInstance(libraryResolutionFacade) - - binder.bind<DokkaConfiguration.PassConfiguration>().toInstance(passConfiguration) - - binder.bind<DefaultPlatformsProvider>().toInstance(defaultPlatformsProvider) - - binder.bind<NodeReferenceGraph>().toInstance(nodeReferenceGraph) - - val descriptor = ServiceLocator.lookup<FormatDescriptor>("format", configuration.format) - descriptor.configureAnalysis(binder) - } -} - -object StringListType : TypeLiteral<@JvmSuppressWildcards List<String>>() - -class DokkaOutputModule(val configuration: DokkaConfiguration, - val logger: DokkaLogger) : Module { - override fun configure(binder: Binder) { - binder.bind<DokkaLogger>().toInstance(logger) - - val descriptor = ServiceLocator.lookup<FormatDescriptor>("format", configuration.format) - - descriptor.configureOutput(binder) - } -} - -private inline fun <reified T: Any> Binder.registerCategory(category: String) { - ServiceLocator.allServices(category).forEach { - @Suppress("UNCHECKED_CAST") - bind(T::class.java).annotatedWith(Names.named(it.name)).to(T::class.java.classLoader.loadClass(it.className) as Class<T>) - } -} - -private inline fun <reified Base : Any, reified T : Base> Binder.bindNameAnnotated(name: String) { - bind(Base::class.java).annotatedWith(Names.named(name)).to(T::class.java) -} - - -inline fun <reified T: Any> Binder.bind(): AnnotatedBindingBuilder<T> = bind(T::class.java) - -inline fun <reified T: Any> Binder.lazyBind(): Lazy<AnnotatedBindingBuilder<T>> = lazy { bind(T::class.java) } - -inline infix fun <reified T: Any, TKClass: KClass<out T>> Lazy<AnnotatedBindingBuilder<T>>.toOptional(kClass: TKClass?) = - kClass?.let { value toType it } - -inline infix fun <reified T: Any, TKClass: KClass<out T>> AnnotatedBindingBuilder<T>.toType(kClass: TKClass) = to(kClass.java) diff --git a/core/src/main/kotlin/Utilities/Links.kt b/core/src/main/kotlin/Utilities/Links.kt deleted file mode 100644 index 34423e4e..00000000 --- a/core/src/main/kotlin/Utilities/Links.kt +++ /dev/null @@ -1,18 +0,0 @@ -package org.jetbrains.dokka.Utilities - -import org.jetbrains.dokka.DokkaConfiguration -import org.jetbrains.dokka.ExternalDocumentationLinkImpl - -fun DokkaConfiguration.PassConfiguration.defaultLinks(): List<ExternalDocumentationLinkImpl> { - val links = mutableListOf<ExternalDocumentationLinkImpl>() - if (!noJdkLink) - links += DokkaConfiguration.ExternalDocumentationLink - .Builder("https://docs.oracle.com/javase/${jdkVersion}/docs/api/") - .build() as ExternalDocumentationLinkImpl - - if (!noStdlibLink) - links += DokkaConfiguration.ExternalDocumentationLink - .Builder("https://kotlinlang.org/api/latest/jvm/stdlib/") - .build() as ExternalDocumentationLinkImpl - return links -} diff --git a/core/src/main/kotlin/Utilities/Path.kt b/core/src/main/kotlin/Utilities/Path.kt deleted file mode 100644 index 05838499..00000000 --- a/core/src/main/kotlin/Utilities/Path.kt +++ /dev/null @@ -1,5 +0,0 @@ -package org.jetbrains.dokka - -import java.io.File - -fun File.appendExtension(extension: String) = if (extension.isEmpty()) this else File(path + "." + extension) diff --git a/integration/src/main/kotlin/org/jetbrains/dokka/configuration.kt b/core/src/main/kotlin/configuration.kt index 6eae9701..eaee351b 100644 --- a/integration/src/main/kotlin/org/jetbrains/dokka/configuration.kt +++ b/core/src/main/kotlin/configuration.kt @@ -1,7 +1,31 @@ +@file:Suppress("FunctionName") + package org.jetbrains.dokka +import com.google.gson.Gson +import java.io.File +import java.io.Serializable import java.net.URL +object DokkaDefaults { + const val outputDir = "./dokka" + const val format: String = "html" + val cacheRoot: String? = null + const val offlineMode: Boolean = false + const val failOnWarning: Boolean = false + + const val includeNonPublic: Boolean = false + const val includeRootPackage: Boolean = false + const val reportUndocumented: Boolean = false + const val skipEmptyPackages: Boolean = false + const val skipDeprecated: Boolean = false + const val jdkVersion: Int = 8 + const val noStdlibLink: Boolean = false + const val noJdkLink: Boolean = false + val analysisPlatform: Platform = Platform.DEFAULT + const val suppress: Boolean = false +} + enum class Platform(val key: String) { jvm("jvm"), js("js"), @@ -23,18 +47,40 @@ enum class Platform(val key: String) { } } +data class DokkaSourceSetID( + val moduleName: String, + val sourceSetName: String +) : Serializable { + override fun toString(): String { + return "$moduleName/$sourceSetName" + } +} + +fun DokkaConfigurationImpl(json: String): DokkaConfigurationImpl { + return Gson().fromJson(json, DokkaConfigurationImpl::class.java) +} + +fun DokkaConfiguration.toJson(): String { + return Gson().toJson(this) +} + interface DokkaConfiguration { val outputDir: String - val format: String - val generateIndexPages: Boolean val cacheRoot: String? - val passesConfigurations: List<PassConfiguration> - val impliedPlatforms: List<String> + val offlineMode: Boolean + val failOnWarning: Boolean + val sourceSets: List<DokkaSourceSet> + val modules: List<DokkaModuleDescription> + val pluginsClasspath: List<File> + val pluginsConfiguration: Map<String, String> - interface PassConfiguration { - val moduleName: String + interface DokkaSourceSet { + val sourceSetID: DokkaSourceSetID + val displayName: String + val moduleDisplayName: String val classpath: List<String> val sourceRoots: List<SourceRoot> + val dependentSourceSets: Set<DokkaSourceSetID> val samples: List<String> val includes: List<String> val includeNonPublic: Boolean @@ -51,10 +97,7 @@ interface DokkaConfiguration { val noStdlibLink: Boolean val noJdkLink: Boolean val suppressedFiles: List<String> - val collectInheritedExtensionsFromLibraries: Boolean val analysisPlatform: Platform - val targets: List<String> - val sinceKotlin: String? } interface SourceRoot { @@ -67,10 +110,16 @@ interface DokkaConfiguration { val lineSuffix: String? } + interface DokkaModuleDescription { + val name: String + val path: String + val docFile: String + } + interface PackageOptions { val prefix: String val includeNonPublic: Boolean - val reportUndocumented: Boolean + val reportUndocumented: Boolean? val skipDeprecated: Boolean val suppress: Boolean } @@ -79,8 +128,10 @@ interface DokkaConfiguration { val url: URL val packageListUrl: URL - open class Builder(open var url: URL? = null, - open var packageListUrl: URL? = null) { + open class Builder( + open var url: URL? = null, + open var packageListUrl: URL? = null + ) { constructor(root: String, packageList: String? = null) : this(URL(root), packageList?.let { URL(it) }) @@ -94,3 +145,5 @@ interface DokkaConfiguration { } } } + + diff --git a/integration/src/main/kotlin/org/jetbrains/dokka/defaultConfiguration.kt b/core/src/main/kotlin/defaultConfiguration.kt index 78112904..02274e5d 100644 --- a/integration/src/main/kotlin/org/jetbrains/dokka/defaultConfiguration.kt +++ b/core/src/main/kotlin/defaultConfiguration.kt @@ -1,21 +1,28 @@ package org.jetbrains.dokka +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet import java.io.File import java.net.URL +import java.io.Serializable data class DokkaConfigurationImpl( override val outputDir: String, - override val format: String, - override val generateIndexPages: Boolean, override val cacheRoot: String?, - override val impliedPlatforms: List<String>, - override val passesConfigurations: List<PassConfigurationImpl> + override val offlineMode: Boolean, + override val sourceSets: List<DokkaSourceSetImpl>, + override val pluginsClasspath: List<File>, + override val pluginsConfiguration: Map<String, String>, + override val modules: List<DokkaModuleDescriptionImpl>, + override val failOnWarning: Boolean ) : DokkaConfiguration -data class PassConfigurationImpl ( - override val moduleName: String, +data class DokkaSourceSetImpl( + override val moduleDisplayName: String, + override val displayName: String, + override val sourceSetID: DokkaSourceSetID, override val classpath: List<String>, override val sourceRoots: List<SourceRootImpl>, + override val dependentSourceSets: Set<DokkaSourceSetID>, override val samples: List<String>, override val includes: List<String>, override val includeNonPublic: Boolean, @@ -32,22 +39,24 @@ data class PassConfigurationImpl ( override val noStdlibLink: Boolean, override val noJdkLink: Boolean, override val suppressedFiles: List<String>, - override val collectInheritedExtensionsFromLibraries: Boolean, - override val analysisPlatform: Platform, - override val targets: List<String>, - override val sinceKotlin: String? -) : DokkaConfiguration.PassConfiguration + override val analysisPlatform: Platform +) : DokkaSourceSet +data class DokkaModuleDescriptionImpl( + override val name: String, + override val path: String, + override val docFile: String +) : DokkaConfiguration.DokkaModuleDescription data class SourceRootImpl( override val path: String -): DokkaConfiguration.SourceRoot +) : DokkaConfiguration.SourceRoot data class SourceLinkDefinitionImpl( override val path: String, override val url: String, override val lineSuffix: String? -): DokkaConfiguration.SourceLinkDefinition { +) : DokkaConfiguration.SourceLinkDefinition { companion object { fun parseSourceLinkDefinition(srcLink: String): SourceLinkDefinitionImpl { val (path, urlAndLine) = srcLink.split('=') @@ -62,12 +71,13 @@ data class SourceLinkDefinitionImpl( data class PackageOptionsImpl( override val prefix: String, override val includeNonPublic: Boolean, - override val reportUndocumented: Boolean, + override val reportUndocumented: Boolean?, override val skipDeprecated: Boolean, override val suppress: Boolean -): DokkaConfiguration.PackageOptions +) : DokkaConfiguration.PackageOptions -data class ExternalDocumentationLinkImpl(override val url: URL, - override val packageListUrl: URL -) : DokkaConfiguration.ExternalDocumentationLink
\ No newline at end of file +data class ExternalDocumentationLinkImpl( + override val url: URL, + override val packageListUrl: URL +) : DokkaConfiguration.ExternalDocumentationLink, Serializable diff --git a/core/src/main/kotlin/javadoc/docbase.kt b/core/src/main/kotlin/javadoc/docbase.kt deleted file mode 100644 index 0bf72ccf..00000000 --- a/core/src/main/kotlin/javadoc/docbase.kt +++ /dev/null @@ -1,539 +0,0 @@ -package org.jetbrains.dokka.javadoc - -import com.sun.javadoc.* -import org.jetbrains.dokka.* -import java.lang.reflect.Modifier.* -import java.util.* -import kotlin.reflect.KClass - -private interface HasModule { - val module: ModuleNodeAdapter -} - -private interface HasDocumentationNode { - val node: DocumentationNode -} - -open class DocumentationNodeBareAdapter(override val node: DocumentationNode) : Doc, HasDocumentationNode { - private var rawCommentText_: String? = null - - override fun name(): String = node.name - override fun position(): SourcePosition? = SourcePositionAdapter(node) - - override fun inlineTags(): Array<out Tag>? = emptyArray() - override fun firstSentenceTags(): Array<out Tag>? = emptyArray() - override fun tags(): Array<out Tag> = emptyArray() - override fun tags(tagname: String?): Array<out Tag>? = tags().filter { it.kind() == tagname || it.kind() == "@$tagname" }.toTypedArray() - override fun seeTags(): Array<out SeeTag>? = tags().filterIsInstance<SeeTag>().toTypedArray() - override fun commentText(): String = "" - - override fun setRawCommentText(rawDocumentation: String?) { - rawCommentText_ = rawDocumentation ?: "" - } - - override fun getRawCommentText(): String = rawCommentText_ ?: "" - - override fun isError(): Boolean = false - override fun isException(): Boolean = node.kind == NodeKind.Exception - override fun isEnumConstant(): Boolean = node.kind == NodeKind.EnumItem - override fun isEnum(): Boolean = node.kind == NodeKind.Enum - override fun isMethod(): Boolean = node.kind == NodeKind.Function - override fun isInterface(): Boolean = node.kind == NodeKind.Interface - override fun isField(): Boolean = node.kind == NodeKind.Field - override fun isClass(): Boolean = node.kind == NodeKind.Class - override fun isAnnotationType(): Boolean = node.kind == NodeKind.AnnotationClass - override fun isConstructor(): Boolean = node.kind == NodeKind.Constructor - override fun isOrdinaryClass(): Boolean = node.kind == NodeKind.Class - override fun isAnnotationTypeElement(): Boolean = node.kind == NodeKind.Annotation - - override fun compareTo(other: Any?): Int = when (other) { - !is DocumentationNodeAdapter -> 1 - else -> node.name.compareTo(other.node.name) - } - - override fun equals(other: Any?): Boolean = node.qualifiedName() == (other as? DocumentationNodeAdapter)?.node?.qualifiedName() - override fun hashCode(): Int = node.name.hashCode() - - override fun isIncluded(): Boolean = node.kind != NodeKind.ExternalClass -} - - -// TODO think of source position instead of null -// TODO tags -open class DocumentationNodeAdapter(override val module: ModuleNodeAdapter, node: DocumentationNode) : DocumentationNodeBareAdapter(node), HasModule { - override fun inlineTags(): Array<out Tag> = buildInlineTags(module, this, node.content).toTypedArray() - override fun firstSentenceTags(): Array<out Tag> = buildInlineTags(module, this, node.summary).toTypedArray() - - override fun tags(): Array<out Tag> { - val result = ArrayList<Tag>(buildInlineTags(module, this, node.content)) - node.content.sections.flatMapTo(result) { - when (it.tag) { - ContentTags.SeeAlso -> buildInlineTags(module, this, it) - else -> emptyList<Tag>() - } - } - - node.deprecation?.let { - val content = it.content.asText() - result.add(TagImpl(this, "deprecated", content ?: "")) - } - - return result.toTypedArray() - } -} - -// should be extension property but can't because of KT-8745 -private fun <T> nodeAnnotations(self: T): List<AnnotationDescAdapter> where T : HasModule, T : HasDocumentationNode - = self.node.annotations.map { AnnotationDescAdapter(self.module, it) } - -private fun DocumentationNode.hasAnnotation(klass: KClass<*>) = klass.qualifiedName in annotations.map { it.qualifiedName() } -private fun DocumentationNode.hasModifier(name: String) = details(NodeKind.Modifier).any { it.name == name } - - -class PackageAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : DocumentationNodeAdapter(module, node), PackageDoc { - private val allClasses = listOf(node).collectAllTypesRecursively() - - override fun findClass(className: String?): ClassDoc? = - allClasses.get(className)?.let { ClassDocumentationNodeAdapter(module, it) } - - override fun annotationTypes(): Array<out AnnotationTypeDoc> = emptyArray() - override fun annotations(): Array<out AnnotationDesc> = node.members(NodeKind.AnnotationClass).map { AnnotationDescAdapter(module, it) }.toTypedArray() - override fun exceptions(): Array<out ClassDoc> = node.members(NodeKind.Exception).map { ClassDocumentationNodeAdapter(module, it) }.toTypedArray() - override fun ordinaryClasses(): Array<out ClassDoc> = node.members(NodeKind.Class).map { ClassDocumentationNodeAdapter(module, it) }.toTypedArray() - override fun interfaces(): Array<out ClassDoc> = node.members(NodeKind.Interface).map { ClassDocumentationNodeAdapter(module, it) }.toTypedArray() - override fun errors(): Array<out ClassDoc> = emptyArray() - override fun enums(): Array<out ClassDoc> = node.members(NodeKind.Enum).map { ClassDocumentationNodeAdapter(module, it) }.toTypedArray() - override fun allClasses(filter: Boolean): Array<out ClassDoc> = allClasses.values.map { ClassDocumentationNodeAdapter(module, it) }.toTypedArray() - override fun allClasses(): Array<out ClassDoc> = allClasses(true) - - override fun isIncluded(): Boolean = node.name in module.allPackages -} - -class AnnotationTypeDocAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : ClassDocumentationNodeAdapter(module, node), AnnotationTypeDoc { - override fun elements(): Array<out AnnotationTypeElementDoc>? = emptyArray() // TODO -} - -class AnnotationDescAdapter(val module: ModuleNodeAdapter, val node: DocumentationNode) : AnnotationDesc { - override fun annotationType(): AnnotationTypeDoc? = AnnotationTypeDocAdapter(module, node.links.find { it.kind == NodeKind.AnnotationClass } ?: node) // TODO ????? - override fun isSynthesized(): Boolean = false - override fun elementValues(): Array<out AnnotationDesc.ElementValuePair>? = emptyArray() // TODO -} - -open class ProgramElementAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : DocumentationNodeAdapter(module, node), ProgramElementDoc { - override fun isPublic(): Boolean = node.hasModifier("public") || node.hasModifier("internal") - override fun isPackagePrivate(): Boolean = false - override fun isStatic(): Boolean = node.hasModifier("static") - override fun modifierSpecifier(): Int = visibilityModifier or (if (isStatic) STATIC else 0) - private val visibilityModifier - get() = when { - isPublic -> PUBLIC - isPrivate -> PRIVATE - isProtected -> PROTECTED - else -> 0 - } - override fun qualifiedName(): String? = node.qualifiedName() - override fun annotations(): Array<out AnnotationDesc>? = nodeAnnotations(this).toTypedArray() - override fun modifiers(): String? = "public ${if (isStatic) "static" else ""}".trim() - override fun isProtected(): Boolean = node.hasModifier("protected") - - override fun isFinal(): Boolean = node.hasModifier("final") - - override fun containingPackage(): PackageDoc? { - if (node.kind == NodeKind.Type) { - return null - } - - var owner: DocumentationNode? = node - while (owner != null) { - if (owner.kind == NodeKind.Package) { - return PackageAdapter(module, owner) - } - owner = owner.owner - } - - return null - } - - override fun containingClass(): ClassDoc? { - if (node.kind == NodeKind.Type) { - return null - } - - var owner = node.owner - while (owner != null) { - if (owner.kind in NodeKind.classLike) { - return ClassDocumentationNodeAdapter(module, owner) - } - owner = owner.owner - } - - return null - } - - override fun isPrivate(): Boolean = node.hasModifier("private") - override fun isIncluded(): Boolean = containingPackage()?.isIncluded ?: false && containingClass()?.let { it.isIncluded } ?: true -} - -open class TypeAdapter(override val module: ModuleNodeAdapter, override val node: DocumentationNode) : Type, HasDocumentationNode, HasModule { - private val javaLanguageService = JavaLanguageService() - - override fun qualifiedTypeName(): String = javaLanguageService.getArrayElementType(node)?.qualifiedNameFromType() ?: node.qualifiedNameFromType() - override fun typeName(): String = (javaLanguageService.getArrayElementType(node)?.simpleName() ?: node.simpleName()) + dimension() - override fun simpleTypeName(): String = typeName() // TODO difference typeName() vs simpleTypeName() - - override fun dimension(): String = Collections.nCopies(javaLanguageService.getArrayDimension(node), "[]").joinToString("") - override fun isPrimitive(): Boolean = simpleTypeName() in setOf("int", "long", "short", "byte", "char", "double", "float", "boolean", "void") - - override fun asClassDoc(): ClassDoc? = if (isPrimitive) null else - elementType?.asClassDoc() ?: - when (node.kind) { - in NodeKind.classLike, - NodeKind.ExternalClass, - NodeKind.Exception -> module.classNamed(qualifiedTypeName()) ?: ClassDocumentationNodeAdapter(module, node) - - else -> when { - node.links.isNotEmpty() -> TypeAdapter(module, node.links.first()).asClassDoc() - else -> ClassDocumentationNodeAdapter(module, node) // TODO ? - } - } - - override fun asTypeVariable(): TypeVariable? = if (node.kind == NodeKind.TypeParameter) TypeVariableAdapter(module, node) else null - override fun asParameterizedType(): ParameterizedType? = - if (node.details(NodeKind.Type).isNotEmpty() && javaLanguageService.getArrayElementType(node) == null) - ParameterizedTypeAdapter(module, node) - else - null - - override fun asAnnotationTypeDoc(): AnnotationTypeDoc? = if (node.kind == NodeKind.AnnotationClass) AnnotationTypeDocAdapter(module, node) else null - override fun asAnnotatedType(): AnnotatedType? = if (node.annotations.isNotEmpty()) AnnotatedTypeAdapter(module, node) else null - override fun getElementType(): Type? = javaLanguageService.getArrayElementType(node)?.let { et -> TypeAdapter(module, et) } - override fun asWildcardType(): WildcardType? = null - - override fun toString(): String = qualifiedTypeName() + dimension() - override fun hashCode(): Int = node.name.hashCode() - override fun equals(other: Any?): Boolean = other is TypeAdapter && toString() == other.toString() -} - -class NotAnnotatedTypeAdapter(typeAdapter: AnnotatedTypeAdapter) : Type by typeAdapter { - override fun asAnnotatedType() = null -} - -class AnnotatedTypeAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : TypeAdapter(module, node), AnnotatedType { - override fun underlyingType(): Type? = NotAnnotatedTypeAdapter(this) - override fun annotations(): Array<out AnnotationDesc> = nodeAnnotations(this).toTypedArray() -} - -class WildcardTypeAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : TypeAdapter(module, node), WildcardType { - override fun extendsBounds(): Array<out Type> = node.details(NodeKind.UpperBound).map { TypeAdapter(module, it) }.toTypedArray() - override fun superBounds(): Array<out Type> = node.details(NodeKind.LowerBound).map { TypeAdapter(module, it) }.toTypedArray() -} - -class TypeVariableAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : TypeAdapter(module, node), TypeVariable { - override fun owner(): ProgramElementDoc = node.owner!!.let<DocumentationNode, ProgramElementDoc> { owner -> - when (owner.kind) { - NodeKind.Function, - NodeKind.Constructor -> ExecutableMemberAdapter(module, owner) - - NodeKind.Class, - NodeKind.Interface, - NodeKind.Enum -> ClassDocumentationNodeAdapter(module, owner) - - else -> ProgramElementAdapter(module, node.owner!!) - } - } - - override fun bounds(): Array<out Type>? = node.details(NodeKind.UpperBound).map { TypeAdapter(module, it) }.toTypedArray() - override fun annotations(): Array<out AnnotationDesc>? = node.members(NodeKind.Annotation).map { AnnotationDescAdapter(module, it) }.toTypedArray() - - override fun qualifiedTypeName(): String = node.name - override fun simpleTypeName(): String = node.name - override fun typeName(): String = node.name - - override fun hashCode(): Int = node.name.hashCode() - override fun equals(other: Any?): Boolean = other is Type && other.typeName() == typeName() && other.asTypeVariable()?.owner() == owner() - - override fun asTypeVariable(): TypeVariableAdapter = this -} - -class ParameterizedTypeAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : TypeAdapter(module, node), ParameterizedType { - override fun typeArguments(): Array<out Type> = node.details(NodeKind.Type).map { TypeVariableAdapter(module, it) }.toTypedArray() - override fun superclassType(): Type? = - node.lookupSuperClasses(module) - .firstOrNull { it.kind == NodeKind.Class || it.kind == NodeKind.ExternalClass } - ?.let { ClassDocumentationNodeAdapter(module, it) } - - override fun interfaceTypes(): Array<out Type> = - node.lookupSuperClasses(module) - .filter { it.kind == NodeKind.Interface } - .map { ClassDocumentationNodeAdapter(module, it) } - .toTypedArray() - - override fun containingType(): Type? = when (node.owner?.kind) { - NodeKind.Package -> null - NodeKind.Class, - NodeKind.Interface, - NodeKind.Object, - NodeKind.Enum -> ClassDocumentationNodeAdapter(module, node.owner!!) - - else -> null - } -} - -class ParameterAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : DocumentationNodeAdapter(module, node), Parameter { - override fun typeName(): String? = type()?.typeName() - override fun type(): Type? = TypeAdapter(module, node.detail(NodeKind.Type)) - override fun annotations(): Array<out AnnotationDesc> = nodeAnnotations(this).toTypedArray() -} - -class ReceiverParameterAdapter(module: ModuleNodeAdapter, val receiverType: DocumentationNode, val parent: ExecutableMemberAdapter) : DocumentationNodeAdapter(module, receiverType), Parameter { - override fun typeName(): String? = receiverType.name - override fun type(): Type? = TypeAdapter(module, receiverType) - override fun annotations(): Array<out AnnotationDesc> = nodeAnnotations(this).toTypedArray() - override fun name(): String = tryName("receiver") - - private tailrec fun tryName(name: String): String = when (name) { - in parent.parameters().drop(1).map { it.name() } -> tryName("$$name") - else -> name - } -} - -fun classOf(fqName: String, kind: NodeKind = NodeKind.Class) = DocumentationNode(fqName.substringAfterLast(".", fqName), Content.Empty, kind).let { node -> - val pkg = fqName.substringBeforeLast(".", "") - if (pkg.isNotEmpty()) { - node.append(DocumentationNode(pkg, Content.Empty, NodeKind.Package), RefKind.Owner) - } - - node -} - -private fun DocumentationNode.hasNonEmptyContent() = - this.content.summary !is ContentEmpty || this.content.description !is ContentEmpty || this.content.sections.isNotEmpty() - - -open class ExecutableMemberAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : ProgramElementAdapter(module, node), ExecutableMemberDoc { - - override fun isSynthetic(): Boolean = false - override fun isNative(): Boolean = node.annotations.any { it.name == "native" } - - override fun thrownExceptions(): Array<out ClassDoc> = emptyArray() // TODO - override fun throwsTags(): Array<out ThrowsTag> = - node.content.sections - .filter { it.tag == ContentTags.Exceptions && it.subjectName != null } - .map { ThrowsTagAdapter(this, ClassDocumentationNodeAdapter(module, classOf(it.subjectName!!, NodeKind.Exception)), it.children) } - .toTypedArray() - - override fun isVarArgs(): Boolean = node.details(NodeKind.Parameter).last().hasModifier("vararg") - - override fun isSynchronized(): Boolean = node.annotations.any { it.name == "synchronized" } - - override fun paramTags(): Array<out ParamTag> = - collectParamTags(NodeKind.Parameter, sectionFilter = { it.subjectName in parameters().map { it.name() } }) - - override fun thrownExceptionTypes(): Array<out Type> = emptyArray() - override fun receiverType(): Type? = receiverNode()?.let { receiver -> TypeAdapter(module, receiver) } - override fun flatSignature(): String = node.details(NodeKind.Parameter).map { JavaLanguageService().renderType(it) }.joinToString(", ", "(", ")") - override fun signature(): String = node.details(NodeKind.Parameter).map { JavaLanguageService().renderType(it) }.joinToString(", ", "(", ")") // TODO it should be FQ types - - override fun parameters(): Array<out Parameter> = - ((receiverNode()?.let { receiver -> listOf<Parameter>(ReceiverParameterAdapter(module, receiver, this)) } ?: emptyList()) - + node.details(NodeKind.Parameter).map { ParameterAdapter(module, it) } - ).toTypedArray() - - override fun typeParameters(): Array<out TypeVariable> = node.details(NodeKind.TypeParameter).map { TypeVariableAdapter(module, it) }.toTypedArray() - - override fun typeParamTags(): Array<out ParamTag> = - collectParamTags(NodeKind.TypeParameter, sectionFilter = { it.subjectName in typeParameters().map { it.simpleTypeName() } }) - - private fun receiverNode() = node.details(NodeKind.Receiver).let { receivers -> - when { - receivers.isNotEmpty() -> receivers.single().detail(NodeKind.Type) - else -> null - } - } -} - -class ConstructorAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : ExecutableMemberAdapter(module, node), ConstructorDoc { - override fun name(): String = node.owner?.name ?: throw IllegalStateException("No owner for $node") - - override fun containingClass(): ClassDoc? { - return super.containingClass() - } -} - -class MethodAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : ExecutableMemberAdapter(module, node), MethodDoc { - override fun overrides(meth: MethodDoc?): Boolean = false // TODO - - override fun overriddenType(): Type? = node.overrides.firstOrNull()?.owner?.let { owner -> TypeAdapter(module, owner) } - - override fun overriddenMethod(): MethodDoc? = node.overrides.map { MethodAdapter(module, it) }.firstOrNull() - override fun overriddenClass(): ClassDoc? = overriddenMethod()?.containingClass() - - override fun isAbstract(): Boolean = false // TODO - - override fun isDefault(): Boolean = false - - override fun returnType(): Type = TypeAdapter(module, node.detail(NodeKind.Type)) - - override fun tags(tagname: String?) = super.tags(tagname) - - override fun tags(): Array<out Tag> { - val tags = super.tags().toMutableList() - node.content.findSectionByTag(ContentTags.Return)?.let { - tags += ReturnTagAdapter(module, this, it.children) - } - - return tags.toTypedArray() - } -} - -class FieldAdapter(module: ModuleNodeAdapter, node: DocumentationNode) : ProgramElementAdapter(module, node), FieldDoc { - override fun isSynthetic(): Boolean = false - - override fun constantValueExpression(): String? = node.detailOrNull(NodeKind.Value)?.let { it.name } - override fun constantValue(): Any? = constantValueExpression() - - override fun type(): Type = TypeAdapter(module, node.detail(NodeKind.Type)) - override fun isTransient(): Boolean = node.hasAnnotation(Transient::class) - override fun serialFieldTags(): Array<out SerialFieldTag> = emptyArray() - - override fun isVolatile(): Boolean = node.hasAnnotation(Volatile::class) -} -open class ClassDocumentationNodeAdapter(module: ModuleNodeAdapter, val classNode: DocumentationNode) - : ProgramElementAdapter(module, classNode), - Type by TypeAdapter(module, classNode), - ClassDoc, - AnnotationTypeDoc { - - override fun elements(): Array<out AnnotationTypeElementDoc>? = emptyArray() // TODO - - override fun name(): String { - val parent = classNode.owner - if (parent?.kind in NodeKind.classLike) { - return parent!!.name + "." + classNode.name - } - return classNode.simpleName() - } - - override fun qualifiedName(): String? { - return super.qualifiedName() - } - override fun constructors(filter: Boolean): Array<out ConstructorDoc> = classNode.members(NodeKind.Constructor).map { ConstructorAdapter(module, it) }.toTypedArray() - override fun constructors(): Array<out ConstructorDoc> = constructors(true) - override fun importedPackages(): Array<out PackageDoc> = emptyArray() - override fun importedClasses(): Array<out ClassDoc>? = emptyArray() - override fun typeParameters(): Array<out TypeVariable> = classNode.details(NodeKind.TypeParameter).map { TypeVariableAdapter(module, it) }.toTypedArray() - override fun asTypeVariable(): TypeVariable? = if (classNode.kind == NodeKind.Class) TypeVariableAdapter(module, classNode) else null - override fun isExternalizable(): Boolean = interfaces().any { it.qualifiedName() == "java.io.Externalizable" } - override fun definesSerializableFields(): Boolean = false - override fun methods(filter: Boolean): Array<out MethodDoc> = classNode.members(NodeKind.Function).map { MethodAdapter(module, it) }.toTypedArray() // TODO include get/set methods - override fun methods(): Array<out MethodDoc> = methods(true) - override fun enumConstants(): Array<out FieldDoc>? = classNode.members(NodeKind.EnumItem).map { FieldAdapter(module, it) }.toTypedArray() - override fun isAbstract(): Boolean = classNode.details(NodeKind.Modifier).any { it.name == "abstract" } - override fun interfaceTypes(): Array<out Type> = classNode.lookupSuperClasses(module) - .filter { it.kind == NodeKind.Interface } - .map { ClassDocumentationNodeAdapter(module, it) } - .toTypedArray() - - override fun interfaces(): Array<out ClassDoc> = classNode.lookupSuperClasses(module) - .filter { it.kind == NodeKind.Interface } - .map { ClassDocumentationNodeAdapter(module, it) } - .toTypedArray() - - override fun typeParamTags(): Array<out ParamTag> = - collectParamTags(NodeKind.TypeParameter, sectionFilter = { it.subjectName in typeParameters().map { it.simpleTypeName() } }) - - override fun fields(): Array<out FieldDoc> = fields(true) - override fun fields(filter: Boolean): Array<out FieldDoc> = classNode.members(NodeKind.Field).map { FieldAdapter(module, it) }.toTypedArray() - - override fun findClass(className: String?): ClassDoc? = null // TODO !!! - override fun serializableFields(): Array<out FieldDoc> = emptyArray() - override fun superclassType(): Type? = classNode.lookupSuperClasses(module).singleOrNull { it.kind == NodeKind.Class }?.let { ClassDocumentationNodeAdapter(module, it) } - override fun serializationMethods(): Array<out MethodDoc> = emptyArray() // TODO - override fun superclass(): ClassDoc? = classNode.lookupSuperClasses(module).singleOrNull { it.kind == NodeKind.Class }?.let { ClassDocumentationNodeAdapter(module, it) } - override fun isSerializable(): Boolean = false // TODO - override fun subclassOf(cd: ClassDoc?): Boolean { - if (cd == null) { - return false - } - - val expectedFQName = cd.qualifiedName() - val types = arrayListOf(classNode) - val visitedTypes = HashSet<String>() - - while (types.isNotEmpty()) { - val type = types.removeAt(types.lastIndex) - val fqName = type.qualifiedName() - - if (expectedFQName == fqName) { - return true - } - - visitedTypes.add(fqName) - types.addAll(type.details(NodeKind.Supertype).filter { it.qualifiedName() !in visitedTypes }) - } - - return false - } - - override fun innerClasses(): Array<out ClassDoc> = classNode.members(NodeKind.Class).map { ClassDocumentationNodeAdapter(module, it) }.toTypedArray() - override fun innerClasses(filter: Boolean): Array<out ClassDoc> = innerClasses() -} - -fun DocumentationNode.lookupSuperClasses(module: ModuleNodeAdapter) = - details(NodeKind.Supertype) - .map { it.links.firstOrNull() }.mapNotNull { module.allTypes[it?.qualifiedName()] } - -fun List<DocumentationNode>.collectAllTypesRecursively(): Map<String, DocumentationNode> { - val result = hashMapOf<String, DocumentationNode>() - - fun DocumentationNode.collectTypesRecursively() { - val classLikeMembers = NodeKind.classLike.flatMap { members(it) } - classLikeMembers.forEach { - result.put(it.qualifiedName(), it) - it.collectTypesRecursively() - } - } - - forEach { it.collectTypesRecursively() } - return result -} - -class ModuleNodeAdapter(val module: DocumentationModule, val reporter: DocErrorReporter, val outputPath: String) : DocumentationNodeBareAdapter(module), DocErrorReporter by reporter, RootDoc { - val allPackages = module.members(NodeKind.Package).associateBy { it.name } - val allTypes = module.members(NodeKind.Package).collectAllTypesRecursively() - - override fun packageNamed(name: String?): PackageDoc? = allPackages[name]?.let { PackageAdapter(this, it) } - - override fun classes(): Array<out ClassDoc> = - allTypes.values.map { ClassDocumentationNodeAdapter(this, it) }.toTypedArray() - - override fun options(): Array<out Array<String>> = arrayOf( - arrayOf("-d", outputPath), - arrayOf("-docencoding", "UTF-8"), - arrayOf("-charset", "UTF-8"), - arrayOf("-keywords") - ) - - override fun specifiedPackages(): Array<out PackageDoc>? = module.members(NodeKind.Package).map { PackageAdapter(this, it) }.toTypedArray() - - override fun classNamed(qualifiedName: String?): ClassDoc? = - allTypes[qualifiedName]?.let { ClassDocumentationNodeAdapter(this, it) } - - override fun specifiedClasses(): Array<out ClassDoc> = classes() -} - -private fun DocumentationNodeAdapter.collectParamTags(kind: NodeKind, sectionFilter: (ContentSection) -> Boolean) = - (node.details(kind) - .filter(DocumentationNode::hasNonEmptyContent) - .map { ParamTagAdapter(module, this, it.name, true, it.content.children) } - - + node.content.sections - .filter(sectionFilter) - .map { - ParamTagAdapter(module, this, it.subjectName ?: "?", true, - it.children.filterNot { contentNode -> contentNode is LazyContentBlock } - ) - } - ) - .distinctBy { it.parameterName } - .toTypedArray()
\ No newline at end of file diff --git a/core/src/main/kotlin/javadoc/dokka-adapters.kt b/core/src/main/kotlin/javadoc/dokka-adapters.kt deleted file mode 100644 index 1329876a..00000000 --- a/core/src/main/kotlin/javadoc/dokka-adapters.kt +++ /dev/null @@ -1,42 +0,0 @@ -package org.jetbrains.dokka.javadoc - -import com.google.inject.Binder -import com.google.inject.Inject -import com.sun.tools.doclets.formats.html.HtmlDoclet -import org.jetbrains.dokka.* -import org.jetbrains.dokka.Formats.DefaultAnalysisComponent -import org.jetbrains.dokka.Formats.DefaultAnalysisComponentServices -import org.jetbrains.dokka.Formats.FormatDescriptor -import org.jetbrains.dokka.Formats.KotlinAsJava -import org.jetbrains.dokka.Utilities.bind -import org.jetbrains.dokka.Utilities.toType - -class JavadocGenerator @Inject constructor(val configuration: DokkaConfiguration, val logger: DokkaLogger) : Generator { - - override fun buildPages(nodes: Iterable<DocumentationNode>) { - val module = nodes.single() as DocumentationModule - - HtmlDoclet.start(ModuleNodeAdapter(module, StandardReporter(logger), configuration.outputDir)) - } - - override fun buildOutlines(nodes: Iterable<DocumentationNode>) { - // no outline could be generated separately - } - - override fun buildSupportFiles() { - } - - override fun buildPackageList(nodes: Iterable<DocumentationNode>) { - // handled by javadoc itself - } -} - -class JavadocFormatDescriptor : - FormatDescriptor, - DefaultAnalysisComponent, - DefaultAnalysisComponentServices by KotlinAsJava { - - override fun configureOutput(binder: Binder): Unit = with(binder) { - bind<Generator>() toType JavadocGenerator::class - } -} diff --git a/core/src/main/kotlin/javadoc/reporter.kt b/core/src/main/kotlin/javadoc/reporter.kt deleted file mode 100644 index fc38368c..00000000 --- a/core/src/main/kotlin/javadoc/reporter.kt +++ /dev/null @@ -1,34 +0,0 @@ -package org.jetbrains.dokka.javadoc - -import com.sun.javadoc.DocErrorReporter -import com.sun.javadoc.SourcePosition -import org.jetbrains.dokka.DokkaLogger - -class StandardReporter(val logger: DokkaLogger) : DocErrorReporter { - override fun printWarning(msg: String?) { - logger.warn(msg.toString()) - } - - override fun printWarning(pos: SourcePosition?, msg: String?) { - logger.warn(format(pos, msg)) - } - - override fun printError(msg: String?) { - logger.error(msg.toString()) - } - - override fun printError(pos: SourcePosition?, msg: String?) { - logger.error(format(pos, msg)) - } - - override fun printNotice(msg: String?) { - logger.info(msg.toString()) - } - - override fun printNotice(pos: SourcePosition?, msg: String?) { - logger.info(format(pos, msg)) - } - - private fun format(pos: SourcePosition?, msg: String?) = - if (pos == null) msg.toString() else "${pos.file()}:${pos.line()}:${pos.column()}: $msg" -}
\ No newline at end of file diff --git a/core/src/main/kotlin/javadoc/source-position.kt b/core/src/main/kotlin/javadoc/source-position.kt deleted file mode 100644 index 6125f968..00000000 --- a/core/src/main/kotlin/javadoc/source-position.kt +++ /dev/null @@ -1,19 +0,0 @@ -package org.jetbrains.dokka.javadoc - -import com.sun.javadoc.SourcePosition -import org.jetbrains.dokka.DocumentationNode -import org.jetbrains.dokka.NodeKind -import java.io.File - -class SourcePositionAdapter(val docNode: DocumentationNode) : SourcePosition { - - private val sourcePositionParts: List<String> by lazy { - docNode.details(NodeKind.SourcePosition).firstOrNull()?.name?.split(":") ?: emptyList() - } - - override fun file(): File? = if (sourcePositionParts.isEmpty()) null else File(sourcePositionParts[0]) - - override fun line(): Int = sourcePositionParts.getOrNull(1)?.toInt() ?: -1 - - override fun column(): Int = sourcePositionParts.getOrNull(2)?.toInt() ?: -1 -} diff --git a/core/src/main/kotlin/javadoc/tags.kt b/core/src/main/kotlin/javadoc/tags.kt deleted file mode 100644 index 99c9bfff..00000000 --- a/core/src/main/kotlin/javadoc/tags.kt +++ /dev/null @@ -1,228 +0,0 @@ -package org.jetbrains.dokka.javadoc - -import com.sun.javadoc.* -import org.jetbrains.dokka.* -import java.util.* - -class TagImpl(val holder: Doc, val name: String, val text: String): Tag { - override fun text(): String? = text - - override fun holder(): Doc = holder - override fun firstSentenceTags(): Array<out Tag>? = arrayOf() - override fun inlineTags(): Array<out Tag>? = arrayOf() - - override fun name(): String = name - override fun kind(): String = name - - override fun position(): SourcePosition = holder.position() -} - -class TextTag(val holder: Doc, val content: ContentText) : Tag { - val plainText: String - get() = content.text - - override fun name(): String = "Text" - override fun kind(): String = name() - override fun text(): String? = plainText - override fun inlineTags(): Array<out Tag> = arrayOf(this) - override fun holder(): Doc = holder - override fun firstSentenceTags(): Array<out Tag> = arrayOf(this) - override fun position(): SourcePosition = holder.position() -} - -abstract class SeeTagAdapter(val holder: Doc, val content: ContentNodeLink) : SeeTag { - override fun position(): SourcePosition? = holder.position() - override fun name(): String = "@see" - override fun kind(): String = "@see" - override fun holder(): Doc = holder - - override fun text(): String? = content.node?.name ?: "(?)" -} - -class SeeExternalLinkTagAdapter(val holder: Doc, val link: ContentExternalLink) : SeeTag { - override fun position(): SourcePosition = holder.position() - override fun text(): String = label() - override fun inlineTags(): Array<out Tag> = emptyArray() // TODO - - override fun label(): String { - val label = link.asText() ?: link.href - return "<a href=\"${link.href}\">$label</a>" - } - - override fun referencedPackage(): PackageDoc? = null - override fun referencedClass(): ClassDoc? = null - override fun referencedMemberName(): String? = null - override fun referencedClassName(): String? = null - override fun referencedMember(): MemberDoc? = null - override fun holder(): Doc = holder - override fun firstSentenceTags(): Array<out Tag> = inlineTags() - override fun name(): String = "@link" - override fun kind(): String = "@see" -} - -fun ContentBlock.asText(): String? { - val contentText = children.singleOrNull() as? ContentText - return contentText?.text -} - -class SeeMethodTagAdapter(holder: Doc, val method: MethodAdapter, content: ContentNodeLink) : SeeTagAdapter(holder, content) { - override fun referencedMember(): MemberDoc = method - override fun referencedMemberName(): String = method.name() - override fun referencedPackage(): PackageDoc? = null - override fun referencedClass(): ClassDoc? = method.containingClass() - override fun referencedClassName(): String = method.containingClass()?.name() ?: "" - override fun label(): String = content.text ?: "${method.containingClass()?.name()}.${method.name()}" - - override fun inlineTags(): Array<out Tag> = emptyArray() // TODO - override fun firstSentenceTags(): Array<out Tag> = inlineTags() // TODO -} - -class SeeClassTagAdapter(holder: Doc, val clazz: ClassDocumentationNodeAdapter, content: ContentNodeLink) : SeeTagAdapter(holder, content) { - override fun referencedMember(): MemberDoc? = null - override fun referencedMemberName(): String? = null - override fun referencedPackage(): PackageDoc? = null - override fun referencedClass(): ClassDoc = clazz - override fun referencedClassName(): String = clazz.name() - override fun label(): String = "${clazz.classNode.kind.name.toLowerCase()} ${clazz.name()}" - - override fun inlineTags(): Array<out Tag> = emptyArray() // TODO - override fun firstSentenceTags(): Array<out Tag> = inlineTags() // TODO -} - -class ParamTagAdapter(val module: ModuleNodeAdapter, - val holder: Doc, - val parameterName: String, - val typeParameter: Boolean, - val content: List<ContentNode>) : ParamTag { - - constructor(module: ModuleNodeAdapter, holder: Doc, parameterName: String, isTypeParameter: Boolean, content: ContentNode) - : this(module, holder, parameterName, isTypeParameter, listOf(content)) { - } - - override fun name(): String = "@param" - override fun kind(): String = name() - override fun holder(): Doc = holder - override fun position(): SourcePosition? = holder.position() - - override fun text(): String = "@param $parameterName ${parameterComment()}" // Seems has no effect, so used for debug - override fun inlineTags(): Array<out Tag> = buildInlineTags(module, holder, content).toTypedArray() - override fun firstSentenceTags(): Array<out Tag> = arrayOf(TextTag(holder, ContentText(text()))) - - override fun isTypeParameter(): Boolean = typeParameter - override fun parameterComment(): String = content.toString() // TODO - override fun parameterName(): String = parameterName -} - - -class ThrowsTagAdapter(val holder: Doc, val type: ClassDocumentationNodeAdapter, val content: List<ContentNode>) : ThrowsTag { - override fun name(): String = "@throws" - override fun kind(): String = name() - override fun holder(): Doc = holder - override fun position(): SourcePosition? = holder.position() - - override fun text(): String = "${name()} ${exceptionName()} ${exceptionComment()}" - override fun inlineTags(): Array<out Tag> = buildInlineTags(type.module, holder, content).toTypedArray() - override fun firstSentenceTags(): Array<out Tag> = emptyArray() - - override fun exceptionComment(): String = content.toString() - override fun exceptionType(): Type = type - override fun exception(): ClassDoc = type - override fun exceptionName(): String = type.qualifiedTypeName() -} - -class ReturnTagAdapter(val module: ModuleNodeAdapter, val holder: Doc, val content: List<ContentNode>) : Tag { - override fun name(): String = "@return" - override fun kind() = name() - override fun holder() = holder - override fun position(): SourcePosition? = holder.position() - - override fun text(): String = "@return $content" // Seems has no effect, so used for debug - override fun inlineTags(): Array<Tag> = buildInlineTags(module, holder, content).toTypedArray() - override fun firstSentenceTags(): Array<Tag> = inlineTags() -} - -fun buildInlineTags(module: ModuleNodeAdapter, holder: Doc, tags: List<ContentNode>): List<Tag> = ArrayList<Tag>().apply { tags.forEach { buildInlineTags(module, holder, it, this) } } - -fun buildInlineTags(module: ModuleNodeAdapter, holder: Doc, root: ContentNode): List<Tag> = ArrayList<Tag>().apply { buildInlineTags(module, holder, root, this) } - -private fun buildInlineTags(module: ModuleNodeAdapter, holder: Doc, nodes: List<ContentNode>, result: MutableList<Tag>) { - nodes.forEach { - buildInlineTags(module, holder, it, result) - } -} - - -private fun buildInlineTags(module: ModuleNodeAdapter, holder: Doc, node: ContentNode, result: MutableList<Tag>) { - fun surroundWith(module: ModuleNodeAdapter, holder: Doc, prefix: String, postfix: String, node: ContentBlock, result: MutableList<Tag>) { - if (node.children.isNotEmpty()) { - val open = TextTag(holder, ContentText(prefix)) - val close = TextTag(holder, ContentText(postfix)) - - result.add(open) - buildInlineTags(module, holder, node.children, result) - - if (result.last() === open) { - result.removeAt(result.lastIndex) - } else { - result.add(close) - } - } - } - - fun surroundWith(module: ModuleNodeAdapter, holder: Doc, prefix: String, postfix: String, node: ContentNode, result: MutableList<Tag>) { - if (node !is ContentEmpty) { - val open = TextTag(holder, ContentText(prefix)) - val close = TextTag(holder, ContentText(postfix)) - - result.add(open) - buildInlineTags(module, holder, node, result) - if (result.last() === open) { - result.removeAt(result.lastIndex) - } else { - result.add(close) - } - } - } - - when (node) { - is ContentText -> result.add(TextTag(holder, node)) - is ContentNodeLink -> { - val target = node.node - when (target?.kind) { - NodeKind.Function -> result.add(SeeMethodTagAdapter(holder, MethodAdapter(module, node.node!!), node)) - - in NodeKind.classLike -> result.add(SeeClassTagAdapter(holder, ClassDocumentationNodeAdapter(module, node.node!!), node)) - - else -> buildInlineTags(module, holder, node.children, result) - } - } - is ContentExternalLink -> result.add(SeeExternalLinkTagAdapter(holder, node)) - is ContentCode -> surroundWith(module, holder, "<code>", "</code>", node, result) - is ContentBlockCode -> surroundWith(module, holder, "<code><pre>", "</pre></code>", node, result) - is ContentEmpty -> {} - is ContentEmphasis -> surroundWith(module, holder, "<em>", "</em>", node, result) - is ContentHeading -> surroundWith(module, holder, "<h${node.level}>", "</h${node.level}>", node, result) - is ContentEntity -> result.add(TextTag(holder, ContentText(node.text))) // TODO ?? - is ContentIdentifier -> result.add(TextTag(holder, ContentText(node.text))) // TODO - is ContentKeyword -> result.add(TextTag(holder, ContentText(node.text))) // TODO - is ContentListItem -> surroundWith(module, holder, "<li>", "</li>", node, result) - is ContentOrderedList -> surroundWith(module, holder, "<ol>", "</ol>", node, result) - is ContentUnorderedList -> surroundWith(module, holder, "<ul>", "</ul>", node, result) - is ContentParagraph -> surroundWith(module, holder, "<p>", "</p>", node, result) - is ContentSection -> surroundWith(module, holder, "<p>", "</p>", node, result) // TODO how section should be represented? - is ContentNonBreakingSpace -> result.add(TextTag(holder, ContentText(" "))) - is ContentStrikethrough -> surroundWith(module, holder, "<strike>", "</strike>", node, result) - is ContentStrong -> surroundWith(module, holder, "<strong>", "</strong>", node, result) - is ContentSymbol -> result.add(TextTag(holder, ContentText(node.text))) // TODO? - is Content -> { - surroundWith(module, holder, "<p>", "</p>", node.summary, result) - surroundWith(module, holder, "<p>", "</p>", node.description, result) - } - is ContentBlock -> { - surroundWith(module, holder, "", "", node, result) - } - is ContentHardLineBreak -> result.add(TextTag(holder, ContentText("<br/>"))) - - else -> result.add(TextTag(holder, ContentText("$node"))) - } -}
\ No newline at end of file diff --git a/core/src/main/kotlin/links/DRI.kt b/core/src/main/kotlin/links/DRI.kt new file mode 100644 index 00000000..4a555e71 --- /dev/null +++ b/core/src/main/kotlin/links/DRI.kt @@ -0,0 +1,105 @@ +package org.jetbrains.dokka.links + +import org.jetbrains.dokka.model.ClassKind + +/** + * [DRI] stands for DokkaResourceIdentifier + */ +data class DRI( + val packageName: String? = null, + val classNames: String? = null, + val callable: Callable? = null, + val target: DriTarget = PointingToDeclaration, + val extra: String? = null +) { + override fun toString(): String = + "${packageName.orEmpty()}/${classNames.orEmpty()}/${callable?.name.orEmpty()}/${callable?.signature() + .orEmpty()}/$target/${extra.orEmpty()}" + + companion object { + val topLevel = DRI() + } +} + +val DriOfUnit = DRI("kotlin", "Unit") +val DriOfAny = DRI("kotlin", "Any") + +fun DRI.withClass(name: String) = copy(classNames = if (classNames.isNullOrBlank()) name else "$classNames.$name") + +fun DRI.withTargetToDeclaration() = copy(target = PointingToDeclaration) + +val DRI.parent: DRI + get() = when { + extra != null -> copy(extra = null) + target != PointingToDeclaration -> copy(target = PointingToDeclaration) + callable != null -> copy(callable = null) + classNames != null -> copy(classNames = classNames.substringBeforeLast(".", "").takeIf { it.isNotBlank() }) + else -> DRI.topLevel + } + +val DRI.sureClassNames + get() = classNames ?: throw IllegalStateException("Malformed DRI. It requires classNames in this context.") + +data class Callable( + val name: String, + val receiver: TypeReference? = null, + val params: List<TypeReference> +) { + fun signature() = "${receiver?.toString().orEmpty()}#${params.joinToString("#")}" + + companion object +} + +sealed class TypeReference { + companion object +} + +data class JavaClassReference(val name: String) : TypeReference() { + override fun toString(): String = name +} + +data class TypeParam(val bounds: List<TypeReference>) : TypeReference() + +data class TypeConstructor( + val fullyQualifiedName: String, + val params: List<TypeReference> +) : TypeReference() { + override fun toString() = fullyQualifiedName + + (if (params.isNotEmpty()) "[${params.joinToString(",")}]" else "") +} + +object SelfType : TypeReference() { + override fun toString() = "^" +} + +data class Nullable(val wrapped: TypeReference) : TypeReference() { + override fun toString() = "$wrapped?" +} + +object StarProjection : TypeReference() { + override fun toString() = "*" +} + +sealed class DriTarget { + override fun toString(): String = this.javaClass.simpleName + + companion object +} + +data class PointingToGenericParameters(val parameterIndex: Int) : DriTarget() { + override fun toString(): String = "PointingToGenericParameters($parameterIndex)" +} + +object PointingToDeclaration : DriTarget() + +data class PointingToCallableParameters(val parameterIndex: Int) : DriTarget() { + override fun toString(): String = "PointingToCallableParameters($parameterIndex)" +} + +fun DriTarget.nextTarget(): DriTarget = when (this) { + is PointingToGenericParameters -> PointingToGenericParameters(this.parameterIndex + 1) + is PointingToCallableParameters -> PointingToCallableParameters(this.parameterIndex + 1) + else -> this +} + +data class DriWithKind(val dri: DRI, val kind: ClassKind) diff --git a/core/src/main/kotlin/model/Documentable.kt b/core/src/main/kotlin/model/Documentable.kt new file mode 100644 index 00000000..2c3e1323 --- /dev/null +++ b/core/src/main/kotlin/model/Documentable.kt @@ -0,0 +1,398 @@ +package org.jetbrains.dokka.model + +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.links.DriWithKind +import org.jetbrains.dokka.model.doc.DocumentationNode +import org.jetbrains.dokka.model.properties.PropertyContainer +import org.jetbrains.dokka.model.properties.WithExtraProperties + + +abstract class Documentable : WithChildren<Documentable> { + abstract val name: String? + abstract val dri: DRI + abstract val documentation: SourceSetDependent<DocumentationNode> + abstract val sourceSets: Set<DokkaSourceSet> + abstract val expectPresentInSet: DokkaSourceSet? + + override fun toString(): String = + "${javaClass.simpleName}($dri)" + + override fun equals(other: Any?) = + other is Documentable && this.dri == other.dri // TODO: https://github.com/Kotlin/dokka/pull/667#discussion_r382555806 + + override fun hashCode() = dri.hashCode() +} + +typealias SourceSetDependent<T> = Map<DokkaSourceSet, T> + +interface WithExpectActual { + val sources: SourceSetDependent<DocumentableSource> +} + +interface WithScope { + val functions: List<DFunction> + val properties: List<DProperty> + val classlikes: List<DClasslike> +} + +interface WithVisibility { + val visibility: SourceSetDependent<Visibility> +} + +interface WithType { + val type: Bound +} + +interface WithAbstraction { + val modifier: SourceSetDependent<Modifier> +} + +sealed class Modifier(val name: String) +sealed class KotlinModifier(name: String) : Modifier(name) { + object Abstract : KotlinModifier("abstract") + object Open : KotlinModifier("open") + object Final : KotlinModifier("final") + object Sealed : KotlinModifier("sealed") + object Empty : KotlinModifier("") +} + +sealed class JavaModifier(name: String) : Modifier(name) { + object Abstract : JavaModifier("abstract") + object Final : JavaModifier("final") + object Empty : JavaModifier("") +} + +interface WithCompanion { + val companion: DObject? +} + +interface WithConstructors { + val constructors: List<DFunction> +} + +interface WithGenerics { + val generics: List<DTypeParameter> +} + +interface WithSupertypes { + val supertypes: SourceSetDependent<List<DriWithKind>> +} + +interface Callable : WithVisibility, WithType, WithAbstraction, WithExpectActual { + val receiver: DParameter? +} + +sealed class DClasslike : Documentable(), WithScope, WithVisibility, WithExpectActual + +data class DModule( + override val name: String, + val packages: List<DPackage>, + override val documentation: SourceSetDependent<DocumentationNode>, + override val expectPresentInSet: DokkaSourceSet? = null, + override val sourceSets: Set<DokkaSourceSet>, + override val extra: PropertyContainer<DModule> = PropertyContainer.empty() +) : Documentable(), WithExtraProperties<DModule> { + override val dri: DRI = DRI.topLevel + override val children: List<Documentable> + get() = packages + + override fun withNewExtras(newExtras: PropertyContainer<DModule>) = copy(extra = newExtras) +} + +data class DPackage( + override val dri: DRI, + override val functions: List<DFunction>, + override val properties: List<DProperty>, + override val classlikes: List<DClasslike>, + val typealiases: List<DTypeAlias>, + override val documentation: SourceSetDependent<DocumentationNode>, + override val expectPresentInSet: DokkaSourceSet? = null, + override val sourceSets: Set<DokkaSourceSet>, + override val extra: PropertyContainer<DPackage> = PropertyContainer.empty() +) : Documentable(), WithScope, WithExtraProperties<DPackage> { + override val name = dri.packageName.orEmpty() + override val children: List<Documentable> + get() = (properties + functions + classlikes) + + override fun withNewExtras(newExtras: PropertyContainer<DPackage>) = copy(extra = newExtras) +} + +data class DClass( + override val dri: DRI, + override val name: String, + override val constructors: List<DFunction>, + override val functions: List<DFunction>, + override val properties: List<DProperty>, + override val classlikes: List<DClasslike>, + override val sources: SourceSetDependent<DocumentableSource>, + override val visibility: SourceSetDependent<Visibility>, + override val companion: DObject?, + override val generics: List<DTypeParameter>, + override val supertypes: SourceSetDependent<List<DriWithKind>>, + override val documentation: SourceSetDependent<DocumentationNode>, + override val expectPresentInSet: DokkaSourceSet?, + override val modifier: SourceSetDependent<Modifier>, + override val sourceSets: Set<DokkaSourceSet>, + override val extra: PropertyContainer<DClass> = PropertyContainer.empty() +) : DClasslike(), WithAbstraction, WithCompanion, WithConstructors, WithGenerics, WithSupertypes, + WithExtraProperties<DClass> { + + override val children: List<Documentable> + get() = (functions + properties + classlikes + constructors) + + override fun withNewExtras(newExtras: PropertyContainer<DClass>) = copy(extra = newExtras) +} + +data class DEnum( + override val dri: DRI, + override val name: String, + val entries: List<DEnumEntry>, + override val documentation: SourceSetDependent<DocumentationNode>, + override val expectPresentInSet: DokkaSourceSet?, + override val sources: SourceSetDependent<DocumentableSource>, + override val functions: List<DFunction>, + override val properties: List<DProperty>, + override val classlikes: List<DClasslike>, + override val visibility: SourceSetDependent<Visibility>, + override val companion: DObject?, + override val constructors: List<DFunction>, + override val supertypes: SourceSetDependent<List<DriWithKind>>, + override val sourceSets: Set<DokkaSourceSet>, + override val extra: PropertyContainer<DEnum> = PropertyContainer.empty() +) : DClasslike(), WithCompanion, WithConstructors, WithSupertypes, WithExtraProperties<DEnum> { + override val children: List<Documentable> + get() = (entries + functions + properties + classlikes + constructors) + + override fun withNewExtras(newExtras: PropertyContainer<DEnum>) = copy(extra = newExtras) +} + +data class DEnumEntry( + override val dri: DRI, + override val name: String, + override val documentation: SourceSetDependent<DocumentationNode>, + override val expectPresentInSet: DokkaSourceSet?, + override val functions: List<DFunction>, + override val properties: List<DProperty>, + override val classlikes: List<DClasslike>, + override val sourceSets: Set<DokkaSourceSet>, + override val extra: PropertyContainer<DEnumEntry> = PropertyContainer.empty() +) : Documentable(), WithScope, WithExtraProperties<DEnumEntry> { + override val children: List<Documentable> + get() = (functions + properties + classlikes) + + override fun withNewExtras(newExtras: PropertyContainer<DEnumEntry>) = copy(extra = newExtras) +} + +data class DFunction( + override val dri: DRI, + override val name: String, + val isConstructor: Boolean, + val parameters: List<DParameter>, + override val documentation: SourceSetDependent<DocumentationNode>, + override val expectPresentInSet: DokkaSourceSet?, + override val sources: SourceSetDependent<DocumentableSource>, + override val visibility: SourceSetDependent<Visibility>, + override val type: Bound, + override val generics: List<DTypeParameter>, + override val receiver: DParameter?, + override val modifier: SourceSetDependent<Modifier>, + override val sourceSets: Set<DokkaSourceSet>, + override val extra: PropertyContainer<DFunction> = PropertyContainer.empty() +) : Documentable(), Callable, WithGenerics, WithExtraProperties<DFunction> { + override val children: List<Documentable> + get() = parameters + + override fun withNewExtras(newExtras: PropertyContainer<DFunction>) = copy(extra = newExtras) +} + +data class DInterface( + override val dri: DRI, + override val name: String, + override val documentation: SourceSetDependent<DocumentationNode>, + override val expectPresentInSet: DokkaSourceSet?, + override val sources: SourceSetDependent<DocumentableSource>, + override val functions: List<DFunction>, + override val properties: List<DProperty>, + override val classlikes: List<DClasslike>, + override val visibility: SourceSetDependent<Visibility>, + override val companion: DObject?, + override val generics: List<DTypeParameter>, + override val supertypes: SourceSetDependent<List<DriWithKind>>, + override val sourceSets: Set<DokkaSourceSet>, + override val extra: PropertyContainer<DInterface> = PropertyContainer.empty() +) : DClasslike(), WithCompanion, WithGenerics, WithSupertypes, WithExtraProperties<DInterface> { + override val children: List<Documentable> + get() = (functions + properties + classlikes) + + override fun withNewExtras(newExtras: PropertyContainer<DInterface>) = copy(extra = newExtras) +} + +data class DObject( + override val name: String?, + override val dri: DRI, + override val documentation: SourceSetDependent<DocumentationNode>, + override val expectPresentInSet: DokkaSourceSet?, + override val sources: SourceSetDependent<DocumentableSource>, + override val functions: List<DFunction>, + override val properties: List<DProperty>, + override val classlikes: List<DClasslike>, + override val visibility: SourceSetDependent<Visibility>, + override val supertypes: SourceSetDependent<List<DriWithKind>>, + override val sourceSets: Set<DokkaSourceSet>, + override val extra: PropertyContainer<DObject> = PropertyContainer.empty() +) : DClasslike(), WithSupertypes, WithExtraProperties<DObject> { + override val children: List<Documentable> + get() = (functions + properties + classlikes) as List<Documentable> + + override fun withNewExtras(newExtras: PropertyContainer<DObject>) = copy(extra = newExtras) +} + +data class DAnnotation( + override val name: String, + override val dri: DRI, + override val documentation: SourceSetDependent<DocumentationNode>, + override val expectPresentInSet: DokkaSourceSet?, + override val sources: SourceSetDependent<DocumentableSource>, + override val functions: List<DFunction>, + override val properties: List<DProperty>, + override val classlikes: List<DClasslike>, + override val visibility: SourceSetDependent<Visibility>, + override val companion: DObject?, + override val constructors: List<DFunction>, + override val generics: List<DTypeParameter>, + override val sourceSets: Set<DokkaSourceSet>, + override val extra: PropertyContainer<DAnnotation> = PropertyContainer.empty() +) : DClasslike(), WithCompanion, WithConstructors, WithExtraProperties<DAnnotation>, WithGenerics { + override val children: List<Documentable> + get() = (functions + properties + classlikes + constructors) + + override fun withNewExtras(newExtras: PropertyContainer<DAnnotation>) = copy(extra = newExtras) +} + +data class DProperty( + override val dri: DRI, + override val name: String, + override val documentation: SourceSetDependent<DocumentationNode>, + override val expectPresentInSet: DokkaSourceSet?, + override val sources: SourceSetDependent<DocumentableSource>, + override val visibility: SourceSetDependent<Visibility>, + override val type: Bound, + override val receiver: DParameter?, + val setter: DFunction?, + val getter: DFunction?, + override val modifier: SourceSetDependent<Modifier>, + override val sourceSets: Set<DokkaSourceSet>, + override val generics: List<DTypeParameter>, + override val extra: PropertyContainer<DProperty> = PropertyContainer.empty() +) : Documentable(), Callable, WithExtraProperties<DProperty>, WithGenerics { + override val children: List<Nothing> + get() = emptyList() + + override fun withNewExtras(newExtras: PropertyContainer<DProperty>) = copy(extra = newExtras) +} + +// TODO: treat named Parameters and receivers differently +data class DParameter( + override val dri: DRI, + override val name: String?, + override val documentation: SourceSetDependent<DocumentationNode>, + override val expectPresentInSet: DokkaSourceSet?, + val type: Bound, + override val sourceSets: Set<DokkaSourceSet>, + override val extra: PropertyContainer<DParameter> = PropertyContainer.empty() +) : Documentable(), WithExtraProperties<DParameter> { + override val children: List<Nothing> + get() = emptyList() + + override fun withNewExtras(newExtras: PropertyContainer<DParameter>) = copy(extra = newExtras) +} + +data class DTypeParameter( + override val dri: DRI, + override val name: String, + override val documentation: SourceSetDependent<DocumentationNode>, + override val expectPresentInSet: DokkaSourceSet?, + val bounds: List<Bound>, + override val sourceSets: Set<DokkaSourceSet>, + override val extra: PropertyContainer<DTypeParameter> = PropertyContainer.empty() +) : Documentable(), WithExtraProperties<DTypeParameter> { + override val children: List<Nothing> + get() = emptyList() + + override fun withNewExtras(newExtras: PropertyContainer<DTypeParameter>) = copy(extra = newExtras) +} + +data class DTypeAlias( + override val dri: DRI, + override val name: String, + override val type: Bound, + val underlyingType: SourceSetDependent<Bound>, + override val visibility: SourceSetDependent<Visibility>, + override val documentation: SourceSetDependent<DocumentationNode>, + override val expectPresentInSet: DokkaSourceSet?, + override val sourceSets: Set<DokkaSourceSet>, + override val extra: PropertyContainer<DTypeAlias> = PropertyContainer.empty() +) : Documentable(), WithType, WithVisibility, WithExtraProperties<DTypeAlias> { + override val children: List<Nothing> + get() = emptyList() + + override fun withNewExtras(newExtras: PropertyContainer<DTypeAlias>) = copy(extra = newExtras) +} + +sealed class Projection +sealed class Bound : Projection() +data class OtherParameter(val declarationDRI: DRI, val name: String) : Bound() +object Star : Projection() +data class TypeConstructor( + val dri: DRI, + val projections: List<Projection>, + val modifier: FunctionModifiers = FunctionModifiers.NONE +) : Bound() + +data class Nullable(val inner: Bound) : Bound() +data class Variance(val kind: Kind, val inner: Bound) : Projection() { + enum class Kind { In, Out } +} + +data class PrimitiveJavaType(val name: String) : Bound() +object Void : Bound() +object JavaObject : Bound() +object Dynamic : Bound() +data class UnresolvedBound(val name: String) : Bound() + +enum class FunctionModifiers { + NONE, FUNCTION, EXTENSION +} + +private fun String.shorten(maxLength: Int) = lineSequence().first().let { + if (it.length != length || it.length > maxLength) it.take(maxLength - 3) + "..." else it +} + +fun Documentable.dfs(predicate: (Documentable) -> Boolean): Documentable? = + if (predicate(this)) { + this + } else { + this.children.asSequence().mapNotNull { it.dfs(predicate) }.firstOrNull() + } + +sealed class Visibility(val name: String) +sealed class KotlinVisibility(name: String) : Visibility(name) { + object Public : KotlinVisibility("public") + object Private : KotlinVisibility("private") + object Protected : KotlinVisibility("protected") + object Internal : KotlinVisibility("internal") +} + +sealed class JavaVisibility(name: String) : Visibility(name) { + object Public : JavaVisibility("public") + object Private : JavaVisibility("private") + object Protected : JavaVisibility("protected") + object Default : JavaVisibility("") +} + +fun <T> SourceSetDependent<T>?.orEmpty(): SourceSetDependent<T> = this ?: emptyMap() + +interface DocumentableSource { + val path: String +} diff --git a/core/src/main/kotlin/model/WithChildren.kt b/core/src/main/kotlin/model/WithChildren.kt new file mode 100644 index 00000000..589bcd2a --- /dev/null +++ b/core/src/main/kotlin/model/WithChildren.kt @@ -0,0 +1,64 @@ +package org.jetbrains.dokka.model + +interface WithChildren<out T> { + val children: List<T> +} + +inline fun <reified T> WithChildren<*>.firstChildOfTypeOrNull(): T? = + children.filterIsInstance<T>().firstOrNull() + +inline fun <reified T> WithChildren<*>.firstChildOfTypeOrNull(predicate: (T) -> Boolean): T? = + children.filterIsInstance<T>().firstOrNull(predicate) + +inline fun <reified T> WithChildren<*>.firstChildOfType(): T = + children.filterIsInstance<T>().first() + +inline fun <reified T> WithChildren<*>.firstChildOfType(predicate: (T) -> Boolean): T = + children.filterIsInstance<T>().first(predicate) + +inline fun <reified T> WithChildren<WithChildren<*>>.firstMemberOfType(): T where T : WithChildren<*> { + return withDescendants().filterIsInstance<T>().first() +} + +inline fun <reified T> WithChildren<WithChildren<*>>.firstMemberOfTypeOrNull(): T? where T : WithChildren<*> { + return withDescendants().filterIsInstance<T>().firstOrNull() +} + +fun <T> T.withDescendants(): Sequence<T> where T : WithChildren<T> { + return sequence { + yield(this@withDescendants) + children.forEach { child -> + yieldAll(child.withDescendants()) + } + } +} + +@JvmName("withDescendantsProjection") +fun WithChildren<*>.withDescendants(): Sequence<Any?> { + return sequence { + yield(this@withDescendants) + children.forEach { child -> + if (child is WithChildren<*>) { + yieldAll(child.withDescendants()) + } + } + } +} + +@JvmName("withDescendantsAny") +fun WithChildren<Any>.withDescendants(): Sequence<Any> { + return sequence { + yield(this@withDescendants) + children.forEach { child -> + if (child is WithChildren<*>) { + yieldAll(child.withDescendants().filterNotNull()) + } + } + } +} + +fun <T> T.dfs(predicate: (T) -> Boolean): T? where T : WithChildren<T> = if (predicate(this)) { + this +} else { + children.asSequence().mapNotNull { it.dfs(predicate) }.firstOrNull() +} diff --git a/core/src/main/kotlin/model/additionalExtras.kt b/core/src/main/kotlin/model/additionalExtras.kt new file mode 100644 index 00000000..94d0e751 --- /dev/null +++ b/core/src/main/kotlin/model/additionalExtras.kt @@ -0,0 +1,75 @@ +package org.jetbrains.dokka.model + +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.properties.ExtraProperty +import org.jetbrains.dokka.model.properties.MergeStrategy + +class AdditionalModifiers(val content: SourceSetDependent<Set<ExtraModifiers>>) : ExtraProperty<Documentable> { + companion object : ExtraProperty.Key<Documentable, AdditionalModifiers> { + override fun mergeStrategyFor( + left: AdditionalModifiers, + right: AdditionalModifiers + ): MergeStrategy<Documentable> = MergeStrategy.Replace(AdditionalModifiers(left.content + right.content)) + } + + override fun equals(other: Any?): Boolean = + if (other is AdditionalModifiers) other.content == content else false + + override fun hashCode() = content.hashCode() + override val key: ExtraProperty.Key<Documentable, *> = AdditionalModifiers +} + +fun SourceSetDependent<Set<ExtraModifiers>>.toAdditionalModifiers() = AdditionalModifiers(this) + +class Annotations(val content: SourceSetDependent<List<Annotation>>) : ExtraProperty<Documentable> { + companion object : ExtraProperty.Key<Documentable, Annotations> { + override fun mergeStrategyFor(left: Annotations, right: Annotations): MergeStrategy<Documentable> = + MergeStrategy.Replace(Annotations(left.content + right.content)) + } + + override val key: ExtraProperty.Key<Documentable, *> = Annotations + + data class Annotation(val dri: DRI, val params: Map<String, AnnotationParameterValue>, val mustBeDocumented: Boolean = false) { + override fun equals(other: Any?): Boolean = when (other) { + is Annotation -> dri == other.dri + else -> false + } + + override fun hashCode(): Int = dri.hashCode() + } +} + +fun SourceSetDependent<List<Annotations.Annotation>>.toAnnotations() = Annotations(this) + +sealed class AnnotationParameterValue +data class AnnotationValue(val annotation: Annotations.Annotation) : AnnotationParameterValue() +data class ArrayValue(val value: List<AnnotationParameterValue>) : AnnotationParameterValue() +data class EnumValue(val enumName: String, val enumDri: DRI) : AnnotationParameterValue() +data class ClassValue(val className: String, val classDRI: DRI) : AnnotationParameterValue() +data class StringValue(val value: String) : AnnotationParameterValue() + + +object PrimaryConstructorExtra : ExtraProperty<DFunction>, ExtraProperty.Key<DFunction, PrimaryConstructorExtra> { + override val key: ExtraProperty.Key<DFunction, *> = this +} + +data class ActualTypealias(val underlyingType: SourceSetDependent<Bound>) : ExtraProperty<DClasslike> { + companion object : ExtraProperty.Key<DClasslike, ActualTypealias> { + override fun mergeStrategyFor( + left: ActualTypealias, + right: ActualTypealias + ) = + MergeStrategy.Replace(ActualTypealias(left.underlyingType + right.underlyingType)) + } + + override val key: ExtraProperty.Key<DClasslike, ActualTypealias> = ActualTypealias +} + +data class ConstructorValues(val values: SourceSetDependent<List<String>>) : ExtraProperty<DEnumEntry>{ + companion object : ExtraProperty.Key<DEnumEntry, ConstructorValues> { + override fun mergeStrategyFor(left: ConstructorValues, right: ConstructorValues) = + MergeStrategy.Replace(ConstructorValues(left.values + right.values)) + } + + override val key: ExtraProperty.Key<DEnumEntry, ConstructorValues> = ConstructorValues +}
\ No newline at end of file diff --git a/core/src/main/kotlin/model/classKinds.kt b/core/src/main/kotlin/model/classKinds.kt new file mode 100644 index 00000000..be8c47b0 --- /dev/null +++ b/core/src/main/kotlin/model/classKinds.kt @@ -0,0 +1,20 @@ +package org.jetbrains.dokka.model + +interface ClassKind + +enum class KotlinClassKindTypes : ClassKind { + CLASS, + INTERFACE, + ENUM_CLASS, + ENUM_ENTRY, + ANNOTATION_CLASS, + OBJECT; +} + +enum class JavaClassKindTypes : ClassKind { + CLASS, + INTERFACE, + ENUM_CLASS, + ENUM_ENTRY, + ANNOTATION_CLASS; +} diff --git a/core/src/main/kotlin/model/defaultValues.kt b/core/src/main/kotlin/model/defaultValues.kt new file mode 100644 index 00000000..ab6cd376 --- /dev/null +++ b/core/src/main/kotlin/model/defaultValues.kt @@ -0,0 +1,13 @@ +package org.jetbrains.dokka.model + +import org.jetbrains.dokka.model.properties.ExtraProperty +import org.jetbrains.dokka.model.properties.MergeStrategy + +class DefaultValue(val value: String): ExtraProperty<DParameter> { + companion object : ExtraProperty.Key<DParameter, DefaultValue> { + override fun mergeStrategyFor(left: DefaultValue, right: DefaultValue): MergeStrategy<DParameter> = MergeStrategy.Remove // TODO pass a logger somehow and log this + } + + override val key: ExtraProperty.Key<DParameter, *> + get() = Companion +}
\ No newline at end of file diff --git a/core/src/main/kotlin/model/doc/DocTag.kt b/core/src/main/kotlin/model/doc/DocTag.kt new file mode 100644 index 00000000..dc2cd2be --- /dev/null +++ b/core/src/main/kotlin/model/doc/DocTag.kt @@ -0,0 +1,96 @@ +package org.jetbrains.dokka.model.doc + +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.WithChildren + +sealed class DocTag( + override val children: List<DocTag>, + val params: Map<String, String> +) : WithChildren<DocTag> { + override fun equals(other: Any?): Boolean = + ( + other != null && + other::class == this::class && + this.children == (other as DocTag).children && + this.params == other.params + ) + + override fun hashCode(): Int = children.hashCode() + params.hashCode() +} + +class A(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Big(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class B(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class BlockQuote(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +object Br : DocTag(emptyList(), emptyMap()) +class Cite(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +sealed class Code(children: List<DocTag>, params: Map<String, String>) : DocTag(children, params) +class CodeInline(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : Code(children, params) +class CodeBlock(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : Code(children, params) +class Dd(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Dfn(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Dir(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Div(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Dl(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Dt(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Em(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Font(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Footer(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Frame(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class FrameSet(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class H1(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class H2(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class H3(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class H4(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class H5(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class H6(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Head(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Header(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Html(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class I(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class IFrame(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Img(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Input(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Li(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Link(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Listing(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Main(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Menu(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Meta(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Nav(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class NoFrames(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class NoScript(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Ol(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class P(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Pre(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Script(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Section(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Small(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Span(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Strikethrough(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Strong(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Sub(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Sup(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Table(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Text(val body: String = "", children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) { + override fun equals(other: Any?): Boolean = super.equals(other) && this.body == (other as Text).body + override fun hashCode(): Int = super.hashCode() + body.hashCode() +} +class TBody(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Td(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class TFoot(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Th(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class THead(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Title(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Tr(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Tt(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class U(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Ul(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class Var(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class DocumentationLink(val dri: DRI, children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) { + override fun equals(other: Any?): Boolean = super.equals(other) && this.dri == (other as DocumentationLink).dri + override fun hashCode(): Int = super.hashCode() + dri.hashCode() +} +object HorizontalRule : DocTag(emptyList(), emptyMap()) +class Index(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) +class CustomDocTag(children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap()) : DocTag(children, params) diff --git a/core/src/main/kotlin/model/doc/DocumentationNode.kt b/core/src/main/kotlin/model/doc/DocumentationNode.kt new file mode 100644 index 00000000..6eb26a6a --- /dev/null +++ b/core/src/main/kotlin/model/doc/DocumentationNode.kt @@ -0,0 +1,5 @@ +package org.jetbrains.dokka.model.doc + +import org.jetbrains.dokka.model.WithChildren + +data class DocumentationNode(override val children: List<TagWrapper>): WithChildren<TagWrapper> diff --git a/core/src/main/kotlin/model/doc/TagWrapper.kt b/core/src/main/kotlin/model/doc/TagWrapper.kt new file mode 100644 index 00000000..095f3eaf --- /dev/null +++ b/core/src/main/kotlin/model/doc/TagWrapper.kt @@ -0,0 +1,39 @@ +package org.jetbrains.dokka.model.doc + +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.WithChildren + +sealed class TagWrapper(val root: DocTag) : WithChildren<DocTag> { + + override val children: List<DocTag> + get() = root.children + + override fun equals(other: Any?): Boolean = + ( + other != null && + this::class == other::class && + this.root == (other as TagWrapper).root + ) + + override fun hashCode(): Int = root.hashCode() +} +sealed class NamedTagWrapper(root: DocTag, val name: String) : TagWrapper(root) { + override fun equals(other: Any?): Boolean = super.equals(other) && this.name == (other as NamedTagWrapper).name + override fun hashCode(): Int = super.hashCode() + name.hashCode() +} + +class Description(root: DocTag) : TagWrapper(root) +class Author(root: DocTag) : TagWrapper(root) +class Version(root: DocTag) : TagWrapper(root) +class Since(root: DocTag) : TagWrapper(root) +class See(root: DocTag, name: String, val address: DRI?) : NamedTagWrapper(root, name) +class Param(root: DocTag, name: String) : NamedTagWrapper(root, name) +class Return(root: DocTag) : TagWrapper(root) +class Receiver(root: DocTag) : TagWrapper(root) +class Constructor(root: DocTag) : TagWrapper(root) +class Throws(root: DocTag, name: String) : NamedTagWrapper(root, name) +class Sample(root: DocTag, name: String) : NamedTagWrapper(root, name) +class Deprecated(root: DocTag) : TagWrapper(root) +class Property(root: DocTag, name: String) : NamedTagWrapper(root, name) +class Suppress(root: DocTag) : TagWrapper(root) +class CustomTagWrapper(root: DocTag, name: String) : NamedTagWrapper(root, name) diff --git a/core/src/main/kotlin/model/documentableProperties.kt b/core/src/main/kotlin/model/documentableProperties.kt new file mode 100644 index 00000000..cd6a9335 --- /dev/null +++ b/core/src/main/kotlin/model/documentableProperties.kt @@ -0,0 +1,27 @@ +package org.jetbrains.dokka.model + +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.properties.ExtraProperty +import org.jetbrains.dokka.model.properties.MergeStrategy + +data class InheritedFunction(val inheritedFrom: SourceSetDependent<DRI?>) : ExtraProperty<DFunction> { + companion object : ExtraProperty.Key<DFunction, InheritedFunction> { + override fun mergeStrategyFor(left: InheritedFunction, right: InheritedFunction) = MergeStrategy.Replace( + InheritedFunction(left.inheritedFrom + right.inheritedFrom) + ) + } + + fun isInherited(sourceSetDependent: DokkaSourceSet): Boolean = inheritedFrom[sourceSetDependent] != null + + override val key: ExtraProperty.Key<DFunction, *> = InheritedFunction +} + +data class ImplementedInterfaces(val interfaces: SourceSetDependent<List<DRI>>) : ExtraProperty<Documentable> { + companion object : ExtraProperty.Key<Documentable, ImplementedInterfaces> { + override fun mergeStrategyFor(left: ImplementedInterfaces, right: ImplementedInterfaces) = + MergeStrategy.Replace(ImplementedInterfaces(left.interfaces + right.interfaces)) + } + + override val key: ExtraProperty.Key<Documentable, *> = ImplementedInterfaces +}
\ No newline at end of file diff --git a/core/src/main/kotlin/model/documentableUtils.kt b/core/src/main/kotlin/model/documentableUtils.kt new file mode 100644 index 00000000..287cf313 --- /dev/null +++ b/core/src/main/kotlin/model/documentableUtils.kt @@ -0,0 +1,22 @@ +package org.jetbrains.dokka.model + +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet + +fun <T> SourceSetDependent<T>.filtered(sourceSets: Set<DokkaSourceSet>) = filter { it.key in sourceSets } +fun DokkaSourceSet?.filtered(sourceSets: Set<DokkaSourceSet>) = takeIf { this in sourceSets } + +fun DTypeParameter.filter(filteredSet: Set<DokkaSourceSet>) = + if (filteredSet.containsAll(sourceSets)) this + else { + val intersection = filteredSet.intersect(sourceSets) + if (intersection.isEmpty()) null + else DTypeParameter( + dri, + name, + documentation.filtered(intersection), + expectPresentInSet?.takeIf { intersection.contains(expectPresentInSet) }, + bounds, + intersection, + extra + ) + } diff --git a/core/src/main/kotlin/model/extraModifiers.kt b/core/src/main/kotlin/model/extraModifiers.kt new file mode 100644 index 00000000..efaa3d60 --- /dev/null +++ b/core/src/main/kotlin/model/extraModifiers.kt @@ -0,0 +1,62 @@ +package org.jetbrains.dokka.model + +sealed class ExtraModifiers(val name: String) { + + sealed class KotlinOnlyModifiers(name: String) : ExtraModifiers(name) { + object Inline : KotlinOnlyModifiers("inline") + object Infix : KotlinOnlyModifiers("infix") + object External : KotlinOnlyModifiers("external") + object Suspend : KotlinOnlyModifiers("suspend") + object Reified : KotlinOnlyModifiers("reified") + object CrossInline : KotlinOnlyModifiers("crossinline") + object NoInline : KotlinOnlyModifiers("noinline") + object Override : KotlinOnlyModifiers("override") + object Data : KotlinOnlyModifiers("data") + object Const : KotlinOnlyModifiers("const") + object Inner : KotlinOnlyModifiers("inner") + object LateInit : KotlinOnlyModifiers("lateinit") + object Operator : KotlinOnlyModifiers("operator") + object TailRec : KotlinOnlyModifiers("tailrec") + object VarArg : KotlinOnlyModifiers("vararg") + object Fun : KotlinOnlyModifiers("fun") + } + + sealed class JavaOnlyModifiers(name: String) : ExtraModifiers(name) { + object Static : JavaOnlyModifiers("static") + object Native : JavaOnlyModifiers("native") + object Synchronized : JavaOnlyModifiers("synchronized") + object StrictFP : JavaOnlyModifiers("strictfp") + object Transient : JavaOnlyModifiers("transient") + object Volatile : JavaOnlyModifiers("volatile") + object Transitive : JavaOnlyModifiers("transitive") + } + + companion object { + fun valueOf(str: String) = when (str) { + "inline" -> KotlinOnlyModifiers.Inline + "infix" -> KotlinOnlyModifiers.Infix + "external" -> KotlinOnlyModifiers.External + "suspend" -> KotlinOnlyModifiers.Suspend + "reified" -> KotlinOnlyModifiers.Reified + "crossinline" -> KotlinOnlyModifiers.CrossInline + "noinline" -> KotlinOnlyModifiers.NoInline + "override" -> KotlinOnlyModifiers.Override + "data" -> KotlinOnlyModifiers.Data + "const" -> KotlinOnlyModifiers.Const + "inner" -> KotlinOnlyModifiers.Inner + "lateinit" -> KotlinOnlyModifiers.LateInit + "operator" -> KotlinOnlyModifiers.Operator + "tailrec" -> KotlinOnlyModifiers.TailRec + "vararg" -> KotlinOnlyModifiers.VarArg + "static" -> JavaOnlyModifiers.Static + "native" -> JavaOnlyModifiers.Native + "synchronized" -> JavaOnlyModifiers.Synchronized + "strictfp" -> JavaOnlyModifiers.StrictFP + "transient" -> JavaOnlyModifiers.Transient + "volatile" -> JavaOnlyModifiers.Volatile + "transitive" -> JavaOnlyModifiers.Transitive + "fun" -> KotlinOnlyModifiers.Fun + else -> throw IllegalArgumentException("There is no Extra Modifier for given name $str") + } + } +}
\ No newline at end of file diff --git a/core/src/main/kotlin/model/properties/PropertyContainer.kt b/core/src/main/kotlin/model/properties/PropertyContainer.kt new file mode 100644 index 00000000..6009bfe0 --- /dev/null +++ b/core/src/main/kotlin/model/properties/PropertyContainer.kt @@ -0,0 +1,60 @@ +package org.jetbrains.dokka.model.properties + +class PropertyContainer<C : Any> internal constructor( + @PublishedApi internal val map: Map<ExtraProperty.Key<C, *>, ExtraProperty<C>> +) { + operator fun <D : C> plus(prop: ExtraProperty<D>): PropertyContainer<D> = + PropertyContainer(map + (prop.key to prop)) + + // TODO: Add logic for caching calculated properties + inline operator fun <reified T : Any> get(key: ExtraProperty.Key<C, T>): T? = when (val prop = map[key]) { + is T? -> prop + else -> throw ClassCastException("Property for $key stored under not matching key type.") + } + + inline fun <reified T : Any> allOfType(): List<T> = map.values.filterIsInstance<T>() + fun <D : C> addAll(extras: Collection<ExtraProperty<D>>): PropertyContainer<D> = + PropertyContainer(map + extras.map { p -> p.key to p }) + + companion object { + fun <T : Any> empty(): PropertyContainer<T> = PropertyContainer(emptyMap()) + fun <T : Any> withAll(vararg extras: ExtraProperty<T>) = empty<T>().addAll(extras.toList()) + fun <T : Any> withAll(extras: Collection<ExtraProperty<T>>) = empty<T>().addAll(extras) + } +} + +operator fun <D: Any> PropertyContainer<D>.plus(prop: ExtraProperty<D>?): PropertyContainer<D> = + if (prop == null) this else PropertyContainer(map + (prop.key to prop)) + +interface WithExtraProperties<C : Any> { + val extra: PropertyContainer<C> + + fun withNewExtras(newExtras: PropertyContainer<C>): C +} + +fun <C> C.mergeExtras(left: C, right: C): C where C : Any, C : WithExtraProperties<C> { + val aggregatedExtras: List<List<ExtraProperty<C>>> = + (left.extra.map.values + right.extra.map.values) + .groupBy { it.key } + .values + .map { it.distinct() } + + val (unambiguous, toMerge) = aggregatedExtras.partition { it.size == 1 } + + @Suppress("UNCHECKED_CAST") + val strategies: List<MergeStrategy<C>> = toMerge.map { (l, r) -> + (l.key as ExtraProperty.Key<C, ExtraProperty<C>>).mergeStrategyFor(l, r) + } + + strategies.filterIsInstance<MergeStrategy.Fail>().firstOrNull()?.error?.invoke() + + val replaces: List<ExtraProperty<C>> = + strategies.filterIsInstance<MergeStrategy.Replace<C>>().map { it.newProperty } + + val needingFullMerge: List<(preMerged: C, left: C, right: C) -> C> = + strategies.filterIsInstance<MergeStrategy.Full<C>>().map { it.merger } + + val newExtras = PropertyContainer((unambiguous.flatten() + replaces).associateBy { it.key }) + + return needingFullMerge.fold(withNewExtras(newExtras)) { acc, merger -> merger(acc, left, right) } +}
\ No newline at end of file diff --git a/core/src/main/kotlin/model/properties/properties.kt b/core/src/main/kotlin/model/properties/properties.kt new file mode 100644 index 00000000..7010d0df --- /dev/null +++ b/core/src/main/kotlin/model/properties/properties.kt @@ -0,0 +1,22 @@ +package org.jetbrains.dokka.model.properties + +interface ExtraProperty<in C : Any> { + interface Key<in C : Any, T : Any> { + fun mergeStrategyFor(left: T, right: T): MergeStrategy<C> = MergeStrategy.Fail { + throw NotImplementedError("Property merging for $this is not implemented") + } + } + + val key: Key<C, *> +} + +interface CalculatedProperty<in C : Any, T : Any> : ExtraProperty.Key<C, T> { + fun calculate(subject: C): T +} + +sealed class MergeStrategy<in C> { + class Replace<in C : Any>(val newProperty: ExtraProperty<C>) : MergeStrategy<C>() + object Remove : MergeStrategy<Any>() + class Full<C : Any>(val merger: (preMerged: C, left: C, right: C) -> C) : MergeStrategy<C>() + class Fail(val error: () -> Nothing) : MergeStrategy<Any>() +} diff --git a/core/src/main/kotlin/pages/ContentNodes.kt b/core/src/main/kotlin/pages/ContentNodes.kt new file mode 100644 index 00000000..5129dfcf --- /dev/null +++ b/core/src/main/kotlin/pages/ContentNodes.kt @@ -0,0 +1,266 @@ +package org.jetbrains.dokka.pages + +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.WithChildren +import org.jetbrains.dokka.model.properties.PropertyContainer +import org.jetbrains.dokka.model.properties.WithExtraProperties + +data class DCI(val dri: Set<DRI>, val kind: Kind) { + override fun toString() = "$dri[$kind]" +} + +interface ContentNode : WithExtraProperties<ContentNode>, WithChildren<ContentNode> { + val dci: DCI + val sourceSets: Set<DokkaSourceSet> + val style: Set<Style> + + fun hasAnyContent(): Boolean + + override val children: List<ContentNode> + get() = emptyList() +} + +/** Simple text */ +data class ContentText( + val text: String, + override val dci: DCI, + override val sourceSets: Set<DokkaSourceSet>, + override val style: Set<Style> = emptySet(), + override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty() +) : ContentNode { + override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentNode = copy(extra = newExtras) + + override fun hasAnyContent(): Boolean = !text.isBlank() +} + +// TODO: Remove +data class ContentBreakLine( + override val sourceSets: Set<DokkaSourceSet>, + override val dci: DCI = DCI(emptySet(), ContentKind.Empty), + override val style: Set<Style> = emptySet(), + override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty() +) : ContentNode { + override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentNode = copy(extra = newExtras) + + override fun hasAnyContent(): Boolean = true +} + +/** Headers */ +data class ContentHeader( + override val children: List<ContentNode>, + val level: Int, + override val dci: DCI, + override val sourceSets: Set<DokkaSourceSet>, + override val style: Set<Style>, + override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty() +) : ContentComposite { + constructor(level: Int, c: ContentComposite) : this(c.children, level, c.dci, c.sourceSets, c.style, c.extra) + + override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentHeader = copy(extra = newExtras) +} + +interface ContentCode : ContentComposite + +/** Code blocks */ +data class ContentCodeBlock( + override val children: List<ContentNode>, + val language: String, + override val dci: DCI, + override val sourceSets: Set<DokkaSourceSet>, + override val style: Set<Style>, + override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty() +) : ContentCode { + override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentCodeBlock = copy(extra = newExtras) +} + +data class ContentCodeInline( + override val children: List<ContentNode>, + val language: String, + override val dci: DCI, + override val sourceSets: Set<DokkaSourceSet>, + override val style: Set<Style>, + override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty() +) : ContentCode { + override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentCodeInline = copy(extra = newExtras) +} + +/** Union type replacement */ +interface ContentLink : ContentComposite + +/** All links to classes, packages, etc. that have te be resolved */ +data class ContentDRILink( + override val children: List<ContentNode>, + val address: DRI, + override val dci: DCI, + override val sourceSets: Set<DokkaSourceSet>, + override val style: Set<Style> = emptySet(), + override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty() +) : ContentLink { + override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentDRILink = copy(extra = newExtras) +} + +/** All links that do not need to be resolved */ +data class ContentResolvedLink( + override val children: List<ContentNode>, + val address: String, + override val dci: DCI, + override val sourceSets: Set<DokkaSourceSet>, + override val style: Set<Style> = emptySet(), + override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty() +) : ContentLink { + override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentResolvedLink = + copy(extra = newExtras) +} + +/** Embedded resources like images */ +data class ContentEmbeddedResource( + override val children: List<ContentNode> = emptyList(), + val address: String, + val altText: String?, + override val dci: DCI, + override val sourceSets: Set<DokkaSourceSet>, + override val style: Set<Style> = emptySet(), + override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty() +) : ContentLink { + override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentEmbeddedResource = + copy(extra = newExtras) +} + +/** Logical grouping of [ContentNode]s */ +interface ContentComposite : ContentNode { + override val children: List<ContentNode> // overwrite to make it abstract once again + + override fun hasAnyContent(): Boolean = children.any { it.hasAnyContent() } +} + +/** Tables */ +data class ContentTable( + val header: List<ContentGroup>, + override val children: List<ContentGroup>, + override val dci: DCI, + override val sourceSets: Set<DokkaSourceSet>, + override val style: Set<Style>, + override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty() +) : ContentComposite { + override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentTable = copy(extra = newExtras) +} + +/** Lists */ +data class ContentList( + override val children: List<ContentNode>, + val ordered: Boolean, + override val dci: DCI, + override val sourceSets: Set<DokkaSourceSet>, + override val style: Set<Style>, + override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty() +) : ContentComposite { + override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentList = copy(extra = newExtras) +} + +/** Default group, eg. for blocks of Functions, Properties, etc. **/ +data class ContentGroup( + override val children: List<ContentNode>, + override val dci: DCI, + override val sourceSets: Set<DokkaSourceSet>, + override val style: Set<Style>, + override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty() +) : ContentComposite { + override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentGroup = copy(extra = newExtras) +} + +/** + * @property groupID is used for finding and copying [ContentDivergentInstance]s when merging [ContentPage]s + */ +data class ContentDivergentGroup( + override val children: List<ContentDivergentInstance>, + override val dci: DCI, + override val style: Set<Style>, + override val extra: PropertyContainer<ContentNode>, + val groupID: GroupID, + val implicitlySourceSetHinted: Boolean = true +) : ContentComposite { + data class GroupID(val name: String) + + override val sourceSets: Set<DokkaSourceSet> + get() = children.flatMap { it.sourceSets }.distinct().toSet() + + override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentDivergentGroup = + copy(extra = newExtras) +} + +/** Instance of a divergent content */ +data class ContentDivergentInstance( + val before: ContentNode?, + val divergent: ContentNode, + val after: ContentNode?, + override val dci: DCI, + override val sourceSets: Set<DokkaSourceSet>, + override val style: Set<Style>, + override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty() +) : ContentComposite { + override val children: List<ContentNode> + get() = listOfNotNull(before, divergent, after) + + override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentDivergentInstance = + copy(extra = newExtras) +} + +data class PlatformHintedContent( + val inner: ContentNode, + override val sourceSets: Set<DokkaSourceSet> +) : ContentComposite { + override val children = listOf(inner) + + override val dci: DCI + get() = inner.dci + + override val extra: PropertyContainer<ContentNode> + get() = inner.extra + + override val style: Set<Style> + get() = inner.style + + override fun withNewExtras(newExtras: PropertyContainer<ContentNode>) = + throw UnsupportedOperationException("This method should not be called on this PlatformHintedContent") +} + +interface Style +interface Kind + +enum class ContentKind : Kind { + + Comment, Constructors, Functions, Parameters, Properties, Classlikes, Packages, Symbol, Sample, Main, BriefComment, + Empty, Source, TypeAliases, Cover, Inheritors, SourceSetDependentHint, Extensions, Annotations; + + companion object { + private val platformTagged = + setOf(Constructors, Functions, Properties, Classlikes, Packages, Source, TypeAliases, Inheritors, Extensions) + + fun shouldBePlatformTagged(kind: Kind): Boolean = kind in platformTagged + } +} + +enum class TextStyle : Style { + Bold, Italic, Strong, Strikethrough, Paragraph, Block, Span, Monospace, Indented, Cover, UnderCoverText, BreakableAfter, Breakable +} + +enum class ContentStyle : Style { + RowTitle, TabbedContent, WithExtraAttributes, RunnableSample, InDocumentationAnchor +} + +object CommentTable : Style + +object MultimoduleTable : Style + +fun ContentNode.dfs(predicate: (ContentNode) -> Boolean): ContentNode? = if (predicate(this)) { + this +} else { + if (this is ContentComposite) { + this.children.asSequence().mapNotNull { it.dfs(predicate) }.firstOrNull() + } else { + null + } +} + +fun ContentNode.hasStyle(style: Style) = this.style.contains(style) diff --git a/core/src/main/kotlin/pages/PageNodes.kt b/core/src/main/kotlin/pages/PageNodes.kt new file mode 100644 index 00000000..71ec8597 --- /dev/null +++ b/core/src/main/kotlin/pages/PageNodes.kt @@ -0,0 +1,181 @@ +package org.jetbrains.dokka.pages + +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.Documentable +import org.jetbrains.dokka.model.WithChildren +import java.util.* + +interface PageNode: WithChildren<PageNode> { + val name: String + + fun modified( + name: String = this.name, + children: List<PageNode> = this.children + ): PageNode +} + +interface ContentPage: PageNode { + val content: ContentNode + val dri: Set<DRI> + val documentable: Documentable? + val embeddedResources: List<String> + + fun modified( + name: String = this.name, + content: ContentNode = this.content, + dri: Set<DRI> = this.dri, + embeddedResources: List<String> = this.embeddedResources, + children: List<PageNode> = this.children + ): ContentPage +} + +abstract class RootPageNode: PageNode { + val parentMap: Map<PageNode, PageNode> by lazy { + IdentityHashMap<PageNode, PageNode>().apply { + fun process(parent: PageNode) { + parent.children.forEach { child -> + put(child, parent) + process(child) + } + } + process(this@RootPageNode) + } + } + + fun transformPageNodeTree(operation: (PageNode) -> PageNode) = + this.transformNode(operation) as RootPageNode + + fun transformContentPagesTree(operation: (ContentPage) -> ContentPage) = transformPageNodeTree { + if (it is ContentPage) operation(it) else it + } + + private fun PageNode.transformNode(operation: (PageNode) -> PageNode): PageNode = + operation(this).let { newNode -> + newNode.modified(children = newNode.children.map { it.transformNode(operation) }) + } + + abstract override fun modified( + name: String, + children: List<PageNode> + ): RootPageNode +} + +class ModulePageNode( + override val name: String, + override val content: ContentNode, + override val documentable: Documentable?, + override val children: List<PageNode>, + override val embeddedResources: List<String> = listOf() +) : RootPageNode(), ContentPage { + override val dri: Set<DRI> = setOf(DRI.topLevel) + + override fun modified(name: String, children: List<PageNode>): ModulePageNode = + modified(name = name, content = this.content, dri = dri, children = children) + + override fun modified( + name: String, + content: ContentNode, + dri: Set<DRI>, + embeddedResources: List<String>, + children: List<PageNode> + ): ModulePageNode = + if (name == this.name && content === this.content && embeddedResources === this.embeddedResources && children shallowEq this.children) this + else ModulePageNode(name, content, documentable, children, embeddedResources) +} + +class PackagePageNode( + override val name: String, + override val content: ContentNode, + override val dri: Set<DRI>, + override val documentable: Documentable?, + override val children: List<PageNode>, + override val embeddedResources: List<String> = listOf() +) : ContentPage { + override fun modified(name: String, children: List<PageNode>): PackagePageNode = + modified(name = name, content = this.content, children = children) + + override fun modified( + name: String, + content: ContentNode, + dri: Set<DRI>, + embeddedResources: List<String>, + children: List<PageNode> + ): PackagePageNode = + if (name == this.name && content === this.content && embeddedResources === this.embeddedResources && children shallowEq this.children) this + else PackagePageNode(name, content, dri, documentable, children, embeddedResources) +} + +class ClasslikePageNode( + override val name: String, + override val content: ContentNode, + override val dri: Set<DRI>, + override val documentable: Documentable?, + override val children: List<PageNode>, + override val embeddedResources: List<String> = listOf() +) : ContentPage { + override fun modified(name: String, children: List<PageNode>): ClasslikePageNode = + modified(name = name, content = this.content, children = children) + + override fun modified( + name: String, + content: ContentNode, + dri: Set<DRI>, + embeddedResources: List<String>, + children: List<PageNode> + ): ClasslikePageNode = + if (name == this.name && content === this.content && embeddedResources === this.embeddedResources && children shallowEq this.children) this + else ClasslikePageNode(name, content, dri, documentable, children, embeddedResources) +} + +class MemberPageNode( + override val name: String, + override val content: ContentNode, + override val dri: Set<DRI>, + override val documentable: Documentable?, + override val children: List<PageNode> = emptyList(), + override val embeddedResources: List<String> = listOf() +) : ContentPage { + override fun modified(name: String, children: List<PageNode>): MemberPageNode = + modified(name = name, content = this.content, children = children) as MemberPageNode + + override fun modified( + name: String, + content: ContentNode, + dri: Set<DRI>, + embeddedResources: List<String>, + children: List<PageNode> + ): MemberPageNode = + if (name == this.name && content === this.content && embeddedResources === this.embeddedResources && children shallowEq this.children) this + else MemberPageNode(name, content, dri, documentable, children, embeddedResources) +} + + +class MultimoduleRootPageNode( + override val name: String, + override val dri: Set<DRI>, + override val content: ContentNode, + override val embeddedResources: List<String> = emptyList() +) : RootPageNode(), ContentPage { + + override val children: List<PageNode> = emptyList() + + override val documentable: Documentable? = null + + override fun modified(name: String, children: List<PageNode>): RootPageNode = + MultimoduleRootPageNode(name, dri, content, embeddedResources) + + override fun modified( + name: String, + content: ContentNode, + dri: Set<DRI>, + embeddedResources: List<String>, + children: List<PageNode> + ) = + if (name == this.name && content === this.content && embeddedResources === this.embeddedResources && children shallowEq this.children) this + else MultimoduleRootPageNode(name, dri, content, embeddedResources) +} + +inline fun <reified T: PageNode> PageNode.children() = children.filterIsInstance<T>() + +private infix fun <T> List<T>.shallowEq(other: List<T>) = + this === other || (this.size == other.size && (this zip other).all { (a, b) -> a === b }) diff --git a/core/src/main/kotlin/pages/RendererSpecificPage.kt b/core/src/main/kotlin/pages/RendererSpecificPage.kt new file mode 100644 index 00000000..85e6d530 --- /dev/null +++ b/core/src/main/kotlin/pages/RendererSpecificPage.kt @@ -0,0 +1,40 @@ +package org.jetbrains.dokka.pages + +import org.jetbrains.dokka.renderers.Renderer +import kotlin.reflect.KClass + +interface RendererSpecificPage : PageNode { + val strategy: RenderingStrategy +} + +class RendererSpecificRootPage( + override val name: String, + override val children: List<PageNode>, + override val strategy: RenderingStrategy +) : RootPageNode(), RendererSpecificPage { + override fun modified(name: String, children: List<PageNode>): RendererSpecificRootPage = + RendererSpecificRootPage(name, children, strategy) +} + +class RendererSpecificResourcePage( + override val name: String, + override val children: List<PageNode>, + override val strategy: RenderingStrategy +): RendererSpecificPage { + override fun modified(name: String, children: List<PageNode>): RendererSpecificResourcePage = + RendererSpecificResourcePage(name, children, strategy) +} + +sealed class RenderingStrategy { + class Callback(val instructions: Renderer.(PageNode) -> String): RenderingStrategy() + data class Copy(val from: String) : RenderingStrategy() + data class Write(val text: String) : RenderingStrategy() + object DoNothing : RenderingStrategy() + + companion object { + inline operator fun <reified T: Renderer> invoke(crossinline instructions: T.(PageNode) -> String) = + Callback { if (this is T) instructions(it) else throw WrongRendererTypeException(T::class) } + } +} + +data class WrongRendererTypeException(val expectedType: KClass<*>): Exception()
\ No newline at end of file diff --git a/core/src/main/kotlin/pages/contentNodeProperties.kt b/core/src/main/kotlin/pages/contentNodeProperties.kt new file mode 100644 index 00000000..67acef6d --- /dev/null +++ b/core/src/main/kotlin/pages/contentNodeProperties.kt @@ -0,0 +1,12 @@ +package org.jetbrains.dokka.pages + +import org.jetbrains.dokka.model.properties.ExtraProperty + +class SimpleAttr(val extraKey: String, val extraValue: String) : ExtraProperty<ContentNode> { + data class SimpleAttrKey(val key: String) : ExtraProperty.Key<ContentNode, SimpleAttr> + override val key: ExtraProperty.Key<ContentNode, SimpleAttr> = SimpleAttrKey(extraKey) + + companion object { + fun header(value: String) = SimpleAttr("data-togglable", value) + } +} diff --git a/core/src/main/kotlin/pages/utils.kt b/core/src/main/kotlin/pages/utils.kt new file mode 100644 index 00000000..c9039416 --- /dev/null +++ b/core/src/main/kotlin/pages/utils.kt @@ -0,0 +1,31 @@ +package org.jetbrains.dokka.pages + +import kotlin.reflect.KClass + +inline fun <reified T : ContentNode, R : ContentNode> R.mapTransform(noinline operation: (T) -> T): R = + mapTransform(T::class, operation) + +@PublishedApi +@Suppress("UNCHECKED_CAST") +internal fun <T : ContentNode, R : ContentNode> R.mapTransform(type: KClass<T>, operation: (T) -> T): R { + if (this::class == type) { + return operation(this as T) as R + } + val new = when (this) { + is ContentGroup -> this.copy(children.map { it.mapTransform(type, operation) }) + is ContentHeader -> this.copy(children.map { it.mapTransform(type, operation) }) + is ContentCodeBlock -> this.copy(children.map { it.mapTransform(type, operation) }) + is ContentCodeInline -> this.copy(children.map { it.mapTransform(type, operation) }) + is ContentTable -> this.copy(children.map { it.mapTransform(type, operation) }) + is ContentList -> this.copy(children.map { it.mapTransform(type, operation) }) + is ContentDivergentGroup -> this.copy(children.map { it.mapTransform(type, operation) }) + is ContentDivergentInstance -> this.copy( + before = before?.mapTransform(type, operation), + divergent = divergent.mapTransform(type, operation), + after = after?.mapTransform(type, operation) + ) + is PlatformHintedContent -> this.copy(inner.mapTransform(type, operation)) + else -> this + } + return new as R +} diff --git a/core/src/main/kotlin/plugability/DefaultExtensions.kt b/core/src/main/kotlin/plugability/DefaultExtensions.kt new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/core/src/main/kotlin/plugability/DefaultExtensions.kt diff --git a/core/src/main/kotlin/plugability/DokkaContext.kt b/core/src/main/kotlin/plugability/DokkaContext.kt new file mode 100644 index 00000000..323039e9 --- /dev/null +++ b/core/src/main/kotlin/plugability/DokkaContext.kt @@ -0,0 +1,223 @@ +package org.jetbrains.dokka.plugability + +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.utilities.DokkaLogger +import java.io.File +import java.net.URLClassLoader +import java.util.* +import kotlin.reflect.KClass +import kotlin.reflect.full.createInstance + + +interface DokkaContext { + fun <T : DokkaPlugin> plugin(kclass: KClass<T>): T? + + operator fun <T, E> get(point: E): List<T> + where T : Any, E : ExtensionPoint<T> + + fun <T, E> single(point: E): T where T : Any, E : ExtensionPoint<T> + + val logger: DokkaLogger + val configuration: DokkaConfiguration + val unusedPoints: Collection<ExtensionPoint<*>> + + + companion object { + fun create( + configuration: DokkaConfiguration, + logger: DokkaLogger, + pluginOverrides: List<DokkaPlugin> + ): DokkaContext = + DokkaContextConfigurationImpl(logger, configuration).apply { + // File(it.path) is a workaround for an incorrect filesystem in a File instance returned by Gradle. + configuration.pluginsClasspath.map { File(it.path).toURI().toURL() } + .toTypedArray() + .let { URLClassLoader(it, this.javaClass.classLoader) } + .also { checkClasspath(it) } + .let { ServiceLoader.load(DokkaPlugin::class.java, it) } + .let { it + pluginOverrides } + .forEach { install(it) } + topologicallySortAndPrune() + }.also { it.logInitialisationInfo() } + } +} + +inline fun <reified T : DokkaPlugin> DokkaContext.plugin(): T = plugin(T::class) + ?: throw java.lang.IllegalStateException("Plugin ${T::class.qualifiedName} is not present in context.") + +interface DokkaContextConfiguration { + fun installExtension(extension: Extension<*, *, *>) +} + +private class DokkaContextConfigurationImpl( + override val logger: DokkaLogger, + override val configuration: DokkaConfiguration +) : DokkaContext, DokkaContextConfiguration { + private val plugins = mutableMapOf<KClass<*>, DokkaPlugin>() + private val pluginStubs = mutableMapOf<KClass<*>, DokkaPlugin>() + val extensions = mutableMapOf<ExtensionPoint<*>, MutableList<Extension<*, *, *>>>() + val pointsUsed: MutableSet<ExtensionPoint<*>> = mutableSetOf() + val pointsPopulated: MutableSet<ExtensionPoint<*>> = mutableSetOf() + override val unusedPoints: Set<ExtensionPoint<*>> + get() = pointsPopulated - pointsUsed + + private enum class State { + UNVISITED, + VISITING, + VISITED; + } + + private sealed class Suppression { + data class ByExtension(val extension: Extension<*, *, *>) : Suppression() { + override fun toString() = extension.toString() + } + + data class ByPlugin(val plugin: DokkaPlugin) : Suppression() { + override fun toString() = "Plugin ${plugin::class.qualifiedName}" + } + } + + private val rawExtensions = mutableListOf<Extension<*, *, *>>() + private val rawAdjacencyList = mutableMapOf<Extension<*, *, *>, MutableList<Extension<*, *, *>>>() + private val suppressedExtensions = mutableMapOf<Extension<*, *, *>, MutableList<Suppression>>() + + fun topologicallySortAndPrune() { + pointsPopulated.clear() + extensions.clear() + + val overridesInfo = processOverrides() + val extensionsToSort = overridesInfo.keys + val adjacencyList = translateAdjacencyList(overridesInfo) + + val verticesWithState = extensionsToSort.associateWithTo(mutableMapOf()) { State.UNVISITED } + val result: MutableList<Extension<*, *, *>> = mutableListOf() + + fun visit(n: Extension<*, *, *>) { + val state = verticesWithState[n] + if (state == State.VISITED) + return + if (state == State.VISITING) + throw Error("Detected cycle in plugins graph") + verticesWithState[n] = State.VISITING + adjacencyList[n]?.forEach { visit(it) } + verticesWithState[n] = State.VISITED + result += n + } + + extensionsToSort.forEach(::visit) + + val filteredResult = result.asReversed().filterNot { it in suppressedExtensions } + + filteredResult.mapTo(pointsPopulated) { it.extensionPoint } + filteredResult.groupByTo(extensions) { it.extensionPoint } + } + + private fun processOverrides(): Map<Extension<*, *, *>, Set<Extension<*, *, *>>> { + val buckets = rawExtensions.associateWithTo(mutableMapOf()) { setOf(it) } + suppressedExtensions.forEach { (extension, suppressions) -> + val mergedBucket = suppressions.filterIsInstance<Suppression.ByExtension>() + .map { it.extension } + .plus(extension) + .flatMap { buckets[it].orEmpty() } + .toSet() + mergedBucket.forEach { buckets[it] = mergedBucket } + } + return buckets.values.distinct().associateBy(::findNotOverridden) + } + + private fun findNotOverridden(bucket: Set<Extension<*, *, *>>): Extension<*, *, *> { + val filtered = bucket.filter { it !in suppressedExtensions } + return filtered.singleOrNull() ?: throw IllegalStateException("Conflicting overrides: $filtered") + } + + private fun translateAdjacencyList( + overridesInfo: Map<Extension<*, *, *>, Set<Extension<*, *, *>>> + ): Map<Extension<*, *, *>, List<Extension<*, *, *>>> { + val reverseOverrideInfo = overridesInfo.flatMap { (ext, set) -> set.map { it to ext } }.toMap() + return rawAdjacencyList.mapNotNull { (ext, list) -> + reverseOverrideInfo[ext]?.to(list.mapNotNull { reverseOverrideInfo[it] }) + }.toMap() + } + + @Suppress("UNCHECKED_CAST") + override operator fun <T, E> get(point: E) where T : Any, E : ExtensionPoint<T> = + actions(point).also { pointsUsed += point }.orEmpty() as List<T> + + @Suppress("UNCHECKED_CAST") + override fun <T, E> single(point: E): T where T : Any, E : ExtensionPoint<T> { + fun throwBadArity(substitution: String): Nothing = throw IllegalStateException( + "$point was expected to have exactly one extension registered, but $substitution found." + ) + pointsUsed += point + + val extensions = extensions[point].orEmpty() as List<Extension<T, *, *>> + return when (extensions.size) { + 0 -> throwBadArity("none was") + 1 -> extensions.single().action.get(this) + else -> throwBadArity("many were") + } + } + + private fun <E : ExtensionPoint<*>> actions(point: E) = extensions[point]?.map { it.action.get(this) } + + @Suppress("UNCHECKED_CAST") + override fun <T : DokkaPlugin> plugin(kclass: KClass<T>) = (plugins[kclass] ?: pluginStubFor(kclass)) as T + + private fun <T : DokkaPlugin> pluginStubFor(kclass: KClass<T>): DokkaPlugin = + pluginStubs.getOrPut(kclass) { kclass.createInstance().also { it.context = this } } + + fun install(plugin: DokkaPlugin) { + plugins[plugin::class] = plugin + plugin.context = this + plugin.internalInstall(this, this.configuration) + + if (plugin is WithUnsafeExtensionSuppression) { + plugin.extensionsSuppressed.forEach { + suppressedExtensions.listFor(it) += Suppression.ByPlugin(plugin) + } + } + } + + override fun installExtension(extension: Extension<*, *, *>) { + rawExtensions += extension + + if (extension.ordering is OrderingKind.ByDsl) { + val orderDsl = OrderDsl() + orderDsl.(extension.ordering.block)() + + rawAdjacencyList.listFor(extension) += orderDsl.following.toList() + orderDsl.previous.forEach { rawAdjacencyList.listFor(it) += extension } + } + + if (extension.override is OverrideKind.Present) { + suppressedExtensions.listFor(extension.override.overriden) += Suppression.ByExtension(extension) + } + } + + fun logInitialisationInfo() { + val pluginNames = plugins.values.map { it::class.qualifiedName.toString() } + + val loadedListForDebug = extensions.run { keys + values.flatten() }.toList() + .joinToString(prefix = "[\n", separator = ",\n", postfix = "\n]") { "\t$it" } + + val suppressedList = suppressedExtensions.asSequence() + .joinToString(prefix = "[\n", separator = ",\n", postfix = "\n]") { + "\t${it.key} by " + (it.value.singleOrNull() ?: it.value) + } + + logger.info("Loaded plugins: $pluginNames") + logger.info("Loaded: $loadedListForDebug") + logger.info("Suppressed: $suppressedList") + } +} + +private fun checkClasspath(classLoader: URLClassLoader) { + classLoader.findResource(DokkaContext::class.java.name.replace('.', '/') + ".class")?.also { + throw AssertionError( + "Dokka API found on plugins classpath. This will lead to subtle bugs. " + + "Please fix your plugins dependencies or exclude dokka api artifact from plugin classpath" + ) + } +} + +private fun <K, V> MutableMap<K, MutableList<V>>.listFor(key: K) = getOrPut(key, ::mutableListOf) diff --git a/core/src/main/kotlin/plugability/DokkaPlugin.kt b/core/src/main/kotlin/plugability/DokkaPlugin.kt new file mode 100644 index 00000000..2c755a49 --- /dev/null +++ b/core/src/main/kotlin/plugability/DokkaPlugin.kt @@ -0,0 +1,82 @@ +package org.jetbrains.dokka.plugability + +import com.google.gson.Gson +import org.jetbrains.dokka.DokkaConfiguration +import kotlin.properties.ReadOnlyProperty +import kotlin.reflect.KProperty +import kotlin.reflect.KProperty1 +import kotlin.reflect.full.createInstance + +abstract class DokkaPlugin { + private val extensionDelegates = mutableListOf<KProperty<*>>() + + @PublishedApi + internal var context: DokkaContext? = null + + protected inline fun <reified T : DokkaPlugin> plugin(): T = context?.plugin(T::class) ?: throwIllegalQuery() + + protected fun <T : Any> extensionPoint() = + object : ReadOnlyProperty<DokkaPlugin, ExtensionPoint<T>> { + override fun getValue(thisRef: DokkaPlugin, property: KProperty<*>) = ExtensionPoint<T>( + thisRef::class.qualifiedName ?: throw AssertionError("Plugin must be named class"), + property.name + ) + } + + protected fun <T : Any> extending(definition: ExtendingDSL.() -> Extension<T, *, *>) = ExtensionProvider(definition) + + protected class ExtensionProvider<T : Any> internal constructor( + private val definition: ExtendingDSL.() -> Extension<T, *, *> + ) { + operator fun provideDelegate(thisRef: DokkaPlugin, property: KProperty<*>) = lazy { + ExtendingDSL( + thisRef::class.qualifiedName ?: throw AssertionError("Plugin must be named class"), + property.name + ).definition() + }.also { thisRef.extensionDelegates += property } + } + + internal fun internalInstall(ctx: DokkaContextConfiguration, configuration: DokkaConfiguration) { + extensionDelegates.asSequence() + .filterIsInstance<KProperty1<DokkaPlugin, Extension<*, *, *>>>() // should be always true + .map { it.get(this) } + .forEach { if (configuration.(it.condition)()) ctx.installExtension(it) } + } +} + +interface WithUnsafeExtensionSuppression { + val extensionsSuppressed: List<Extension<*, *, *>> +} + +interface Configurable { + val pluginsConfiguration: Map<String, String> +} + +interface ConfigurableBlock + +inline fun <reified P : DokkaPlugin, reified T : ConfigurableBlock> Configurable.pluginConfiguration(block: T.() -> Unit) { + val instance = T::class.createInstance().apply(block) + + val mutablePluginsConfiguration = pluginsConfiguration as MutableMap<String, String> + mutablePluginsConfiguration[P::class.qualifiedName!!] = Gson().toJson(instance, T::class.java) +} + +inline fun <reified P : DokkaPlugin, reified E : Any> P.query(extension: P.() -> ExtensionPoint<E>): List<E> = + context?.let { it[extension()] } ?: throwIllegalQuery() + +inline fun <reified P : DokkaPlugin, reified E : Any> P.querySingle(extension: P.() -> ExtensionPoint<E>): E = + context?.single(extension()) ?: throwIllegalQuery() + +fun throwIllegalQuery(): Nothing = + throw IllegalStateException("Querying about plugins is only possible with dokka context initialised") + +inline fun <reified T : DokkaPlugin, reified R : ConfigurableBlock> configuration(context: DokkaContext): ReadOnlyProperty<Any?, R> { + return object : ReadOnlyProperty<Any?, R> { + override fun getValue(thisRef: Any?, property: KProperty<*>): R { + return context.configuration.pluginsConfiguration[T::class.qualifiedName + ?: throw AssertionError("Plugin must be named class")].let { + Gson().fromJson(it, R::class.java) + } + } + } +} diff --git a/core/src/main/kotlin/plugability/LazyEvaluated.kt b/core/src/main/kotlin/plugability/LazyEvaluated.kt new file mode 100644 index 00000000..c0c271f4 --- /dev/null +++ b/core/src/main/kotlin/plugability/LazyEvaluated.kt @@ -0,0 +1,16 @@ +package org.jetbrains.dokka.plugability + +internal class LazyEvaluated<T : Any> private constructor(private val recipe: ((DokkaContext) -> T)? = null, private var value: T? = null) { + + internal fun get(context: DokkaContext): T { + if(value == null) { + value = recipe?.invoke(context) + } + return value ?: throw AssertionError("Incorrect initialized LazyEvaluated instance") + } + + companion object { + fun <T : Any> fromInstance(value: T) = LazyEvaluated(value = value) + fun <T : Any> fromRecipe(recipe: (DokkaContext) -> T) = LazyEvaluated(recipe = recipe) + } +}
\ No newline at end of file diff --git a/core/src/main/kotlin/plugability/extensions.kt b/core/src/main/kotlin/plugability/extensions.kt new file mode 100644 index 00000000..c6dd0b85 --- /dev/null +++ b/core/src/main/kotlin/plugability/extensions.kt @@ -0,0 +1,87 @@ +package org.jetbrains.dokka.plugability + +import org.jetbrains.dokka.DokkaConfiguration + +data class ExtensionPoint<T : Any> internal constructor( + internal val pluginClass: String, + internal val pointName: String +) { + override fun toString() = "ExtensionPoint: $pluginClass/$pointName" +} + +sealed class OrderingKind { + object None : OrderingKind() + class ByDsl(val block: (OrderDsl.() -> Unit)) : OrderingKind() +} + +sealed class OverrideKind { + object None : OverrideKind() + class Present(val overriden: Extension<*, *, *>) : OverrideKind() +} + +class Extension<T : Any, Ordering : OrderingKind, Override : OverrideKind> internal constructor( + internal val extensionPoint: ExtensionPoint<T>, + internal val pluginClass: String, + internal val extensionName: String, + internal val action: LazyEvaluated<T>, + internal val ordering: Ordering, + internal val override: Override, + internal val conditions: List<DokkaConfiguration.() -> Boolean> +) { + override fun toString() = "Extension: $pluginClass/$extensionName" + + override fun equals(other: Any?) = + if (other is Extension<*, *, *>) this.pluginClass == other.pluginClass && this.extensionName == other.extensionName + else false + + override fun hashCode() = listOf(pluginClass, extensionName).hashCode() + + val condition: DokkaConfiguration.() -> Boolean + get() = { conditions.all { it(this) } } +} + +private fun <T : Any> Extension( + extensionPoint: ExtensionPoint<T>, + pluginClass: String, + extensionName: String, + action: LazyEvaluated<T> +) = Extension(extensionPoint, pluginClass, extensionName, action, OrderingKind.None, OverrideKind.None, emptyList()) + +@DslMarker +annotation class ExtensionsDsl + +@ExtensionsDsl +class ExtendingDSL(private val pluginClass: String, private val extensionName: String) { + + infix fun <T : Any> ExtensionPoint<T>.with(action: T) = + Extension(this, this@ExtendingDSL.pluginClass, extensionName, LazyEvaluated.fromInstance(action)) + + infix fun <T : Any> ExtensionPoint<T>.providing(action: (DokkaContext) -> T) = + Extension(this, this@ExtendingDSL.pluginClass, extensionName, LazyEvaluated.fromRecipe(action)) + + infix fun <T : Any, Override : OverrideKind> Extension<T, OrderingKind.None, Override>.order( + block: OrderDsl.() -> Unit + ) = Extension(extensionPoint, pluginClass, extensionName, action, OrderingKind.ByDsl(block), override, conditions) + + infix fun <T : Any, Override : OverrideKind, Ordering: OrderingKind> Extension<T, Ordering, Override>.applyIf( + condition: DokkaConfiguration.() -> Boolean + ) = Extension(extensionPoint, pluginClass, extensionName, action, ordering, override, conditions + condition) + + infix fun <T : Any, Override : OverrideKind, Ordering: OrderingKind> Extension<T, Ordering, Override>.override( + overriden: Extension<T, *, *> + ) = Extension(extensionPoint, pluginClass, extensionName, action, ordering, OverrideKind.Present(overriden), conditions) +} + +@ExtensionsDsl +class OrderDsl { + internal val previous = mutableSetOf<Extension<*, *, *>>() + internal val following = mutableSetOf<Extension<*, *, *>>() + + fun after(vararg extensions: Extension<*, *, *>) { + previous += extensions + } + + fun before(vararg extensions: Extension<*, *, *>) { + following += extensions + } +}
\ No newline at end of file diff --git a/core/src/main/kotlin/renderers/Renderer.kt b/core/src/main/kotlin/renderers/Renderer.kt new file mode 100644 index 00000000..10235f21 --- /dev/null +++ b/core/src/main/kotlin/renderers/Renderer.kt @@ -0,0 +1,7 @@ +package org.jetbrains.dokka.renderers + +import org.jetbrains.dokka.pages.RootPageNode + +interface Renderer { + fun render(root: RootPageNode) +}
\ No newline at end of file diff --git a/core/src/main/kotlin/transformers/documentation/DocumentableMerger.kt b/core/src/main/kotlin/transformers/documentation/DocumentableMerger.kt new file mode 100644 index 00000000..c8ae9c02 --- /dev/null +++ b/core/src/main/kotlin/transformers/documentation/DocumentableMerger.kt @@ -0,0 +1,8 @@ +package org.jetbrains.dokka.transformers.documentation + +import org.jetbrains.dokka.model.DModule +import org.jetbrains.dokka.plugability.DokkaContext + +interface DocumentableMerger { + operator fun invoke(modules: Collection<DModule>, context: DokkaContext): DModule +}
\ No newline at end of file diff --git a/core/src/main/kotlin/transformers/documentation/DocumentableToPageTranslator.kt b/core/src/main/kotlin/transformers/documentation/DocumentableToPageTranslator.kt new file mode 100644 index 00000000..a4daba63 --- /dev/null +++ b/core/src/main/kotlin/transformers/documentation/DocumentableToPageTranslator.kt @@ -0,0 +1,9 @@ +package org.jetbrains.dokka.transformers.documentation + +import org.jetbrains.dokka.model.DModule +import org.jetbrains.dokka.pages.ModulePageNode +import org.jetbrains.dokka.pages.RootPageNode + +interface DocumentableToPageTranslator { + operator fun invoke(module: DModule): RootPageNode +}
\ No newline at end of file diff --git a/core/src/main/kotlin/transformers/documentation/DocumentableTransformer.kt b/core/src/main/kotlin/transformers/documentation/DocumentableTransformer.kt new file mode 100644 index 00000000..3eb4704e --- /dev/null +++ b/core/src/main/kotlin/transformers/documentation/DocumentableTransformer.kt @@ -0,0 +1,8 @@ +package org.jetbrains.dokka.transformers.documentation + +import org.jetbrains.dokka.model.DModule +import org.jetbrains.dokka.plugability.DokkaContext + +interface DocumentableTransformer { + operator fun invoke(original: DModule, context: DokkaContext): DModule +}
\ No newline at end of file diff --git a/core/src/main/kotlin/transformers/documentation/PreMergeDocumentableTransformer.kt b/core/src/main/kotlin/transformers/documentation/PreMergeDocumentableTransformer.kt new file mode 100644 index 00000000..b67a1d57 --- /dev/null +++ b/core/src/main/kotlin/transformers/documentation/PreMergeDocumentableTransformer.kt @@ -0,0 +1,8 @@ +package org.jetbrains.dokka.transformers.documentation + +import org.jetbrains.dokka.model.DModule +import org.jetbrains.dokka.plugability.DokkaContext + +interface PreMergeDocumentableTransformer { + operator fun invoke(modules: List<DModule>): List<DModule> +}
\ No newline at end of file diff --git a/core/src/main/kotlin/transformers/pages/PageCreator.kt b/core/src/main/kotlin/transformers/pages/PageCreator.kt new file mode 100644 index 00000000..f74b5efa --- /dev/null +++ b/core/src/main/kotlin/transformers/pages/PageCreator.kt @@ -0,0 +1,8 @@ +package org.jetbrains.dokka.transformers.pages + +import org.jetbrains.dokka.pages.RootPageNode +import org.jetbrains.dokka.plugability.DokkaContext + +interface PageCreator { + operator fun invoke(): RootPageNode +}
\ No newline at end of file diff --git a/core/src/main/kotlin/transformers/pages/PageTransformer.kt b/core/src/main/kotlin/transformers/pages/PageTransformer.kt new file mode 100644 index 00000000..218d9821 --- /dev/null +++ b/core/src/main/kotlin/transformers/pages/PageTransformer.kt @@ -0,0 +1,7 @@ +package org.jetbrains.dokka.transformers.pages + +import org.jetbrains.dokka.pages.RootPageNode + +interface PageTransformer { + operator fun invoke(input: RootPageNode): RootPageNode +}
\ No newline at end of file diff --git a/core/src/main/kotlin/transformers/pages/PageTransformerBuilders.kt b/core/src/main/kotlin/transformers/pages/PageTransformerBuilders.kt new file mode 100644 index 00000000..291b72ef --- /dev/null +++ b/core/src/main/kotlin/transformers/pages/PageTransformerBuilders.kt @@ -0,0 +1,22 @@ +package org.jetbrains.dokka.transformers.pages + +import org.jetbrains.dokka.pages.PageNode +import org.jetbrains.dokka.pages.RootPageNode + +fun pageScanner(block: PageNode.() -> Unit) = object : PageTransformer { + override fun invoke(input: RootPageNode): RootPageNode = input.invokeOnAll(block) as RootPageNode +} + +fun pageMapper(block: PageNode.() -> PageNode) = object : PageTransformer { + override fun invoke(input: RootPageNode): RootPageNode = input.alterChildren(block) as RootPageNode +} + +fun pageStructureTransformer(block: RootPageNode.() -> RootPageNode) = object : PageTransformer { + override fun invoke(input: RootPageNode): RootPageNode = block(input) +} + +fun PageNode.invokeOnAll(block: PageNode.() -> Unit): PageNode = + this.also(block).also { it.children.forEach { it.invokeOnAll(block) } } + +fun PageNode.alterChildren(block: PageNode.() -> PageNode): PageNode = + block(this).modified(children = this.children.map { it.alterChildren(block) })
\ No newline at end of file diff --git a/core/src/main/kotlin/transformers/sources/SourceToDocumentableTranslator.kt b/core/src/main/kotlin/transformers/sources/SourceToDocumentableTranslator.kt new file mode 100644 index 00000000..6bc8fb14 --- /dev/null +++ b/core/src/main/kotlin/transformers/sources/SourceToDocumentableTranslator.kt @@ -0,0 +1,9 @@ +package org.jetbrains.dokka.transformers.sources + +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.model.DModule +import org.jetbrains.dokka.plugability.DokkaContext + +interface SourceToDocumentableTranslator { + fun invoke(sourceSet: DokkaSourceSet, context: DokkaContext): DModule +}
\ No newline at end of file diff --git a/core/src/main/kotlin/utilities/DokkaLogging.kt b/core/src/main/kotlin/utilities/DokkaLogging.kt new file mode 100644 index 00000000..6b8ed5d2 --- /dev/null +++ b/core/src/main/kotlin/utilities/DokkaLogging.kt @@ -0,0 +1,38 @@ +package org.jetbrains.dokka.utilities + +interface DokkaLogger { + var warningsCount: Int + var errorsCount: Int + fun debug(message: String) + fun info(message: String) + fun progress(message: String) + fun warn(message: String) + fun error(message: String) +} + +fun DokkaLogger.report() { + if (DokkaConsoleLogger.warningsCount > 0 || DokkaConsoleLogger.errorsCount > 0) { + info("Generation completed with ${DokkaConsoleLogger.warningsCount} warning" + + (if(DokkaConsoleLogger.warningsCount == 1) "" else "s") + + " and ${DokkaConsoleLogger.errorsCount} error" + + if(DokkaConsoleLogger.errorsCount == 1) "" else "s" + ) + } else { + info("generation completed successfully") + } +} + +object DokkaConsoleLogger : DokkaLogger { + override var warningsCount: Int = 0 + override var errorsCount: Int = 0 + + override fun debug(message: String)= println(message) + + override fun progress(message: String) = println("PROGRESS: $message") + + override fun info(message: String) = println(message) + + override fun warn(message: String) = println("WARN: $message").also { warningsCount++ } + + override fun error(message: String) = println("ERROR: $message").also { errorsCount++ } +} diff --git a/core/src/main/kotlin/Utilities/Html.kt b/core/src/main/kotlin/utilities/Html.kt index de1ce1a5..3226ca9d 100644 --- a/core/src/main/kotlin/Utilities/Html.kt +++ b/core/src/main/kotlin/utilities/Html.kt @@ -1,4 +1,4 @@ -package org.jetbrains.dokka +package org.jetbrains.dokka.utilities import java.net.URLEncoder @@ -9,4 +9,7 @@ import java.net.URLEncoder */ fun String.htmlEscape(): String = replace("&", "&").replace("<", "<").replace(">", ">") -fun String.urlEncoded(): String = URLEncoder.encode(this, "UTF-8")
\ No newline at end of file +fun String.urlEncoded(): String = URLEncoder.encode(this, "UTF-8") + +fun String.formatToEndWithHtml() = + if (endsWith(".html") || contains(Regex("\\.html#"))) this else "$this.html"
\ No newline at end of file diff --git a/core/src/main/kotlin/Utilities/ServiceLocator.kt b/core/src/main/kotlin/utilities/ServiceLocator.kt index eda83422..00c9ae9f 100644 --- a/core/src/main/kotlin/Utilities/ServiceLocator.kt +++ b/core/src/main/kotlin/utilities/ServiceLocator.kt @@ -1,4 +1,4 @@ -package org.jetbrains.dokka.Utilities +package org.jetbrains.dokka.utilities import java.io.File import java.net.URISyntaxException diff --git a/core/src/main/kotlin/Utilities/Uri.kt b/core/src/main/kotlin/utilities/Uri.kt index 9827c624..089b3cff 100644 --- a/core/src/main/kotlin/Utilities/Uri.kt +++ b/core/src/main/kotlin/utilities/Uri.kt @@ -1,4 +1,4 @@ -package org.jetbrains.dokka +package org.jetbrains.dokka.utilities import java.net.URI diff --git a/core/src/main/kotlin/utilities/cast.kt b/core/src/main/kotlin/utilities/cast.kt new file mode 100644 index 00000000..d4a8d73d --- /dev/null +++ b/core/src/main/kotlin/utilities/cast.kt @@ -0,0 +1,5 @@ +package org.jetbrains.dokka.utilities + +inline fun <reified T> Any.cast(): T { + return this as T +} diff --git a/core/src/main/kotlin/utilities/nodeDebug.kt b/core/src/main/kotlin/utilities/nodeDebug.kt new file mode 100644 index 00000000..0e8c61f7 --- /dev/null +++ b/core/src/main/kotlin/utilities/nodeDebug.kt @@ -0,0 +1,50 @@ +package org.jetbrains.dokka.utilities + +import org.jetbrains.dokka.model.Documentable +import org.jetbrains.dokka.pages.* + +const val DOWN = '\u2503' +const val BRANCH = '\u2523' +const val LAST = '\u2517' + +fun Documentable.pretty(prefix: String = "", isLast: Boolean = true): String { + val nextPrefix = prefix + (if (isLast) ' ' else DOWN) + ' ' + + return prefix + (if (isLast) LAST else BRANCH) + this.toString() + + children.dropLast(1) + .map { it.pretty(nextPrefix, false) } + .plus(children.lastOrNull()?.pretty(nextPrefix)) + .filterNotNull() + .takeIf { it.isNotEmpty() } + ?.joinToString(prefix = "\n", separator = "") + .orEmpty() + if (children.isEmpty()) "\n" else "" +} + +//fun Any.genericPretty(prefix: String = "", isLast: Boolean = true): String { +// val nextPrefix = prefix + (if (isLast) ' ' else DOWN) + ' ' +// +// return prefix + (if (isLast) LAST else BRANCH) + this.stringify() + +// allChildren().dropLast(1) +// .map { it.genericPretty(nextPrefix, false) } +// .plus(allChildren().lastOrNull()?.genericPretty(nextPrefix)) +// .filterNotNull() +// .takeIf { it.isNotEmpty() } +// ?.joinToString(prefix = "\n", separator = "") +// .orEmpty() + if (allChildren().isEmpty()) "\n" else "" +//} +private fun Any.stringify() = when(this) { + is ContentNode -> toString() + this.dci + is ContentPage -> this.name + this::class.simpleName + else -> toString() +} +//private fun Any.allChildren() = when(this){ +// is PageNode -> children + content +// is ContentBlock -> this.children +// is ContentHeader -> this.items +// is ContentStyle -> this.items +// is ContentSymbol -> this.parts +// is ContentComment -> this.parts +// is ContentGroup -> this.children +// is ContentList -> this.items +// else -> emptyList() +//} diff --git a/core/src/main/resources/META-INF/MANIFEST.MF b/core/src/main/resources/META-INF/MANIFEST.MF index 78fabddc..9d885be5 100644 --- a/core/src/main/resources/META-INF/MANIFEST.MF +++ b/core/src/main/resources/META-INF/MANIFEST.MF @@ -1,4 +1 @@ Manifest-Version: 1.0 -Class-Path: kotlin-plugin.jar -Main-Class: org.jetbrains.dokka.DokkaPackage - diff --git a/core/src/main/resources/META-INF/dokka/dokka-version.properties b/core/src/main/resources/META-INF/dokka/dokka-version.properties new file mode 100644 index 00000000..6b2e2bcd --- /dev/null +++ b/core/src/main/resources/META-INF/dokka/dokka-version.properties @@ -0,0 +1 @@ +dokka-version=<dokka-version> diff --git a/core/src/main/resources/dokka/format/javadoc.properties b/core/src/main/resources/dokka/format/javadoc.properties deleted file mode 100644 index a0d8a945..00000000 --- a/core/src/main/resources/dokka/format/javadoc.properties +++ /dev/null @@ -1,2 +0,0 @@ -class=org.jetbrains.dokka.javadoc.JavadocFormatDescriptor -description=Produces Javadoc, with Kotlin declarations as Java view
\ No newline at end of file diff --git a/core/src/main/resources/dokka/styles/style.css b/core/src/main/resources/dokka/styles/style.css deleted file mode 100644 index 914be69d..00000000 --- a/core/src/main/resources/dokka/styles/style.css +++ /dev/null @@ -1,283 +0,0 @@ -@import url(https://fonts.googleapis.com/css?family=Open+Sans:300i,400,700); - -body, table { - padding:50px; - font:14px/1.5 'Open Sans', "Helvetica Neue", Helvetica, Arial, sans-serif; - color:#555; - font-weight:300; - margin-left: auto; - margin-right: auto; - max-width: 1440px; -} - -.keyword { - color:black; - font-family:Monaco, Bitstream Vera Sans Mono, Lucida Console, Terminal; - font-size:12px; -} - -.symbol { - font-family:Monaco, Bitstream Vera Sans Mono, Lucida Console, Terminal; - font-size:12px; -} - -.identifier { - color: darkblue; - font-size:12px; - font-family:Monaco, Bitstream Vera Sans Mono, Lucida Console, Terminal; -} - -h1, h2, h3, h4, h5, h6 { - color:#222; - margin:0 0 20px; -} - -p, ul, ol, table, pre, dl { - margin:0 0 20px; -} - -h1, h2, h3 { - line-height:1.1; -} - -h1 { - font-size:28px; -} - -h2 { - color:#393939; -} - -h3, h4, h5, h6 { - color:#494949; -} - -a { - color:#258aaf; - font-weight:400; - text-decoration:none; -} - -a:hover { - color: inherit; - text-decoration:underline; -} - -a small { - font-size:11px; - color:#555; - margin-top:-0.6em; - display:block; -} - -.wrapper { - width:860px; - margin:0 auto; -} - -blockquote { - border-left:1px solid #e5e5e5; - margin:0; - padding:0 0 0 20px; - font-style:italic; -} - -code, pre { - font-family:Monaco, Bitstream Vera Sans Mono, Lucida Console, Terminal; - color:#333; - font-size:12px; -} - -pre { - display: block; -/* - padding:8px 8px; - background: #f8f8f8; - border-radius:5px; - border:1px solid #e5e5e5; -*/ - overflow-x: auto; -} - -table { - width:100%; - border-collapse:collapse; -} - -th, td { - text-align:left; - vertical-align: top; - padding:5px 10px; -} - -dt { - color:#444; - font-weight:700; -} - -th { - color:#444; -} - -img { - max-width:100%; -} - -header { - width:270px; - float:left; - position:fixed; -} - -header ul { - list-style:none; - height:40px; - - padding:0; - - background: #eee; - background: -moz-linear-gradient(top, #f8f8f8 0%, #dddddd 100%); - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f8f8f8), color-stop(100%,#dddddd)); - background: -webkit-linear-gradient(top, #f8f8f8 0%,#dddddd 100%); - background: -o-linear-gradient(top, #f8f8f8 0%,#dddddd 100%); - background: -ms-linear-gradient(top, #f8f8f8 0%,#dddddd 100%); - background: linear-gradient(top, #f8f8f8 0%,#dddddd 100%); - - border-radius:5px; - border:1px solid #d2d2d2; - box-shadow:inset #fff 0 1px 0, inset rgba(0,0,0,0.03) 0 -1px 0; - width:270px; -} - -header li { - width:89px; - float:left; - border-right:1px solid #d2d2d2; - height:40px; -} - -header ul a { - line-height:1; - font-size:11px; - color:#999; - display:block; - text-align:center; - padding-top:6px; - height:40px; -} - -strong { - color:#222; - font-weight:700; -} - -header ul li + li { - width:88px; - border-left:1px solid #fff; -} - -header ul li + li + li { - border-right:none; - width:89px; -} - -header ul a strong { - font-size:14px; - display:block; - color:#222; -} - -section { - width:500px; - float:right; - padding-bottom:50px; -} - -small { - font-size:11px; -} - -hr { - border:0; - background:#e5e5e5; - height:1px; - margin:0 0 20px; -} - -footer { - width:270px; - float:left; - position:fixed; - bottom:50px; -} - -@media print, screen and (max-width: 960px) { - - div.wrapper { - width:auto; - margin:0; - } - - header, section, footer { - float:none; - position:static; - width:auto; - } - - header { - padding-right:320px; - } - - section { - border:1px solid #e5e5e5; - border-width:1px 0; - padding:20px 0; - margin:0 0 20px; - } - - header a small { - display:inline; - } - - header ul { - position:absolute; - right:50px; - top:52px; - } -} - -@media print, screen and (max-width: 720px) { - body { - word-wrap:break-word; - } - - header { - padding:0; - } - - header ul, header p.view { - position:static; - } - - pre, code { - word-wrap:normal; - } -} - -@media print, screen and (max-width: 480px) { - body { - padding:15px; - } - - header ul { - display:none; - } -} - -@media print { - body { - padding:0.4in; - font-size:12pt; - color:#444; - } -} diff --git a/core/src/test/kotlin/DokkaConfigurationTestImplementations.kt b/core/src/test/kotlin/DokkaConfigurationTestImplementations.kt deleted file mode 100644 index a6f427b1..00000000 --- a/core/src/test/kotlin/DokkaConfigurationTestImplementations.kt +++ /dev/null @@ -1,81 +0,0 @@ -package org.jetbrains.dokka.tests - -import org.jetbrains.dokka.DokkaConfiguration -import org.jetbrains.dokka.Platform -import java.io.File - - -data class SourceLinkDefinitionImpl(override val path: String, - override val url: String, - override val lineSuffix: String?) : DokkaConfiguration.SourceLinkDefinition { - companion object { - fun parseSourceLinkDefinition(srcLink: String): DokkaConfiguration.SourceLinkDefinition { - val (path, urlAndLine) = srcLink.split('=') - return SourceLinkDefinitionImpl( - File(path).canonicalPath, - urlAndLine.substringBefore("#"), - urlAndLine.substringAfter("#", "").let { if (it.isEmpty()) null else "#$it" }) - } - } -} - -class SourceRootImpl(path: String) : DokkaConfiguration.SourceRoot { - override val path: String = File(path).absolutePath - - companion object { - fun parseSourceRoot(sourceRoot: String): DokkaConfiguration.SourceRoot = SourceRootImpl(sourceRoot) - } -} - -data class PackageOptionsImpl(override val prefix: String, - override val includeNonPublic: Boolean = false, - override val reportUndocumented: Boolean = true, - override val skipDeprecated: Boolean = false, - override val suppress: Boolean = false) : DokkaConfiguration.PackageOptions - - class DokkaConfigurationImpl( - override val outputDir: String = "", - override val format: String = "html", - override val generateIndexPages: Boolean = false, - override val cacheRoot: String? = null, - override val impliedPlatforms: List<String> = emptyList(), - override val passesConfigurations: List<DokkaConfiguration.PassConfiguration> = emptyList() -) : DokkaConfiguration - -class PassConfigurationImpl ( - override val classpath: List<String> = emptyList(), - override val moduleName: String = "", - override val sourceRoots: List<DokkaConfiguration.SourceRoot> = emptyList(), - override val samples: List<String> = emptyList(), - override val includes: List<String> = emptyList(), - override val includeNonPublic: Boolean = false, - override val includeRootPackage: Boolean = false, - override val reportUndocumented: Boolean = false, - override val skipEmptyPackages: Boolean = false, - override val skipDeprecated: Boolean = false, - override val jdkVersion: Int = 6, - override val sourceLinks: List<DokkaConfiguration.SourceLinkDefinition> = emptyList(), - override val perPackageOptions: List<DokkaConfiguration.PackageOptions> = emptyList(), - externalDocumentationLinks: List<DokkaConfiguration.ExternalDocumentationLink> = emptyList(), - override val languageVersion: String? = null, - override val apiVersion: String? = null, - override val noStdlibLink: Boolean = false, - override val noJdkLink: Boolean = false, - override val suppressedFiles: List<String> = emptyList(), - override val collectInheritedExtensionsFromLibraries: Boolean = false, - override val analysisPlatform: Platform = Platform.DEFAULT, - override val targets: List<String> = emptyList(), - override val sinceKotlin: String? = null -): DokkaConfiguration.PassConfiguration { - private val defaultLinks = run { - val links = mutableListOf<DokkaConfiguration.ExternalDocumentationLink>() - if (!noJdkLink) - links += DokkaConfiguration.ExternalDocumentationLink.Builder("https://docs.oracle.com/javase/$jdkVersion/docs/api/").build() - - if (!noStdlibLink) - links += DokkaConfiguration.ExternalDocumentationLink.Builder("https://kotlinlang.org/api/latest/jvm/stdlib/").build() - links - } - override val externalDocumentationLinks = defaultLinks + externalDocumentationLinks -} - diff --git a/core/src/test/kotlin/NodeSelect.kt b/core/src/test/kotlin/NodeSelect.kt deleted file mode 100644 index fe0394f9..00000000 --- a/core/src/test/kotlin/NodeSelect.kt +++ /dev/null @@ -1,90 +0,0 @@ -package org.jetbrains.dokka.tests - -import org.jetbrains.dokka.DocumentationNode -import org.jetbrains.dokka.NodeKind -import org.jetbrains.dokka.RefKind - -class SelectBuilder { - private val root = ChainFilterNode(SubgraphTraverseFilter(), null) - private var activeNode = root - private val chainEnds = mutableListOf<SelectFilter>() - - fun withName(name: String) = matching { it.name == name } - - fun withKind(kind: NodeKind) = matching{ it.kind == kind } - - fun matching(block: (DocumentationNode) -> Boolean) { - attachFilterAndMakeActive(PredicateFilter(block)) - } - - fun subgraph() { - attachFilterAndMakeActive(SubgraphTraverseFilter()) - } - - fun subgraphOf(kind: RefKind) { - attachFilterAndMakeActive(DirectEdgeFilter(kind)) - } - - private fun attachFilterAndMakeActive(next: SelectFilter) { - activeNode = ChainFilterNode(next, activeNode) - } - - private fun endChain() { - chainEnds += activeNode - } - - fun build(): SelectFilter { - endChain() - return CombineFilterNode(chainEnds) - } -} - -private class ChainFilterNode(val filter: SelectFilter, val previous: SelectFilter?): SelectFilter() { - override fun select(roots: Sequence<DocumentationNode>): Sequence<DocumentationNode> { - return filter.select(previous?.select(roots) ?: roots) - } -} - -private class CombineFilterNode(val previous: List<SelectFilter>): SelectFilter() { - override fun select(roots: Sequence<DocumentationNode>): Sequence<DocumentationNode> { - return previous.asSequence().flatMap { it.select(roots) } - } -} - -abstract class SelectFilter { - abstract fun select(roots: Sequence<DocumentationNode>): Sequence<DocumentationNode> -} - -private class SubgraphTraverseFilter: SelectFilter() { - override fun select(roots: Sequence<DocumentationNode>): Sequence<DocumentationNode> { - val visited = mutableSetOf<DocumentationNode>() - return roots.flatMap { - generateSequence(listOf(it)) { nodes -> - nodes.flatMap { it.allReferences() } - .map { it.to } - .filter { visited.add(it) } - .takeUnless { it.isEmpty() } - } - }.flatten() - } - -} - -private class PredicateFilter(val condition: (DocumentationNode) -> Boolean): SelectFilter() { - override fun select(roots: Sequence<DocumentationNode>): Sequence<DocumentationNode> { - return roots.filter(condition) - } -} - -private class DirectEdgeFilter(val kind: RefKind): SelectFilter() { - override fun select(roots: Sequence<DocumentationNode>): Sequence<DocumentationNode> { - return roots.flatMap { it.references(kind).asSequence() }.map { it.to } - } -} - - -fun selectNodes(root: DocumentationNode, block: SelectBuilder.() -> Unit): List<DocumentationNode> { - val builder = SelectBuilder() - builder.apply(block) - return builder.build().select(sequenceOf(root)).toMutableSet().toList() -}
\ No newline at end of file diff --git a/core/src/test/kotlin/TestAPI.kt b/core/src/test/kotlin/TestAPI.kt deleted file mode 100644 index 4f9af761..00000000 --- a/core/src/test/kotlin/TestAPI.kt +++ /dev/null @@ -1,353 +0,0 @@ -package org.jetbrains.dokka.tests - -import com.google.inject.Guice -import com.intellij.openapi.application.PathManager -import com.intellij.openapi.util.Disposer -import com.intellij.openapi.util.io.FileUtil -import com.intellij.rt.execution.junit.FileComparisonFailure -import org.jetbrains.dokka.* -import org.jetbrains.dokka.Utilities.DokkaAnalysisModule -import org.jetbrains.dokka.Utilities.DokkaRunModule -import org.jetbrains.kotlin.cli.common.config.ContentRoot -import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot -import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation -import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity -import org.jetbrains.kotlin.cli.common.messages.MessageCollector -import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.utils.PathUtil -import org.junit.Assert -import org.junit.Assert.fail -import java.io.File - -data class ModelConfig( - val roots: Array<ContentRoot> = arrayOf(), - val withJdk: Boolean = false, - val withKotlinRuntime: Boolean = false, - val format: String = "html", - val includeNonPublic: Boolean = true, - val perPackageOptions: List<DokkaConfiguration.PackageOptions> = emptyList(), - val analysisPlatform: Platform = Platform.DEFAULT, - val defaultPlatforms: List<String> = emptyList(), - val noStdlibLink: Boolean = true, - val collectInheritedExtensionsFromLibraries: Boolean = false, - val sourceLinks: List<DokkaConfiguration.SourceLinkDefinition> = emptyList() -) - -fun verifyModel( - modelConfig: ModelConfig, - verifier: (DocumentationModule) -> Unit -) { - val documentation = DocumentationModule("test") - - val passConfiguration = PassConfigurationImpl( - includeNonPublic = modelConfig.includeNonPublic, - skipEmptyPackages = false, - includeRootPackage = true, - sourceLinks = modelConfig.sourceLinks, - perPackageOptions = modelConfig.perPackageOptions, - noStdlibLink = modelConfig.noStdlibLink, - noJdkLink = false, - languageVersion = null, - apiVersion = null, - collectInheritedExtensionsFromLibraries = modelConfig.collectInheritedExtensionsFromLibraries - ) - val configuration = DokkaConfigurationImpl( - outputDir = "", - format = modelConfig.format, - generateIndexPages = false, - cacheRoot = "default", - passesConfigurations = listOf(passConfiguration) - ) - - appendDocumentation(documentation, configuration, passConfiguration, modelConfig) - documentation.prepareForGeneration(configuration) - - verifier(documentation) -} - -fun appendDocumentation( - documentation: DocumentationModule, - dokkaConfiguration: DokkaConfiguration, - passConfiguration: DokkaConfiguration.PassConfiguration, - modelConfig: ModelConfig -) { - val messageCollector = object : MessageCollector { - override fun clear() { - - } - - override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageLocation?) { - when (severity) { - CompilerMessageSeverity.STRONG_WARNING, - CompilerMessageSeverity.WARNING, - CompilerMessageSeverity.LOGGING, - CompilerMessageSeverity.OUTPUT, - CompilerMessageSeverity.INFO, - CompilerMessageSeverity.ERROR -> { - println("$severity: $message at $location") - } - CompilerMessageSeverity.EXCEPTION -> { - fail("$severity: $message at $location") - } - } - } - - override fun hasErrors() = false - } - - val environment = AnalysisEnvironment(messageCollector, modelConfig.analysisPlatform) - environment.apply { - if (modelConfig.withJdk || modelConfig.withKotlinRuntime) { - addClasspath(PathUtil.getJdkClassesRootsFromCurrentJre()) - } - if (modelConfig.withKotlinRuntime) { - if (analysisPlatform == Platform.jvm) { - val kotlinStrictfpRoot = PathManager.getResourceRoot(Strictfp::class.java, "/kotlin/jvm/Strictfp.class") - addClasspath(File(kotlinStrictfpRoot)) - } - if (analysisPlatform == Platform.js) { - val kotlinStdlibJsRoot = PathManager.getResourceRoot(Any::class.java, "/kotlin/jquery") - addClasspath(File(kotlinStdlibJsRoot)) - } - - if (analysisPlatform == Platform.common) { - // TODO: Feels hacky - val kotlinStdlibCommonRoot = ClassLoader.getSystemResource("kotlin/UInt.kotlin_metadata") - addClasspath(File(kotlinStdlibCommonRoot.file.replace("file:", "").replaceAfter(".jar", ""))) - } - } - addRoots(modelConfig.roots.toList()) - - loadLanguageVersionSettings(passConfiguration.languageVersion, passConfiguration.apiVersion) - } - val defaultPlatformsProvider = object : DefaultPlatformsProvider { - override fun getDefaultPlatforms(descriptor: DeclarationDescriptor) = modelConfig.defaultPlatforms - } - - val globalInjector = Guice.createInjector( - DokkaRunModule(dokkaConfiguration) - ) - - val injector = globalInjector.createChildInjector( - DokkaAnalysisModule( - environment, - dokkaConfiguration, - defaultPlatformsProvider, - documentation.nodeRefGraph, - passConfiguration, - DokkaConsoleLogger - ) - ) - - buildDocumentationModule(injector, documentation) - Disposer.dispose(environment) -} - -fun checkSourceExistsAndVerifyModel( - source: String, - modelConfig: ModelConfig = ModelConfig(), - verifier: (DocumentationModule) -> Unit -) { - require(File(source).exists()) { - "Cannot find test data file $source" - } - verifyModel( - ModelConfig( - roots = arrayOf(contentRootFromPath(source)), - withJdk = modelConfig.withJdk, - withKotlinRuntime = modelConfig.withKotlinRuntime, - format = modelConfig.format, - includeNonPublic = modelConfig.includeNonPublic, - sourceLinks = modelConfig.sourceLinks, - analysisPlatform = modelConfig.analysisPlatform - ), - - verifier = verifier - ) -} - -fun verifyPackageMember( - source: String, - modelConfig: ModelConfig = ModelConfig(), - verifier: (DocumentationNode) -> Unit -) { - checkSourceExistsAndVerifyModel( - source, - modelConfig = ModelConfig( - withJdk = modelConfig.withJdk, - withKotlinRuntime = modelConfig.withKotlinRuntime, - analysisPlatform = modelConfig.analysisPlatform - ) - ) { model -> - val pkg = model.members.single() - verifier(pkg.members.single()) - } -} - -fun verifyJavaModel( - source: String, - modelConfig: ModelConfig = ModelConfig(), - verifier: (DocumentationModule) -> Unit -) { - val tempDir = FileUtil.createTempDirectory("dokka", "") - try { - val sourceFile = File(source) - FileUtil.copy(sourceFile, File(tempDir, sourceFile.name)) - verifyModel( - ModelConfig( - roots = arrayOf(JavaSourceRoot(tempDir, null)), - withJdk = true, - withKotlinRuntime = modelConfig.withKotlinRuntime, - analysisPlatform = modelConfig.analysisPlatform - ), - verifier = verifier - ) - } finally { - FileUtil.delete(tempDir) - } -} - -fun verifyJavaPackageMember( - source: String, - modelConfig: ModelConfig = ModelConfig(), - verifier: (DocumentationNode) -> Unit -) { - verifyJavaModel(source, modelConfig) { model -> - val pkg = model.members.single() - verifier(pkg.members.single()) - } -} - -fun verifyOutput( - modelConfig: ModelConfig = ModelConfig(), - outputExtension: String, - outputGenerator: (DocumentationModule, StringBuilder) -> Unit -) { - verifyModel(modelConfig) { - verifyModelOutput(it, outputExtension, modelConfig.roots.first().path, outputGenerator) - } -} - -fun verifyOutput( - path: String, - outputExtension: String, - modelConfig: ModelConfig = ModelConfig(), - outputGenerator: (DocumentationModule, StringBuilder) -> Unit -) { - verifyOutput( - ModelConfig( - roots = arrayOf(contentRootFromPath(path)) + modelConfig.roots, - withJdk = modelConfig.withJdk, - withKotlinRuntime = modelConfig.withKotlinRuntime, - format = modelConfig.format, - includeNonPublic = modelConfig.includeNonPublic, - analysisPlatform = modelConfig.analysisPlatform, - noStdlibLink = modelConfig.noStdlibLink, - collectInheritedExtensionsFromLibraries = modelConfig.collectInheritedExtensionsFromLibraries - ), - outputExtension, - outputGenerator - ) -} - -fun verifyModelOutput( - it: DocumentationModule, - outputExtension: String, - sourcePath: String, - outputGenerator: (DocumentationModule, StringBuilder) -> Unit -) { - val output = StringBuilder() - outputGenerator(it, output) - val ext = outputExtension.removePrefix(".") - val expectedFile = File(sourcePath.replaceAfterLast(".", ext, sourcePath + "." + ext)) - assertEqualsIgnoringSeparators(expectedFile, output.toString()) -} - -fun verifyJavaOutput( - path: String, - outputExtension: String, - modelConfig: ModelConfig = ModelConfig(), - outputGenerator: (DocumentationModule, StringBuilder) -> Unit -) { - verifyJavaModel(path, modelConfig) { model -> - verifyModelOutput(model, outputExtension, path, outputGenerator) - } -} - -fun assertEqualsIgnoringSeparators(expectedFile: File, output: String) { - if (!expectedFile.exists()) expectedFile.createNewFile() - val expectedText = expectedFile.readText().replace("\r\n", "\n") - val actualText = output.replace("\r\n", "\n") - - if (expectedText != actualText) - throw FileComparisonFailure("", expectedText, actualText, expectedFile.canonicalPath) -} - -fun assertEqualsIgnoringSeparators(expectedOutput: String, output: String) { - Assert.assertEquals(expectedOutput.replace("\r\n", "\n"), output.replace("\r\n", "\n")) -} - -fun StringBuilder.appendChildren(node: ContentBlock): StringBuilder { - for (child in node.children) { - val childText = child.toTestString() - append(childText) - } - return this -} - -fun StringBuilder.appendNode(node: ContentNode): StringBuilder { - when (node) { - is ContentText -> { - append(node.text) - } - is ContentEmphasis -> append("*").appendChildren(node).append("*") - is ContentBlockCode -> { - if (node.language.isNotBlank()) - appendln("[code lang=${node.language}]") - else - appendln("[code]") - appendChildren(node) - appendln() - appendln("[/code]") - } - is ContentNodeLink -> { - append("[") - appendChildren(node) - append(" -> ") - append(node.node.toString()) - append("]") - } - is ContentBlock -> { - appendChildren(node) - } - is NodeRenderContent -> { - append("render(") - append(node.node) - append(",") - append(node.mode) - append(")") - } - is ContentSymbol -> { - append(node.text) - } - is ContentEmpty -> { /* nothing */ - } - else -> throw IllegalStateException("Don't know how to format node $node") - } - return this -} - -fun ContentNode.toTestString(): String { - val node = this - return StringBuilder().apply { - appendNode(node) - }.toString() -} - -val ContentRoot.path: String - get() = when (this) { - is KotlinSourceRoot -> path - is JavaSourceRoot -> file.path - else -> throw UnsupportedOperationException() - } diff --git a/core/src/test/kotlin/format/FileGeneratorTestCase.kt b/core/src/test/kotlin/format/FileGeneratorTestCase.kt deleted file mode 100644 index ef9e815d..00000000 --- a/core/src/test/kotlin/format/FileGeneratorTestCase.kt +++ /dev/null @@ -1,35 +0,0 @@ -package org.jetbrains.dokka.tests - -import org.jetbrains.dokka.* -import org.junit.Before -import org.junit.Rule -import org.junit.rules.TemporaryFolder - - -abstract class FileGeneratorTestCase { - abstract val formatService: FormatService - - @get:Rule - var folder = TemporaryFolder() - - val fileGenerator = FileGenerator(folder.apply { create() }.root) - - @Before - fun bindGenerator() { - fileGenerator.formatService = formatService - } - - fun buildPagesAndReadInto(nodes: List<DocumentationNode>, sb: StringBuilder) = with(fileGenerator) { - buildPages(nodes) - val byLocations = nodes.groupBy { location(it) } - byLocations.forEach { (loc, _) -> - if (byLocations.size > 1) { - if (sb.isNotBlank() && !sb.endsWith('\n')) { - sb.appendln() - } - sb.appendln("<!-- File: ${loc.file.relativeTo(root).toUnixString()} -->") - } - sb.append(loc.file.readText()) - } - } -}
\ No newline at end of file diff --git a/core/src/test/kotlin/format/GFMFormatTest.kt b/core/src/test/kotlin/format/GFMFormatTest.kt deleted file mode 100644 index 60de7d29..00000000 --- a/core/src/test/kotlin/format/GFMFormatTest.kt +++ /dev/null @@ -1,36 +0,0 @@ -package org.jetbrains.dokka.tests - -import org.jetbrains.dokka.GFMFormatService -import org.jetbrains.dokka.KotlinLanguageService -import org.jetbrains.dokka.Platform -import org.junit.Test - -abstract class BaseGFMFormatTest(val analysisPlatform: Platform) : FileGeneratorTestCase() { - override val formatService = GFMFormatService(fileGenerator, KotlinLanguageService(), listOf()) - private val defaultModelConfig = ModelConfig(analysisPlatform = analysisPlatform) - - - @Test - fun sample() { - verifyGFMNodeByName("sample", "Foo", defaultModelConfig) - } - - @Test - fun listInTableCell() { - verifyGFMNodeByName("listInTableCell", "Foo", defaultModelConfig) - } - - private fun verifyGFMNodeByName(fileName: String, name: String, modelConfig: ModelConfig) { - verifyOutput("testdata/format/gfm/$fileName.kt", ".md", modelConfig) { model, output -> - buildPagesAndReadInto( - model.members.single().members.filter { it.name == name }, - output - ) - } - } -} - - -class JsGFMFormatTest : BaseGFMFormatTest(Platform.js) -class JvmGFMFormatTest : BaseGFMFormatTest(Platform.jvm) -class CommonGFMFormatTest : BaseGFMFormatTest(Platform.common)
\ No newline at end of file diff --git a/core/src/test/kotlin/format/HtmlFormatTest.kt b/core/src/test/kotlin/format/HtmlFormatTest.kt deleted file mode 100644 index 60e29006..00000000 --- a/core/src/test/kotlin/format/HtmlFormatTest.kt +++ /dev/null @@ -1,192 +0,0 @@ -package org.jetbrains.dokka.tests - -import org.jetbrains.dokka.* -import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot -import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot -import org.junit.Test -import java.io.File - -abstract class BaseHtmlFormatTest(val analysisPlatform: Platform): FileGeneratorTestCase() { - protected val defaultModelConfig = ModelConfig(analysisPlatform = analysisPlatform) - override val formatService = HtmlFormatService(fileGenerator, KotlinLanguageService(), HtmlTemplateService.default(), listOf()) - - @Test fun classWithCompanionObject() { - verifyHtmlNode("classWithCompanionObject", defaultModelConfig) - } - - @Test fun htmlEscaping() { - verifyHtmlNode("htmlEscaping", defaultModelConfig) - } - - @Test fun overloads() { - verifyHtmlNodes("overloads", defaultModelConfig) { model -> model.members } - } - - @Test fun overloadsWithDescription() { - verifyHtmlNode("overloadsWithDescription", defaultModelConfig) - } - - @Test fun overloadsWithDifferentDescriptions() { - verifyHtmlNode("overloadsWithDifferentDescriptions", defaultModelConfig) - } - - @Test fun deprecated() { - verifyOutput("testdata/format/deprecated.kt", ".package.html", defaultModelConfig) { model, output -> - buildPagesAndReadInto(model.members, output) - } - verifyOutput("testdata/format/deprecated.kt", ".class.html", defaultModelConfig) { model, output -> - buildPagesAndReadInto(model.members.single().members, output) - } - } - - @Test fun brokenLink() { - verifyHtmlNode("brokenLink", defaultModelConfig) - } - - @Test fun codeSpan() { - verifyHtmlNode("codeSpan", defaultModelConfig) - } - - @Test fun parenthesis() { - verifyHtmlNode("parenthesis", defaultModelConfig) - } - - @Test fun bracket() { - verifyHtmlNode("bracket", defaultModelConfig) - } - - @Test fun see() { - verifyHtmlNode("see", defaultModelConfig) - } - - @Test fun tripleBackticks() { - verifyHtmlNode("tripleBackticks", defaultModelConfig) - } - - @Test fun typeLink() { - verifyHtmlNodes("typeLink", defaultModelConfig) { model -> model.members.single().members.filter { it.name == "Bar" } } - } - - @Test fun parameterAnchor() { - verifyHtmlNode("parameterAnchor", defaultModelConfig) - } - - @Test fun codeBlock() { - verifyHtmlNode("codeBlock", defaultModelConfig) - } - @Test fun orderedList() { - verifyHtmlNodes("orderedList", defaultModelConfig) { model -> model.members.single().members.filter { it.name == "Bar" } } - } - - @Test fun linkWithLabel() { - verifyHtmlNodes("linkWithLabel", defaultModelConfig) { model -> model.members.single().members.filter { it.name == "Bar" } } - } - - @Test fun entity() { - verifyHtmlNodes("entity", defaultModelConfig) { model -> model.members.single().members.filter { it.name == "Bar" } } - } - - @Test fun uninterpretedEmphasisCharacters() { - verifyHtmlNode("uninterpretedEmphasisCharacters", defaultModelConfig) - } - - @Test fun markdownInLinks() { - verifyHtmlNode("markdownInLinks", defaultModelConfig) - } - - @Test fun returnWithLink() { - verifyHtmlNode("returnWithLink", defaultModelConfig) - } - - @Test fun linkWithStarProjection() { - verifyHtmlNode("linkWithStarProjection", ModelConfig(analysisPlatform = analysisPlatform, withKotlinRuntime = true)) - } - - @Test fun functionalTypeWithNamedParameters() { - verifyHtmlNode("functionalTypeWithNamedParameters", defaultModelConfig) - } - - @Test fun sinceKotlin() { - verifyHtmlNode("sinceKotlin", defaultModelConfig) - } - - @Test fun blankLineInsideCodeBlock() { - verifyHtmlNode("blankLineInsideCodeBlock", defaultModelConfig) - } - - @Test fun indentedCodeBlock() { - verifyHtmlNode("indentedCodeBlock", defaultModelConfig) - } - - private fun verifyHtmlNode(fileName: String, modelConfig: ModelConfig = ModelConfig()) { - verifyHtmlNodes(fileName, modelConfig) { model -> model.members.single().members } - } - - private fun verifyHtmlNodes(fileName: String, - modelConfig: ModelConfig = ModelConfig(), - nodeFilter: (DocumentationModule) -> List<DocumentationNode>) { - verifyOutput("testdata/format/$fileName.kt", ".html", modelConfig) { model, output -> - buildPagesAndReadInto(nodeFilter(model), output) - } - } - - protected fun verifyJavaHtmlNode(fileName: String, modelConfig: ModelConfig = ModelConfig()) { - verifyJavaHtmlNodes(fileName, modelConfig) { model -> model.members.single().members } - } - - protected fun verifyJavaHtmlNodes(fileName: String, - modelConfig: ModelConfig = ModelConfig(), - nodeFilter: (DocumentationModule) -> List<DocumentationNode>) { - verifyJavaOutput("testdata/format/$fileName.java", ".html", modelConfig) { model, output -> - buildPagesAndReadInto(nodeFilter(model), output) - } - } -} - -class JSHtmlFormatTest: BaseHtmlFormatTest(Platform.js) - -class JVMHtmlFormatTest: BaseHtmlFormatTest(Platform.jvm) { - @Test - fun javaSeeTag() { - verifyJavaHtmlNode("javaSeeTag", defaultModelConfig) - } - - @Test fun javaDeprecated() { - verifyJavaHtmlNodes("javaDeprecated", defaultModelConfig) { model -> - model.members.single().members.single { it.name == "Foo" }.members.filter { it.name == "foo" } - } - } - - @Test fun crossLanguageKotlinExtendsJava() { - verifyOutput( - ModelConfig( - roots = arrayOf( - KotlinSourceRoot("testdata/format/crossLanguage/kotlinExtendsJava/Bar.kt", false), - JavaSourceRoot(File("testdata/format/crossLanguage/kotlinExtendsJava"), null) - ), - analysisPlatform = analysisPlatform - ), ".html") { model, output -> - buildPagesAndReadInto( - model.members.single().members.filter { it.name == "Bar" }, - output - ) - } - } - - @Test fun javaLinkTag() { - verifyJavaHtmlNode("javaLinkTag", defaultModelConfig) - } - - @Test fun javaLinkTagWithLabel() { - verifyJavaHtmlNode("javaLinkTagWithLabel", defaultModelConfig) - } - - @Test fun javaSupertypeLink() { - verifyJavaHtmlNodes("JavaSupertype", defaultModelConfig) { model -> - model.members.single().members.single { it.name == "JavaSupertype" }.members.filter { it.name == "Bar" } - } - } - -} - -class CommonHtmlFormatTest: BaseHtmlFormatTest(Platform.common)
\ No newline at end of file diff --git a/core/src/test/kotlin/format/KotlinWebSiteHtmlFormatTest.kt b/core/src/test/kotlin/format/KotlinWebSiteHtmlFormatTest.kt deleted file mode 100644 index ebab5f36..00000000 --- a/core/src/test/kotlin/format/KotlinWebSiteHtmlFormatTest.kt +++ /dev/null @@ -1,123 +0,0 @@ -package org.jetbrains.dokka.tests - -import org.jetbrains.dokka.* -import org.jetbrains.dokka.Generation.DocumentationMerger -import org.junit.Test - -abstract class BaseKotlinWebSiteHtmlFormatTest(val analysisPlatform: Platform): FileGeneratorTestCase() { - val defaultModelConfig = ModelConfig(analysisPlatform = analysisPlatform) - override val formatService = KotlinWebsiteHtmlFormatService(fileGenerator, KotlinLanguageService(), listOf(), EmptyHtmlTemplateService) - - @Test fun dropImport() { - verifyKWSNodeByName("dropImport", "foo", defaultModelConfig) - } - - @Test fun sample() { - verifyKWSNodeByName("sample", "foo", defaultModelConfig) - } - - @Test fun sampleWithAsserts() { - verifyKWSNodeByName("sampleWithAsserts", "a", defaultModelConfig) - } - - @Test fun newLinesInSamples() { - verifyKWSNodeByName("newLinesInSamples", "foo", defaultModelConfig) - } - - @Test fun newLinesInImportList() { - verifyKWSNodeByName("newLinesInImportList", "foo", defaultModelConfig) - } - - @Test fun returnTag() { - verifyKWSNodeByName("returnTag", "indexOf", defaultModelConfig) - } - - @Test fun overloadGroup() { - verifyKWSNodeByName("overloadGroup", "magic", defaultModelConfig) - } - - @Test fun dataTags() { - val module = buildMultiplePlatforms("dataTags") - verifyMultiplatformPackage(module, "dataTags") - } - - @Test fun dataTagsInGroupNode() { - val path = "dataTagsInGroupNode" - val module = buildMultiplePlatforms(path) - verifyModelOutput(module, ".html", "testdata/format/website-html/$path/multiplatform.kt") { model, output -> - buildPagesAndReadInto( - listOfNotNull(model.members.single().members.find { it.kind == NodeKind.GroupNode }), - output - ) - } - verifyMultiplatformPackage(module, path) - } - - private fun verifyKWSNodeByName(fileName: String, name: String, modelConfig: ModelConfig) { - verifyOutput( - "testdata/format/website-html/$fileName.kt", - ".html", - ModelConfig(analysisPlatform = modelConfig.analysisPlatform, format = "kotlin-website-html") - ) { model, output -> - buildPagesAndReadInto(model.members.single().members.filter { it.name == name }, output) - } - } - - private fun buildMultiplePlatforms(path: String): DocumentationModule { - val moduleName = "test" - val passConfiguration = PassConfigurationImpl( - noStdlibLink = true, - noJdkLink = true, - languageVersion = null, - apiVersion = null - ) - - val dokkaConfiguration = DokkaConfigurationImpl( - outputDir = "", - format = "kotlin-website-html", - generateIndexPages = false, - passesConfigurations = listOf( - passConfiguration - ) - - ) - - val module1 = DocumentationModule(moduleName) - appendDocumentation( - module1, dokkaConfiguration, passConfiguration, ModelConfig( - roots = arrayOf(contentRootFromPath("testdata/format/website-html/$path/jvm.kt")), - defaultPlatforms = listOf("JVM") - ) - ) - - val module2 = DocumentationModule(moduleName) - appendDocumentation( - module2, dokkaConfiguration, passConfiguration, ModelConfig( - roots = arrayOf(contentRootFromPath("testdata/format/website-html/$path/jre7.kt")), - defaultPlatforms = listOf("JVM", "JRE7") - ) - ) - - val module3 = DocumentationModule(moduleName) - appendDocumentation( - module3, dokkaConfiguration, passConfiguration, ModelConfig( - roots = arrayOf(contentRootFromPath("testdata/format/website-html/$path/js.kt")), - defaultPlatforms = listOf("JS") - ) - ) - - return DocumentationMerger(listOf(module1, module2, module3), DokkaConsoleLogger).merge() - } - - private fun verifyMultiplatformPackage(module: DocumentationModule, path: String) { - verifyModelOutput(module, ".package.html", "testdata/format/website-html/$path/multiplatform.kt") { model, output -> - buildPagesAndReadInto(model.members, output) - } - } - -} -class JsKotlinWebSiteHtmlFormatTest: BaseKotlinWebSiteHtmlFormatTest(Platform.js) - -class JvmKotlinWebSiteHtmlFormatTest: BaseKotlinWebSiteHtmlFormatTest(Platform.jvm) - -class CommonKotlinWebSiteHtmlFormatTest: BaseKotlinWebSiteHtmlFormatTest(Platform.common)
\ No newline at end of file diff --git a/core/src/test/kotlin/format/MarkdownFormatTest.kt b/core/src/test/kotlin/format/MarkdownFormatTest.kt deleted file mode 100644 index 4984e1d5..00000000 --- a/core/src/test/kotlin/format/MarkdownFormatTest.kt +++ /dev/null @@ -1,615 +0,0 @@ -package org.jetbrains.dokka.tests - -import org.jetbrains.dokka.* -import org.jetbrains.dokka.Generation.DocumentationMerger -import org.junit.Test - -abstract class BaseMarkdownFormatTest(val analysisPlatform: Platform): FileGeneratorTestCase() { - override val formatService = MarkdownFormatService(fileGenerator, KotlinLanguageService(), listOf()) - - protected val defaultModelConfig = ModelConfig(analysisPlatform = analysisPlatform) - - @Test fun emptyDescription() { - verifyMarkdownNode("emptyDescription", defaultModelConfig) - } - - @Test fun classWithCompanionObject() { - verifyMarkdownNode("classWithCompanionObject", defaultModelConfig) - } - - @Test fun annotations() { - verifyMarkdownNode("annotations", defaultModelConfig) - } - - @Test fun annotationClass() { - verifyMarkdownNode("annotationClass", ModelConfig(analysisPlatform = analysisPlatform, withKotlinRuntime = true)) - verifyMarkdownPackage("annotationClass", ModelConfig(analysisPlatform = analysisPlatform, withKotlinRuntime = true)) - } - - @Test fun enumClass() { - verifyOutput("testdata/format/enumClass.kt", ".md", defaultModelConfig) { model, output -> - buildPagesAndReadInto(model.members.single().members, output) - } - verifyOutput("testdata/format/enumClass.kt", ".value.md", defaultModelConfig) { model, output -> - val enumClassNode = model.members.single().members[0] - buildPagesAndReadInto( - enumClassNode.members.filter { it.name == "LOCAL_CONTINUE_AND_BREAK" }, - output - ) - } - } - - @Test fun varargsFunction() { - verifyMarkdownNode("varargsFunction", defaultModelConfig) - } - - @Test fun overridingFunction() { - verifyMarkdownNodes("overridingFunction", defaultModelConfig) { model-> - val classMembers = model.members.single().members.first { it.name == "D" }.members - classMembers.filter { it.name == "f" } - } - } - - @Test fun propertyVar() { - verifyMarkdownNode("propertyVar", defaultModelConfig) - } - - @Test fun functionWithDefaultParameter() { - verifyMarkdownNode("functionWithDefaultParameter", defaultModelConfig) - } - - @Test fun accessor() { - verifyMarkdownNodes("accessor", defaultModelConfig) { model -> - model.members.single().members.first { it.name == "C" }.members.filter { it.name == "x" } - } - } - - @Test fun paramTag() { - verifyMarkdownNode("paramTag", defaultModelConfig) - } - - @Test fun throwsTag() { - verifyMarkdownNode("throwsTag", defaultModelConfig) - } - - @Test fun typeParameterBounds() { - verifyMarkdownNode("typeParameterBounds", defaultModelConfig) - } - - @Test fun typeParameterVariance() { - verifyMarkdownNode("typeParameterVariance", defaultModelConfig) - } - - @Test fun typeProjectionVariance() { - verifyMarkdownNode("typeProjectionVariance", defaultModelConfig) - } - - @Test fun codeBlockNoHtmlEscape() { - verifyMarkdownNodeByName("codeBlockNoHtmlEscape", "hackTheArithmetic", defaultModelConfig) - } - - @Test fun companionObjectExtension() { - verifyMarkdownNodeByName("companionObjectExtension", "Foo", defaultModelConfig) - } - - @Test fun starProjection() { - verifyMarkdownNode("starProjection", defaultModelConfig) - } - - @Test fun extensionFunctionParameter() { - verifyMarkdownNode("extensionFunctionParameter", defaultModelConfig) - } - - @Test fun summarizeSignatures() { - verifyMarkdownNodes("summarizeSignatures", defaultModelConfig) { model -> model.members } - } - - @Test fun reifiedTypeParameter() { - verifyMarkdownNode("reifiedTypeParameter", ModelConfig(analysisPlatform = analysisPlatform, withKotlinRuntime = true)) - } - - @Test fun suspendInlineFunctionOrder() { - verifyMarkdownNode("suspendInlineFunction", ModelConfig(analysisPlatform = analysisPlatform, withKotlinRuntime = true)) - } - - @Test fun inlineSuspendFunctionOrderChanged() { - verifyMarkdownNode("inlineSuspendFunction", ModelConfig(analysisPlatform = analysisPlatform, withKotlinRuntime = true)) - } - - @Test fun annotatedTypeParameter() { - verifyMarkdownNode("annotatedTypeParameter", ModelConfig(analysisPlatform = analysisPlatform, withKotlinRuntime = true)) - } - - @Test fun inheritedMembers() { - verifyMarkdownNodeByName("inheritedMembers", "Bar", defaultModelConfig) - } - - @Test fun inheritedExtensions() { - verifyMarkdownNodeByName("inheritedExtensions", "Bar", defaultModelConfig) - } - - @Test fun genericInheritedExtensions() { - verifyMarkdownNodeByName("genericInheritedExtensions", "Bar", defaultModelConfig) - } - - @Test fun arrayAverage() { - verifyMarkdownNodeByName("arrayAverage", "XArray", defaultModelConfig) - } - - @Test fun multipleTypeParameterConstraints() { - verifyMarkdownNode("multipleTypeParameterConstraints", ModelConfig(analysisPlatform = analysisPlatform, withKotlinRuntime = true)) - } - - @Test fun inheritedCompanionObjectProperties() { - verifyMarkdownNodeByName("inheritedCompanionObjectProperties", "C", defaultModelConfig) - } - - @Test fun shadowedExtensionFunctions() { - verifyMarkdownNodeByName("shadowedExtensionFunctions", "Bar", defaultModelConfig) - } - - @Test fun inapplicableExtensionFunctions() { - verifyMarkdownNodeByName("inapplicableExtensionFunctions", "Bar", defaultModelConfig) - } - - @Test fun receiverParameterTypeBound() { - verifyMarkdownNodeByName("receiverParameterTypeBound", "Foo", defaultModelConfig) - } - - @Test fun extensionWithDocumentedReceiver() { - verifyMarkdownNodes("extensionWithDocumentedReceiver", defaultModelConfig) { model -> - model.members.single().members.single().members.filter { it.name == "fn" } - } - } - - @Test fun codeBlock() { - verifyMarkdownNode("codeBlock", defaultModelConfig) - } - - @Test fun exclInCodeBlock() { - verifyMarkdownNodeByName("exclInCodeBlock", "foo", defaultModelConfig) - } - - @Test fun backtickInCodeBlock() { - verifyMarkdownNodeByName("backtickInCodeBlock", "foo", defaultModelConfig) - } - - @Test fun qualifiedNameLink() { - verifyMarkdownNodeByName("qualifiedNameLink", "foo", - ModelConfig(analysisPlatform = analysisPlatform, withKotlinRuntime = true)) - } - - @Test fun functionalTypeWithNamedParameters() { - verifyMarkdownNode("functionalTypeWithNamedParameters", defaultModelConfig) - } - - @Test fun typeAliases() { - verifyMarkdownNode("typeAliases", defaultModelConfig) - verifyMarkdownPackage("typeAliases", defaultModelConfig) - } - - @Test fun sampleByShortName() { - verifyMarkdownNode("sampleByShortName", defaultModelConfig) - } - - - @Test fun suspendParam() { - verifyMarkdownNode("suspendParam", defaultModelConfig) - verifyMarkdownPackage("suspendParam", defaultModelConfig) - } - - @Test fun sinceKotlin() { - verifyMarkdownNode("sinceKotlin", defaultModelConfig) - verifyMarkdownPackage("sinceKotlin", defaultModelConfig) - } - - @Test fun sinceKotlinWide() { - verifyMarkdownPackage("sinceKotlinWide", defaultModelConfig) - } - - @Test fun dynamicType() { - verifyMarkdownNode("dynamicType", defaultModelConfig) - } - - @Test fun dynamicExtension() { - verifyMarkdownNodes("dynamicExtension", defaultModelConfig) { model -> model.members.single().members.filter { it.name == "Foo" } } - } - - @Test fun memberExtension() { - verifyMarkdownNodes("memberExtension", defaultModelConfig) { model -> model.members.single().members.filter { it.name == "Foo" } } - } - - @Test fun renderFunctionalTypeInParenthesisWhenItIsReceiver() { - verifyMarkdownNode("renderFunctionalTypeInParenthesisWhenItIsReceiver", defaultModelConfig) - } - - @Test fun multiplePlatforms() { - verifyMultiplatformPackage(buildMultiplePlatforms("multiplatform/simple"), "multiplatform/simple") - } - - @Test fun multiplePlatformsMerge() { - verifyMultiplatformPackage(buildMultiplePlatforms("multiplatform/merge"), "multiplatform/merge") - } - - @Test fun multiplePlatformsMergeMembers() { - val module = buildMultiplePlatforms("multiplatform/mergeMembers") - verifyModelOutput(module, ".md", "testdata/format/multiplatform/mergeMembers/foo.kt") { model, output -> - buildPagesAndReadInto(model.members.single().members, output) - } - } - - @Test fun multiplePlatformsOmitRedundant() { - val module = buildMultiplePlatforms("multiplatform/omitRedundant") - verifyModelOutput(module, ".md", "testdata/format/multiplatform/omitRedundant/foo.kt") { model, output -> - buildPagesAndReadInto(model.members.single().members, output) - } - } - - @Test fun multiplePlatformsImplied() { - val module = buildMultiplePlatforms("multiplatform/implied") - verifyModelOutput(module, ".md", "testdata/format/multiplatform/implied/foo.kt") { model, output -> - val service = MarkdownFormatService(fileGenerator, KotlinLanguageService(), listOf("JVM", "JS")) - fileGenerator.formatService = service - buildPagesAndReadInto(model.members.single().members, output) - } - } - - @Test fun packagePlatformsWithExtExtensions() { - val path = "multiplatform/packagePlatformsWithExtExtensions" - val module = DocumentationModule("test") - val passConfiguration = PassConfigurationImpl( - noStdlibLink = true, - noJdkLink = true, - languageVersion = null, - apiVersion = null - ) - - val dokkaConfiguration = DokkaConfigurationImpl( - outputDir = "", - format = "html", - generateIndexPages = false, - passesConfigurations = listOf( - passConfiguration - ) - ) - - appendDocumentation(module, dokkaConfiguration, passConfiguration, ModelConfig( - roots = arrayOf(contentRootFromPath("testdata/format/$path/jvm.kt")), - defaultPlatforms = listOf("JVM"), - withKotlinRuntime = true, - analysisPlatform = analysisPlatform - ) - ) - verifyMultiplatformIndex(module, path) - verifyMultiplatformPackage(module, path) - } - - @Test fun multiplePlatformsPackagePlatformFromMembers() { - val path = "multiplatform/packagePlatformsFromMembers" - val module = buildMultiplePlatforms(path) - verifyMultiplatformIndex(module, path) - verifyMultiplatformPackage(module, path) - } - - @Test fun multiplePlatformsGroupNode() { - val path = "multiplatform/groupNode" - val module = buildMultiplePlatforms(path) - verifyModelOutput(module, ".md", "testdata/format/$path/multiplatform.kt") { model, output -> - buildPagesAndReadInto( - listOfNotNull(model.members.single().members.find { it.kind == NodeKind.GroupNode }), - output - ) - } - verifyMultiplatformPackage(module, path) - } - - @Test fun multiplePlatformsBreadcrumbsInMemberOfMemberOfGroupNode() { - val path = "multiplatform/breadcrumbsInMemberOfMemberOfGroupNode" - val module = buildMultiplePlatforms(path) - verifyModelOutput(module, ".md", "testdata/format/$path/multiplatform.kt") { model, output -> - buildPagesAndReadInto( - listOfNotNull(model.members.single().members.find { it.kind == NodeKind.GroupNode }?.member(NodeKind.Function)), - output - ) - } - } - - @Test fun linksInEmphasis() { - verifyMarkdownNode("linksInEmphasis", defaultModelConfig) - } - - @Test fun linksInStrong() { - verifyMarkdownNode("linksInStrong", defaultModelConfig) - } - - @Test fun linksInHeaders() { - verifyMarkdownNode("linksInHeaders", defaultModelConfig) - } - - @Test fun tokensInEmphasis() { - verifyMarkdownNode("tokensInEmphasis", defaultModelConfig) - } - - @Test fun tokensInStrong() { - verifyMarkdownNode("tokensInStrong", defaultModelConfig) - } - - @Test fun tokensInHeaders() { - verifyMarkdownNode("tokensInHeaders", defaultModelConfig) - } - - @Test fun unorderedLists() { - verifyMarkdownNode("unorderedLists", defaultModelConfig) - } - - @Test fun nestedLists() { - verifyMarkdownNode("nestedLists", defaultModelConfig) - } - - @Test fun referenceLink() { - verifyMarkdownNode("referenceLink", defaultModelConfig) - } - - @Test fun externalReferenceLink() { - verifyMarkdownNode("externalReferenceLink", defaultModelConfig) - } - - @Test fun newlineInTableCell() { - verifyMarkdownPackage("newlineInTableCell", defaultModelConfig) - } - - @Test fun indentedCodeBlock() { - verifyMarkdownNode("indentedCodeBlock", defaultModelConfig) - } - - @Test fun receiverReference() { - verifyMarkdownNode("receiverReference", defaultModelConfig) - } - - @Test fun extensionScope() { - verifyMarkdownNodeByName("extensionScope", "test", defaultModelConfig) - } - - @Test fun typeParameterReference() { - verifyMarkdownNode("typeParameterReference", defaultModelConfig) - } - - @Test fun notPublishedTypeAliasAutoExpansion() { - verifyMarkdownNodeByName("notPublishedTypeAliasAutoExpansion", "foo", ModelConfig( - analysisPlatform = analysisPlatform, - includeNonPublic = false - )) - } - - @Test fun companionImplements() { - verifyMarkdownNodeByName("companionImplements", "Foo", defaultModelConfig) - } - - - private fun buildMultiplePlatforms(path: String): DocumentationModule { - val moduleName = "test" - val passConfiguration = PassConfigurationImpl( - noStdlibLink = true, - noJdkLink = true, - languageVersion = null, - apiVersion = null - ) - val dokkaConfiguration = DokkaConfigurationImpl( - outputDir = "", - format = "html", - generateIndexPages = false, - passesConfigurations = listOf( - passConfiguration - ) - - ) - val module1 = DocumentationModule(moduleName) - appendDocumentation( - module1, dokkaConfiguration, passConfiguration, ModelConfig( - roots = arrayOf(contentRootFromPath("testdata/format/$path/jvm.kt")), - defaultPlatforms = listOf("JVM"), - analysisPlatform = Platform.jvm - ) - ) - - val module2 = DocumentationModule(moduleName) - appendDocumentation( - module2, dokkaConfiguration, passConfiguration, ModelConfig( - roots = arrayOf(contentRootFromPath("testdata/format/$path/js.kt")), - defaultPlatforms = listOf("JS"), - analysisPlatform = Platform.js - ) - ) - - return DocumentationMerger(listOf(module1, module2), DokkaConsoleLogger).merge() - } - - private fun verifyMultiplatformPackage(module: DocumentationModule, path: String) { - verifyModelOutput(module, ".package.md", "testdata/format/$path/multiplatform.kt") { model, output -> - buildPagesAndReadInto(model.members, output) - } - } - - private fun verifyMultiplatformIndex(module: DocumentationModule, path: String) { - verifyModelOutput(module, ".md", "testdata/format/$path/multiplatform.index.kt") { - model, output -> - val service = MarkdownFormatService(fileGenerator, KotlinLanguageService(), listOf()) - fileGenerator.formatService = service - buildPagesAndReadInto(listOf(model), output) - } - } - - @Test fun blankLineInsideCodeBlock() { - verifyMarkdownNode("blankLineInsideCodeBlock", defaultModelConfig) - } - - protected fun verifyMarkdownPackage(fileName: String, modelConfig: ModelConfig = ModelConfig()) { - verifyOutput("testdata/format/$fileName.kt", ".package.md", modelConfig) { model, output -> - buildPagesAndReadInto(model.members, output) - } - } - - protected fun verifyMarkdownNode(fileName: String, modelConfig: ModelConfig = ModelConfig()) { - verifyMarkdownNodes(fileName, modelConfig) { model -> model.members.single().members } - } - - protected fun verifyMarkdownNodes( - fileName: String, - modelConfig: ModelConfig = ModelConfig(), - nodeFilter: (DocumentationModule) -> List<DocumentationNode> - ) { - verifyOutput( - "testdata/format/$fileName.kt", - ".md", - modelConfig - ) { model, output -> - buildPagesAndReadInto(nodeFilter(model), output) - } - } - - protected fun verifyJavaMarkdownNode(fileName: String, modelConfig: ModelConfig = ModelConfig()) { - verifyJavaMarkdownNodes(fileName, modelConfig) { model -> model.members.single().members } - } - - protected fun verifyJavaMarkdownNodes(fileName: String, modelConfig: ModelConfig = ModelConfig(), nodeFilter: (DocumentationModule) -> List<DocumentationNode>) { - verifyJavaOutput("testdata/format/$fileName.java", ".md", modelConfig) { model, output -> - buildPagesAndReadInto(nodeFilter(model), output) - } - } - - protected fun verifyMarkdownNodeByName( - fileName: String, - name: String, - modelConfig: ModelConfig = ModelConfig() - ) { - verifyMarkdownNodes(fileName, modelConfig) { model-> - val nodesWithName = model.members.single().members.filter { it.name == name } - if (nodesWithName.isEmpty()) { - throw IllegalArgumentException("Found no nodes named $name") - } - nodesWithName - } - } - - @Test fun nullableTypeParameterFunction() { - verifyMarkdownNode("nullableTypeParameterFunction", ModelConfig(analysisPlatform = analysisPlatform, withKotlinRuntime = true)) - } -} - -class JSMarkdownFormatTest: BaseMarkdownFormatTest(Platform.js) - -class JVMMarkdownFormatTest: BaseMarkdownFormatTest(Platform.jvm) { - - @Test - fun enumRef() { - verifyMarkdownNode("enumRef", defaultModelConfig) - } - - @Test - fun javaCodeLiteralTags() { - verifyJavaMarkdownNode("javaCodeLiteralTags", defaultModelConfig) - } - - @Test - fun nullability() { - verifyMarkdownNode("nullability", defaultModelConfig) - } - - @Test - fun exceptionClass() { - verifyMarkdownNode( - "exceptionClass", ModelConfig( - analysisPlatform = analysisPlatform, - withKotlinRuntime = true - ) - ) - verifyMarkdownPackage( - "exceptionClass", ModelConfig( - analysisPlatform = analysisPlatform, - withKotlinRuntime = true - ) - ) - } - - @Test - fun operatorOverloading() { - verifyMarkdownNodes("operatorOverloading", defaultModelConfig) { model-> - model.members.single().members.single { it.name == "C" }.members.filter { it.name == "plus" } - } - } - - @Test - fun extensions() { - verifyOutput("testdata/format/extensions.kt", ".package.md", defaultModelConfig) { model, output -> - buildPagesAndReadInto(model.members, output) - } - verifyOutput("testdata/format/extensions.kt", ".class.md", defaultModelConfig) { model, output -> - buildPagesAndReadInto(model.members.single().members, output) - } - } - - @Test - fun summarizeSignaturesProperty() { - verifyMarkdownNodes("summarizeSignaturesProperty", defaultModelConfig) { model -> model.members } - } - - @Test - fun javaSpaceInAuthor() { - verifyJavaMarkdownNode("javaSpaceInAuthor", defaultModelConfig) - } - - @Test - fun javaCodeInParam() { - verifyJavaMarkdownNodes("javaCodeInParam", defaultModelConfig) { - selectNodes(it) { - subgraphOf(RefKind.Member) - withKind(NodeKind.Function) - } - } - } - - @Test - fun annotationParams() { - verifyMarkdownNode("annotationParams", ModelConfig(analysisPlatform = analysisPlatform, withKotlinRuntime = true)) - } - - @Test fun inheritedLink() { - val filePath = "testdata/format/inheritedLink" - verifyOutput( - filePath, - ".md", - ModelConfig( - roots = arrayOf( - contentRootFromPath("$filePath.kt"), - contentRootFromPath("$filePath.1.kt") - ), - withJdk = true, - withKotlinRuntime = true, - includeNonPublic = false, - analysisPlatform = analysisPlatform - - ) - ) { model, output -> - buildPagesAndReadInto(model.members.single { it.name == "p2" }.members.single().members, output) - } - } - - @Test - fun javadocOrderedList() { - verifyJavaMarkdownNodes("javadocOrderedList", defaultModelConfig) { model -> - model.members.single().members.filter { it.name == "Bar" } - } - } - - @Test - fun jdkLinks() { - verifyMarkdownNode("jdkLinks", ModelConfig(withKotlinRuntime = true, analysisPlatform = analysisPlatform)) - } - - @Test - fun javadocHtml() { - verifyJavaMarkdownNode("javadocHtml", defaultModelConfig) - } -} - -class CommonMarkdownFormatTest: BaseMarkdownFormatTest(Platform.common)
\ No newline at end of file diff --git a/core/src/test/kotlin/format/PackageDocsTest.kt b/core/src/test/kotlin/format/PackageDocsTest.kt deleted file mode 100644 index 3ff5f123..00000000 --- a/core/src/test/kotlin/format/PackageDocsTest.kt +++ /dev/null @@ -1,92 +0,0 @@ -package org.jetbrains.dokka.tests.format - -import com.intellij.openapi.Disposable -import com.intellij.openapi.util.Disposer -import com.nhaarman.mockito_kotlin.any -import com.nhaarman.mockito_kotlin.doAnswer -import com.nhaarman.mockito_kotlin.eq -import com.nhaarman.mockito_kotlin.mock -import org.jetbrains.dokka.* -import org.jetbrains.dokka.tests.assertEqualsIgnoringSeparators -import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles -import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment -import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreProjectEnvironment -import org.jetbrains.kotlin.config.CompilerConfiguration -import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor -import org.junit.After -import org.junit.Assert.assertEquals -import org.junit.Before -import org.junit.Test -import java.io.File - -class PackageDocsTest { - - private lateinit var testDisposable: Disposable - - @Before - fun setup() { - testDisposable = Disposer.newDisposable() - } - - @After - fun cleanup() { - Disposer.dispose(testDisposable) - } - - fun createPackageDocs(linkResolver: DeclarationLinkResolver?): PackageDocs { - val environment = KotlinCoreEnvironment.createForTests(testDisposable, CompilerConfiguration.EMPTY, EnvironmentConfigFiles.JVM_CONFIG_FILES) - return PackageDocs(linkResolver, DokkaConsoleLogger, environment, mock(), mock()) - } - - @Test fun verifyParse() { - - val docs = createPackageDocs(null) - docs.parse("testdata/packagedocs/stdlib.md", emptyList()) - val packageContent = docs.packageContent["kotlin"]!! - val block = (packageContent.children.single() as ContentBlock).children.first() as ContentText - assertEquals("Core functions and types", block.text) - } - - @Test fun testReferenceLinksInPackageDocs() { - val mockLinkResolver = mock<DeclarationLinkResolver> { - val exampleCom = "https://example.com" - on { tryResolveContentLink(any(), eq(exampleCom)) } doAnswer { ContentExternalLink(exampleCom) } - } - - val mockPackageDescriptor = mock<PackageFragmentDescriptor> {} - - val docs = createPackageDocs(mockLinkResolver) - docs.parse("testdata/packagedocs/referenceLinks.md", listOf(mockPackageDescriptor)) - - checkMarkdownOutput(docs, "testdata/packagedocs/referenceLinks") - } - - fun checkMarkdownOutput(docs: PackageDocs, expectedFilePrefix: String) { - - val generator = FileGenerator(File("")) - - val out = StringBuilder() - val outputBuilder = MarkdownOutputBuilder( - out, - FileLocation(generator.root), - generator, - KotlinLanguageService(), - ".md", - emptyList() - ) - fun checkOutput(content: Content, filePostfix: String) { - outputBuilder.appendContent(content) - val expectedFile = File(expectedFilePrefix + filePostfix) - assertEqualsIgnoringSeparators(expectedFile, out.toString()) - out.setLength(0) - } - - checkOutput(docs.moduleContent, ".module.md") - - docs.packageContent.forEach { - (name, content) -> - checkOutput(content, ".$name.md") - } - - } -} diff --git a/core/src/test/kotlin/issues/IssuesTest.kt b/core/src/test/kotlin/issues/IssuesTest.kt deleted file mode 100644 index da5acd6e..00000000 --- a/core/src/test/kotlin/issues/IssuesTest.kt +++ /dev/null @@ -1,34 +0,0 @@ -package issues - -import org.jetbrains.dokka.DocumentationNode -import org.jetbrains.dokka.NodeKind -import org.jetbrains.dokka.Platform -import org.jetbrains.dokka.tests.ModelConfig -import org.jetbrains.dokka.tests.checkSourceExistsAndVerifyModel -import org.junit.Test -import kotlin.test.assertEquals - -abstract class BaseIssuesTest(val analysisPlatform: Platform) { - val defaultModelConfig = ModelConfig(analysisPlatform = analysisPlatform) - - @Test - fun errorClasses() { - checkSourceExistsAndVerifyModel("testdata/issues/errorClasses.kt", - modelConfig = ModelConfig(analysisPlatform = analysisPlatform, withJdk = true, withKotlinRuntime = true)) { model -> - val cls = model.members.single().members.single() - - fun DocumentationNode.returnType() = this.details.find { it.kind == NodeKind.Type }?.name - assertEquals("Test", cls.members[1].returnType()) - assertEquals("Test", cls.members[2].returnType()) - assertEquals("Test", cls.members[3].returnType()) - assertEquals("List", cls.members[4].returnType()) - assertEquals("String", cls.members[5].returnType()) - assertEquals("String", cls.members[6].returnType()) - assertEquals("String", cls.members[7].returnType()) - } - } -} - -class JSIssuesTest: BaseIssuesTest(Platform.js) -class JVMIssuesTest: BaseIssuesTest(Platform.jvm) -class CommonIssuesTest: BaseIssuesTest(Platform.common)
\ No newline at end of file diff --git a/core/src/test/kotlin/javadoc/JavadocTest.kt b/core/src/test/kotlin/javadoc/JavadocTest.kt deleted file mode 100644 index 1c4dd258..00000000 --- a/core/src/test/kotlin/javadoc/JavadocTest.kt +++ /dev/null @@ -1,333 +0,0 @@ -package org.jetbrains.dokka.javadoc - -import com.sun.javadoc.Tag -import com.sun.javadoc.Type -import org.jetbrains.dokka.DokkaConsoleLogger -import org.jetbrains.dokka.Platform -import org.jetbrains.dokka.tests.ModelConfig -import org.jetbrains.dokka.tests.assertEqualsIgnoringSeparators -import org.jetbrains.dokka.tests.checkSourceExistsAndVerifyModel -import org.junit.Assert.* -import org.junit.Test -import java.lang.reflect.Modifier.* - -class JavadocTest { - val defaultModelConfig = ModelConfig(analysisPlatform = Platform.jvm) - - @Test fun testTypes() { - verifyJavadoc("testdata/javadoc/types.kt", ModelConfig(analysisPlatform = Platform.jvm, withJdk = true)) { doc -> - val classDoc = doc.classNamed("foo.TypesKt")!! - val method = classDoc.methods().find { it.name() == "foo" }!! - - val type = method.returnType() - assertFalse(type.asClassDoc().isIncluded) - assertEquals("java.lang.String", type.qualifiedTypeName()) - assertEquals("java.lang.String", type.asClassDoc().qualifiedName()) - - val params = method.parameters() - assertTrue(params[0].type().isPrimitive) - assertFalse(params[1].type().asClassDoc().isIncluded) - } - } - - @Test fun testObject() { - verifyJavadoc("testdata/javadoc/obj.kt", defaultModelConfig) { doc -> - val classDoc = doc.classNamed("foo.O") - assertNotNull(classDoc) - - val companionDoc = doc.classNamed("foo.O.Companion") - assertNotNull(companionDoc) - - val pkgDoc = doc.packageNamed("foo")!! - assertEquals(2, pkgDoc.allClasses().size) - } - } - - @Test fun testException() { - verifyJavadoc( - "testdata/javadoc/exception.kt", - ModelConfig(analysisPlatform = Platform.jvm, withKotlinRuntime = true) - ) { doc -> - val classDoc = doc.classNamed("foo.MyException")!! - val member = classDoc.methods().find { it.name() == "foo" } - assertEquals(classDoc, member!!.containingClass()) - } - } - - @Test fun testByteArray() { - verifyJavadoc( - "testdata/javadoc/bytearr.kt", - ModelConfig(analysisPlatform = Platform.jvm, withKotlinRuntime = true) - ) { doc -> - val classDoc = doc.classNamed("foo.ByteArray")!! - assertNotNull(classDoc.asClassDoc()) - - val member = classDoc.methods().find { it.name() == "foo" }!! - assertEquals("[]", member.returnType().dimension()) - } - } - - @Test fun testStringArray() { - verifyJavadoc( - "testdata/javadoc/stringarr.kt", - ModelConfig(analysisPlatform = Platform.jvm, withKotlinRuntime = true) - ) { doc -> - val classDoc = doc.classNamed("foo.Foo")!! - assertNotNull(classDoc.asClassDoc()) - - val member = classDoc.methods().find { it.name() == "main" }!! - val paramType = member.parameters()[0].type() - assertNull(paramType.asParameterizedType()) - assertEquals("String[]", paramType.typeName()) - assertEquals("String", paramType.asClassDoc().name()) - } - } - - @Test fun testJvmName() { - verifyJavadoc( - "testdata/javadoc/jvmname.kt", - ModelConfig(analysisPlatform = Platform.jvm, withKotlinRuntime = true) - ) { doc -> - val classDoc = doc.classNamed("foo.Apple")!! - assertNotNull(classDoc.asClassDoc()) - - val member = classDoc.methods().find { it.name() == "_tree" } - assertNotNull(member) - } - } - - @Test fun testLinkWithParam() { - verifyJavadoc( - "testdata/javadoc/paramlink.kt", - ModelConfig(analysisPlatform = Platform.jvm, withKotlinRuntime = true) - ) { doc -> - val classDoc = doc.classNamed("demo.Apple")!! - assertNotNull(classDoc.asClassDoc()) - val tags = classDoc.inlineTags().filterIsInstance<SeeTagAdapter>() - assertEquals(2, tags.size) - val linkTag = tags[1] as SeeMethodTagAdapter - assertEquals("cutIntoPieces", linkTag.method.name()) - } - } - - @Test fun testInternalVisibility() { - verifyJavadoc( - "testdata/javadoc/internal.kt", - ModelConfig(analysisPlatform = Platform.jvm, withKotlinRuntime = true, includeNonPublic = false) - ) { doc -> - val classDoc = doc.classNamed("foo.Person")!! - val constructors = classDoc.constructors() - assertEquals(1, constructors.size) - assertEquals(1, constructors.single().parameters().size) - } - } - - @Test fun testSuppress() { - verifyJavadoc( - "testdata/javadoc/suppress.kt", - ModelConfig(analysisPlatform = Platform.jvm, withKotlinRuntime = true) - ) { doc -> - assertNull(doc.classNamed("Some")) - assertNull(doc.classNamed("SomeAgain")) - assertNull(doc.classNamed("Interface")) - val classSame = doc.classNamed("Same")!! - assertTrue(classSame.fields().isEmpty()) - assertTrue(classSame.methods().isEmpty()) - } - } - - @Test fun testTypeAliases() { - verifyJavadoc( - "testdata/javadoc/typealiases.kt", - ModelConfig(analysisPlatform = Platform.jvm, withKotlinRuntime = true) - ) { doc -> - assertNull(doc.classNamed("B")) - assertNull(doc.classNamed("D")) - - assertEquals("A", doc.classNamed("C")!!.superclass().name()) - val methodParamType = doc.classNamed("TypealiasesKt")!!.methods() - .find { it.name() == "some" }!!.parameters().first() - .type() - assertEquals("kotlin.jvm.functions.Function1", methodParamType.qualifiedTypeName()) - assertEquals("? super A, C", - methodParamType.asParameterizedType().typeArguments().joinToString(transform = Type::qualifiedTypeName) - ) - } - } - - @Test fun testKDocKeywordsOnMethod() { - verifyJavadoc( - "testdata/javadoc/kdocKeywordsOnMethod.kt", - ModelConfig(analysisPlatform = Platform.jvm, withKotlinRuntime = true) - ) { doc -> - val method = doc.classNamed("KdocKeywordsOnMethodKt")!!.methods()[0] - assertEquals("@return [ContentText(text=value of a)]", method.tags("return").first().text()) - assertEquals("@param a [ContentText(text=Some string)]", method.paramTags().first().text()) - assertEquals("@throws FireException [ContentText(text=in case of fire)]", method.throwsTags().first().text()) - } - } - - @Test - fun testBlankLineInsideCodeBlock() { - verifyJavadoc( - "testdata/javadoc/blankLineInsideCodeBlock.kt", - ModelConfig(analysisPlatform = Platform.jvm, withKotlinRuntime = true) - ) { doc -> - val method = doc.classNamed("BlankLineInsideCodeBlockKt")!!.methods()[0] - val text = method.inlineTags().joinToString(separator = "", transform = Tag::text) - assertEqualsIgnoringSeparators(""" - <p><code><pre> - This is a test - of Dokka's code blocks. - Here is a blank line. - - The previous line was blank. - </pre></code></p> - """.trimIndent(), text) - } - } - - @Test - fun testCompanionMethodReference() { - verifyJavadoc("testdata/javadoc/companionMethodReference.kt", defaultModelConfig) { doc -> - val classDoc = doc.classNamed("foo.TestClass")!! - val tag = classDoc.inlineTags().filterIsInstance<SeeMethodTagAdapter>().first() - assertEquals("TestClass.Companion", tag.referencedClassName()) - assertEquals("test", tag.referencedMemberName()) - } - } - - @Test - fun testVararg() { - verifyJavadoc("testdata/javadoc/vararg.kt") { doc -> - val classDoc = doc.classNamed("VarargKt")!! - val methods = classDoc.methods() - methods.single { it.name() == "vararg" }.let { method -> - assertTrue(method.isVarArgs) - assertEquals("int", method.parameters().last().typeName()) - } - methods.single { it.name() == "varargInMiddle" }.let { method -> - assertFalse(method.isVarArgs) - assertEquals("int[]", method.parameters()[1].typeName()) - } - } - } - - @Test - fun shouldHaveValidVisibilityModifiers() { - verifyJavadoc("testdata/javadoc/visibilityModifiers.kt", ModelConfig(analysisPlatform = Platform.jvm, withKotlinRuntime = true)) { doc -> - val classDoc = doc.classNamed("foo.Apple")!! - val methods = classDoc.methods() - - val getName = methods[0] - val setName = methods[1] - val getWeight = methods[2] - val setWeight = methods[3] - val getRating = methods[4] - val setRating = methods[5] - val getCode = methods[6] - val color = classDoc.fields()[3] - val code = classDoc.fields()[4] - - assertTrue(getName.isProtected) - assertEquals(PROTECTED, getName.modifierSpecifier()) - assertTrue(setName.isProtected) - assertEquals(PROTECTED, setName.modifierSpecifier()) - - assertTrue(getWeight.isPublic) - assertEquals(PUBLIC, getWeight.modifierSpecifier()) - assertTrue(setWeight.isPublic) - assertEquals(PUBLIC, setWeight.modifierSpecifier()) - - assertTrue(getRating.isPublic) - assertEquals(PUBLIC, getRating.modifierSpecifier()) - assertTrue(setRating.isPublic) - assertEquals(PUBLIC, setRating.modifierSpecifier()) - - assertTrue(getCode.isPublic) - assertEquals(PUBLIC or STATIC, getCode.modifierSpecifier()) - - assertEquals(methods.size, 7) - - assertTrue(color.isPrivate) - assertEquals(PRIVATE, color.modifierSpecifier()) - - assertTrue(code.isPrivate) - assertTrue(code.isStatic) - assertEquals(PRIVATE or STATIC, code.modifierSpecifier()) - } - } - - @Test - fun shouldNotHaveDuplicatedConstructorParameters() { - verifyJavadoc("testdata/javadoc/constructorParameters.kt") { doc -> - val classDoc = doc.classNamed("bar.Banana")!! - val paramTags = classDoc.constructors()[0].paramTags() - - assertEquals(3, paramTags.size) - } - } - - @Test fun shouldHaveAllFunctionMarkedAsDeprecated() { - verifyJavadoc("testdata/javadoc/deprecated.java") { doc -> - val classDoc = doc.classNamed("bar.Banana")!! - - classDoc.methods().forEach { method -> - assertTrue(method.tags().any { it.kind() == "deprecated" }) - } - } - } - - @Test - fun testDefaultNoArgConstructor() { - verifyJavadoc("testdata/javadoc/defaultNoArgConstructor.kt") { doc -> - val classDoc = doc.classNamed("foo.Peach")!! - assertTrue(classDoc.constructors()[0].tags()[2].text() == "print peach") - } - } - - @Test - fun testNoArgConstructor() { - verifyJavadoc("testdata/javadoc/noArgConstructor.kt") { doc -> - val classDoc = doc.classNamed("foo.Plum")!! - assertTrue(classDoc.constructors()[0].tags()[2].text() == "print plum") - } - } - - @Test - fun testArgumentReference() { - verifyJavadoc("testdata/javadoc/argumentReference.kt") { doc -> - val classDoc = doc.classNamed("ArgumentReferenceKt")!! - val method = classDoc.methods().first() - val tag = method.seeTags().first() - assertEquals("argNamedError", tag.referencedMemberName()) - assertEquals("error", tag.label()) - } - } - - @Test - fun functionParameters() { - verifyJavadoc("testdata/javadoc/functionParameters.java") { doc -> - val tags = doc.classNamed("bar.Foo")!!.methods().first().paramTags() - assertEquals((tags.first() as ParamTagAdapter).content.size, 1) - assertEquals((tags[1] as ParamTagAdapter).content.size, 1) - } - } - - private fun verifyJavadoc(name: String, - modelConfig: ModelConfig = ModelConfig(), - callback: (ModuleNodeAdapter) -> Unit) { - - checkSourceExistsAndVerifyModel(name, - ModelConfig( - analysisPlatform = Platform.jvm, - format = "javadoc", - withJdk = modelConfig.withJdk, - withKotlinRuntime = modelConfig.withKotlinRuntime, - includeNonPublic = modelConfig.includeNonPublic - )) { model -> - val doc = ModuleNodeAdapter(model, StandardReporter(DokkaConsoleLogger), "") - callback(doc) - } - } -} diff --git a/core/src/test/kotlin/markdown/ParserTest.kt b/core/src/test/kotlin/markdown/ParserTest.kt deleted file mode 100644 index b0ec68ff..00000000 --- a/core/src/test/kotlin/markdown/ParserTest.kt +++ /dev/null @@ -1,154 +0,0 @@ -package org.jetbrains.dokka.tests - -import org.junit.Test -import org.jetbrains.dokka.toTestString -import org.jetbrains.dokka.parseMarkdown -import org.junit.Ignore - -@Ignore public class ParserTest { - fun runTestFor(text : String) { - println("MD: ---") - println(text) - val markdownTree = parseMarkdown(text) - println("AST: ---") - println(markdownTree.toTestString()) - println() - } - - @Test fun text() { - runTestFor("text") - } - - @Test fun textWithSpaces() { - runTestFor("text and string") - } - - @Test fun textWithColon() { - runTestFor("text and string: cool!") - } - - @Test fun link() { - runTestFor("text [links]") - } - - @Test fun linkWithHref() { - runTestFor("text [links](http://google.com)") - } - - @Test fun multiline() { - runTestFor( - """ -text -and -string -""") - } - - @Test fun para() { - runTestFor( - """ -paragraph number -one - -paragraph -number two -""") - } - - @Test fun bulletList() { - runTestFor( - """* list item 1 -* list item 2 -""") - } - - @Test fun bulletListWithLines() { - runTestFor( - """ -* list item 1 - continue 1 -* list item 2 - continue 2 - """) - } - - @Test fun bulletListStrong() { - runTestFor( - """ -* list *item* 1 - continue 1 -* list *item* 2 - continue 2 - """) - } - - @Test fun emph() { - runTestFor("*text*") - } - - @Test fun underscoresNoEmph() { - runTestFor("text_with_underscores") - } - - @Test fun emphUnderscores() { - runTestFor("_text_") - } - - @Test fun singleStar() { - runTestFor("Embedded*Star") - } - - @Test fun directive() { - runTestFor("A text \${code with.another.value} with directive") - } - - @Test fun emphAndEmptySection() { - runTestFor("*text*\n\$sec:\n") - } - - @Test fun emphAndSection() { - runTestFor("*text*\n\$sec: some text\n") - } - - @Test fun emphAndBracedSection() { - runTestFor("Text *bold* text \n\${sec}: some text") - } - - @Test fun section() { - runTestFor( - "Plain text \n\$one: Summary \n\${two}: Description with *emphasis* \n\${An example of a section}: Example") - } - - @Test fun anonymousSection() { - runTestFor("Summary\n\nDescription\n") - } - - @Test fun specialSection() { - runTestFor( - "Plain text \n\$\$summary: Summary \n\${\$description}: Description \n\${\$An example of a section}: Example") - } - - @Test fun emptySection() { - runTestFor( - "Plain text \n\$summary:") - } - - val b = "$" - @Test fun pair() { - runTestFor( - """Represents a generic pair of two values. - -There is no meaning attached to values in this class, it can be used for any purpose. -Pair exhibits value semantics, i.e. two pairs are equal if both components are equal. - -An example of decomposing it into values: -${b}{code test.tuples.PairTest.pairMultiAssignment} - -${b}constructor: Creates new instance of [Pair] -${b}first: First value -${b}second: Second value"""" - ) - } - -} - diff --git a/core/src/test/kotlin/model/ClassTest.kt b/core/src/test/kotlin/model/ClassTest.kt deleted file mode 100644 index 35ec1d09..00000000 --- a/core/src/test/kotlin/model/ClassTest.kt +++ /dev/null @@ -1,318 +0,0 @@ -package org.jetbrains.dokka.tests - -import org.jetbrains.dokka.Content -import org.jetbrains.dokka.NodeKind -import org.jetbrains.dokka.Platform -import org.jetbrains.dokka.RefKind -import org.junit.Assert -import org.junit.Assert.assertEquals -import org.junit.Assert.assertTrue -import org.junit.Test - -abstract class BaseClassTest(val analysisPlatform: Platform) { - protected val defaultModelConfig = ModelConfig(analysisPlatform = analysisPlatform) - @Test fun emptyClass() { - checkSourceExistsAndVerifyModel("testdata/classes/emptyClass.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals(NodeKind.Class, kind) - assertEquals("Klass", name) - assertEquals(Content.Empty, content) - assertEquals("<init>", members.single().name) - assertTrue(links.none()) - } - } - } - - @Test fun emptyObject() { - checkSourceExistsAndVerifyModel("testdata/classes/emptyObject.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals(NodeKind.Object, kind) - assertEquals("Obj", name) - assertEquals(Content.Empty, content) - assertTrue(members.none()) - assertTrue(links.none()) - } - } - } - - @Test fun classWithConstructor() { - checkSourceExistsAndVerifyModel("testdata/classes/classWithConstructor.kt", defaultModelConfig) { model -> - with (model.members.single().members.single()) { - assertEquals(NodeKind.Class, kind) - assertEquals("Klass", name) - assertEquals(Content.Empty, content) - assertTrue(links.none()) - - assertEquals(1, members.count()) - with(members.elementAt(0)) { - assertEquals("<init>", name) - assertEquals(Content.Empty, content) - assertEquals(NodeKind.Constructor, kind) - assertEquals(3, details.count()) - assertEquals("public", details.elementAt(0).name) - with(details.elementAt(2)) { - assertEquals("name", name) - assertEquals(NodeKind.Parameter, kind) - assertEquals(Content.Empty, content) - assertEquals("String", detail(NodeKind.Type).name) - assertTrue(links.none()) - assertTrue(members.none()) - } - assertTrue(links.none()) - assertTrue(members.none()) - } - } - } - } - - @Test fun classWithFunction() { - checkSourceExistsAndVerifyModel("testdata/classes/classWithFunction.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals(NodeKind.Class, kind) - assertEquals("Klass", name) - assertEquals(Content.Empty, content) - assertTrue(links.none()) - - assertEquals(2, members.count()) - with(members.elementAt(0)) { - assertEquals("<init>", name) - assertEquals(Content.Empty, content) - assertEquals(NodeKind.Constructor, kind) - assertEquals(2, details.count()) - assertEquals("public", details.elementAt(0).name) - assertTrue(links.none()) - assertTrue(members.none()) - } - with(members.elementAt(1)) { - assertEquals("fn", name) - assertEquals(Content.Empty, content) - assertEquals(NodeKind.Function, kind) - assertEquals("Unit", detail(NodeKind.Type).name) - assertTrue(links.none()) - assertTrue(members.none()) - } - } - } - } - - @Test fun classWithProperty() { - checkSourceExistsAndVerifyModel("testdata/classes/classWithProperty.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals(NodeKind.Class, kind) - assertEquals("Klass", name) - assertEquals(Content.Empty, content) - assertTrue(links.none()) - - assertEquals(2, members.count()) - with(members.elementAt(0)) { - assertEquals("<init>", name) - assertEquals(Content.Empty, content) - assertEquals(NodeKind.Constructor, kind) - assertEquals(2, details.count()) - assertEquals("public", details.elementAt(0).name) - assertTrue(members.none()) - assertTrue(links.none()) - } - with(members.elementAt(1)) { - assertEquals("name", name) - assertEquals(Content.Empty, content) - assertEquals(NodeKind.Property, kind) - assertEquals("String", detail(NodeKind.Type).name) - assertTrue(members.none()) - assertTrue(links.none()) - } - } - } - } - - @Test fun classWithCompanionObject() { - checkSourceExistsAndVerifyModel("testdata/classes/classWithCompanionObject.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals(NodeKind.Class, kind) - assertEquals("Klass", name) - assertEquals(Content.Empty, content) - assertTrue(links.none()) - - assertEquals(3, members.count()) - with(members.elementAt(0)) { - assertEquals("<init>", name) - assertEquals(Content.Empty, content) - } - with(members.elementAt(1)) { - assertEquals("x", name) - assertEquals(NodeKind.CompanionObjectProperty, kind) - assertTrue(members.none()) - assertTrue(links.none()) - } - with(members.elementAt(2)) { - assertEquals("foo", name) - assertEquals(NodeKind.CompanionObjectFunction, kind) - assertTrue(members.none()) - assertTrue(links.none()) - } - } - } - } - - @Test fun dataClass() { - verifyPackageMember("testdata/classes/dataClass.kt", defaultModelConfig) { cls -> - val modifiers = cls.details(NodeKind.Modifier).map { it.name } - assertTrue("data" in modifiers) - } - } - - @Test fun sealedClass() { - verifyPackageMember("testdata/classes/sealedClass.kt", defaultModelConfig) { cls -> - val modifiers = cls.details(NodeKind.Modifier).map { it.name } - assertEquals(1, modifiers.count { it == "sealed" }) - } - } - - @Test fun annotatedClassWithAnnotationParameters() { - checkSourceExistsAndVerifyModel( - "testdata/classes/annotatedClassWithAnnotationParameters.kt", - defaultModelConfig - ) { model -> - with(model.members.single().members.single()) { - with(deprecation!!) { - assertEquals("Deprecated", name) - assertEquals(Content.Empty, content) - assertEquals(NodeKind.Annotation, kind) - assertEquals(1, details.count()) - with(details[0]) { - assertEquals(NodeKind.Parameter, kind) - assertEquals(1, details.count()) - with(details[0]) { - assertEquals(NodeKind.Value, kind) - assertEquals("\"should no longer be used\"", name) - } - } - } - } - } - } - - @Test fun notOpenClass() { - checkSourceExistsAndVerifyModel("testdata/classes/notOpenClass.kt", defaultModelConfig) { model -> - with(model.members.single().members.first { it.name == "D"}.members.first { it.name == "f" }) { - val modifiers = details(NodeKind.Modifier) - assertEquals(2, modifiers.size) - assertEquals("final", modifiers[1].name) - - val overrideReferences = references(RefKind.Override) - assertEquals(1, overrideReferences.size) - } - } - } - - @Test fun indirectOverride() { - checkSourceExistsAndVerifyModel("testdata/classes/indirectOverride.kt", defaultModelConfig) { model -> - with(model.members.single().members.first { it.name == "E"}.members.first { it.name == "foo" }) { - val modifiers = details(NodeKind.Modifier) - assertEquals(2, modifiers.size) - assertEquals("final", modifiers[1].name) - - val overrideReferences = references(RefKind.Override) - assertEquals(1, overrideReferences.size) - } - } - } - - @Test fun innerClass() { - verifyPackageMember("testdata/classes/innerClass.kt", defaultModelConfig) { cls -> - val innerClass = cls.members.single { it.name == "D" } - val modifiers = innerClass.details(NodeKind.Modifier) - assertEquals(3, modifiers.size) - assertEquals("inner", modifiers[2].name) - } - } - - @Test fun companionObjectExtension() { - checkSourceExistsAndVerifyModel("testdata/classes/companionObjectExtension.kt", defaultModelConfig) { model -> - val pkg = model.members.single() - val cls = pkg.members.single { it.name == "Foo" } - val extensions = cls.extensions.filter { it.kind == NodeKind.CompanionObjectProperty } - assertEquals(1, extensions.size) - } - } - - @Test fun secondaryConstructor() { - verifyPackageMember("testdata/classes/secondaryConstructor.kt", defaultModelConfig) { cls -> - val constructors = cls.members(NodeKind.Constructor) - assertEquals(2, constructors.size) - with (constructors.first { it.details(NodeKind.Parameter).size == 1}) { - assertEquals("<init>", name) - assertEquals("This is a secondary constructor.", summary.toTestString()) - } - } - } - - @Test fun sinceKotlin() { - checkSourceExistsAndVerifyModel("testdata/classes/sinceKotlin.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("1.1", sinceKotlin) - } - } - } - - @Test fun privateCompanionObject() { - checkSourceExistsAndVerifyModel( - "testdata/classes/privateCompanionObject.kt", - modelConfig = ModelConfig(analysisPlatform = analysisPlatform, includeNonPublic = false) - ) { model -> - with(model.members.single().members.single()) { - assertEquals(0, members(NodeKind.CompanionObjectFunction).size) - assertEquals(0, members(NodeKind.CompanionObjectProperty).size) - } - } - } - -} - -class JSClassTest: BaseClassTest(Platform.js) {} - -class JVMClassTest: BaseClassTest(Platform.jvm) { - @Test - fun annotatedClass() { - verifyPackageMember("testdata/classes/annotatedClass.kt", ModelConfig( - analysisPlatform = analysisPlatform, - withKotlinRuntime = true - ) - ) { cls -> - Assert.assertEquals(1, cls.annotations.count()) - with(cls.annotations[0]) { - Assert.assertEquals("Strictfp", name) - Assert.assertEquals(Content.Empty, content) - Assert.assertEquals(NodeKind.Annotation, kind) - } - } - } - - - @Test fun javaAnnotationClass() { - checkSourceExistsAndVerifyModel( - "testdata/classes/javaAnnotationClass.kt", - modelConfig = ModelConfig(analysisPlatform = analysisPlatform, withJdk = true) - ) { model -> - with(model.members.single().members.single()) { - Assert.assertEquals(1, annotations.count()) - with(annotations[0]) { - Assert.assertEquals("Retention", name) - Assert.assertEquals(Content.Empty, content) - Assert.assertEquals(NodeKind.Annotation, kind) - with(details[0]) { - Assert.assertEquals(NodeKind.Parameter, kind) - Assert.assertEquals(1, details.count()) - with(details[0]) { - Assert.assertEquals(NodeKind.Value, kind) - Assert.assertEquals("RetentionPolicy.SOURCE", name) - } - } - } - } - } - } - -} - -class CommonClassTest: BaseClassTest(Platform.common) {}
\ No newline at end of file diff --git a/core/src/test/kotlin/model/CommentTest.kt b/core/src/test/kotlin/model/CommentTest.kt deleted file mode 100644 index 08aa3572..00000000 --- a/core/src/test/kotlin/model/CommentTest.kt +++ /dev/null @@ -1,190 +0,0 @@ -package org.jetbrains.dokka.tests - -import org.junit.Test -import org.junit.Assert.* -import org.jetbrains.dokka.* - -abstract class BaseCommentTest(val analysisPlatform: Platform) { - val defaultModelConfig = ModelConfig(analysisPlatform = analysisPlatform) - @Test fun codeBlockComment() { - checkSourceExistsAndVerifyModel("testdata/comments/codeBlockComment.kt", defaultModelConfig) { model -> - with(model.members.single().members.first()) { - assertEqualsIgnoringSeparators("""[code lang=brainfuck] - | - |++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>. - | - |[/code] - |""".trimMargin(), - content.toTestString()) - } - with(model.members.single().members.last()) { - assertEqualsIgnoringSeparators("""[code] - | - |a + b - c - | - |[/code] - |""".trimMargin(), - content.toTestString()) - } - } - } - - @Test fun emptyDoc() { - checkSourceExistsAndVerifyModel("testdata/comments/emptyDoc.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals(Content.Empty, content) - } - } - } - - @Test fun emptyDocButComment() { - checkSourceExistsAndVerifyModel("testdata/comments/emptyDocButComment.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals(Content.Empty, content) - } - } - } - - @Test fun multilineDoc() { - checkSourceExistsAndVerifyModel("testdata/comments/multilineDoc.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("doc1", content.summary.toTestString()) - assertEquals("doc2\ndoc3", content.description.toTestString()) - } - } - } - - @Test fun multilineDocWithComment() { - checkSourceExistsAndVerifyModel("testdata/comments/multilineDocWithComment.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("doc1", content.summary.toTestString()) - assertEquals("doc2\ndoc3", content.description.toTestString()) - } - } - } - - @Test fun oneLineDoc() { - checkSourceExistsAndVerifyModel("testdata/comments/oneLineDoc.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("doc", content.summary.toTestString()) - } - } - } - - @Test fun oneLineDocWithComment() { - checkSourceExistsAndVerifyModel("testdata/comments/oneLineDocWithComment.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("doc", content.summary.toTestString()) - } - } - } - - @Test fun oneLineDocWithEmptyLine() { - checkSourceExistsAndVerifyModel("testdata/comments/oneLineDocWithEmptyLine.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("doc", content.summary.toTestString()) - } - } - } - - @Test fun emptySection() { - checkSourceExistsAndVerifyModel("testdata/comments/emptySection.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("Summary", content.summary.toTestString()) - assertEquals(1, content.sections.count()) - with (content.findSectionByTag("one")!!) { - assertEquals("One", tag) - assertEquals("", toTestString()) - } - } - } - } - - @Test fun quotes() { - checkSourceExistsAndVerifyModel("testdata/comments/quotes.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("it's \"useful\"", content.summary.toTestString()) - } - } - } - - @Test fun section1() { - checkSourceExistsAndVerifyModel("testdata/comments/section1.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("Summary", content.summary.toTestString()) - assertEquals(1, content.sections.count()) - with (content.findSectionByTag("one")!!) { - assertEquals("One", tag) - assertEquals("section one", toTestString()) - } - } - } - } - - @Test fun section2() { - checkSourceExistsAndVerifyModel("testdata/comments/section2.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("Summary", content.summary.toTestString()) - assertEquals(2, content.sections.count()) - with (content.findSectionByTag("one")!!) { - assertEquals("One", tag) - assertEquals("section one", toTestString()) - } - with (content.findSectionByTag("two")!!) { - assertEquals("Two", tag) - assertEquals("section two", toTestString()) - } - } - } - } - - @Test fun multilineSection() { - checkSourceExistsAndVerifyModel("testdata/comments/multilineSection.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("Summary", content.summary.toTestString()) - assertEquals(1, content.sections.count()) - with (content.findSectionByTag("one")!!) { - assertEquals("One", tag) - assertEquals("""line one -line two""", toTestString()) - } - } - } - } - - @Test fun directive() { - checkSourceExistsAndVerifyModel("testdata/comments/directive.kt", defaultModelConfig) { model -> - with(model.members.single().members.first()) { - assertEquals("Summary", content.summary.toTestString()) - with (content.description) { - assertEqualsIgnoringSeparators(""" - |[code lang=kotlin] - |if (true) { - | println(property) - |} - |[/code] - |[code lang=kotlin] - |if (true) { - | println(property) - |} - |[/code] - |[code lang=kotlin] - |if (true) { - | println(property) - |} - |[/code] - |[code lang=kotlin] - |if (true) { - | println(property) - |} - |[/code] - |""".trimMargin(), toTestString()) - } - } - } - } -} - -class JSCommentTest: BaseCommentTest(Platform.js) -class JVMCommentTest: BaseCommentTest(Platform.jvm) -class CommonCommentTest: BaseCommentTest(Platform.common)
\ No newline at end of file diff --git a/core/src/test/kotlin/model/DocumentableTest.kt b/core/src/test/kotlin/model/DocumentableTest.kt new file mode 100644 index 00000000..a801d549 --- /dev/null +++ b/core/src/test/kotlin/model/DocumentableTest.kt @@ -0,0 +1,108 @@ +package model + +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.properties.PropertyContainer +import kotlin.test.Test +import kotlin.test.assertEquals + +class DocumentableTest { + + @Test + fun withDescendents() { + val dClass = DClass( + dri = DRI(), + name = "TestClass", + constructors = emptyList(), + classlikes = emptyList(), + companion = null, + documentation = emptyMap(), + expectPresentInSet = null, + extra = PropertyContainer.empty(), + visibility = emptyMap(), + generics = emptyList(), + modifier = emptyMap(), + properties = emptyList(), + sources = emptyMap(), + sourceSets = emptySet(), + supertypes = emptyMap(), + functions = listOf( + DFunction( + dri = DRI(), + name = "function0", + documentation = emptyMap(), + expectPresentInSet = null, + extra = PropertyContainer.empty(), + visibility = emptyMap(), + generics = emptyList(), + modifier = emptyMap(), + sources = emptyMap(), + sourceSets = emptySet(), + type = Void, + receiver = null, + isConstructor = false, + parameters = listOf( + DParameter( + dri = DRI(), + name = "f0p0", + documentation = emptyMap(), + expectPresentInSet = null, + extra = PropertyContainer.empty(), + sourceSets = emptySet(), + type = Void + ), + DParameter( + dri = DRI(), + name = "f0p1", + documentation = emptyMap(), + expectPresentInSet = null, + extra = PropertyContainer.empty(), + sourceSets = emptySet(), + type = Void + ) + ) + ), + DFunction( + dri = DRI(), + name = "function1", + documentation = emptyMap(), + expectPresentInSet = null, + extra = PropertyContainer.empty(), + visibility = emptyMap(), + generics = emptyList(), + modifier = emptyMap(), + sources = emptyMap(), + sourceSets = emptySet(), + type = Void, + receiver = null, + isConstructor = false, + parameters = listOf( + DParameter( + dri = DRI(), + name = "f1p0", + documentation = emptyMap(), + expectPresentInSet = null, + extra = PropertyContainer.empty(), + sourceSets = emptySet(), + type = Void + ), + DParameter( + dri = DRI(), + name = "f1p1", + documentation = emptyMap(), + expectPresentInSet = null, + extra = PropertyContainer.empty(), + sourceSets = emptySet(), + type = Void + ) + ) + ) + ) + ) + + assertEquals( + listOf("TestClass", "function0", "f0p0", "f0p1", "function1", "f1p0", "f1p1"), + dClass.withDescendants().map { it.name }.toList() + ) + } +}
\ No newline at end of file diff --git a/core/src/test/kotlin/model/FunctionTest.kt b/core/src/test/kotlin/model/FunctionTest.kt deleted file mode 100644 index 4c6bfb74..00000000 --- a/core/src/test/kotlin/model/FunctionTest.kt +++ /dev/null @@ -1,281 +0,0 @@ -package org.jetbrains.dokka.tests - -import org.jetbrains.dokka.Content -import org.jetbrains.dokka.NodeKind -import org.jetbrains.dokka.Platform -import org.jetbrains.kotlin.analyzer.PlatformAnalysisParameters -import org.junit.Assert -import org.junit.Assert.assertEquals -import org.junit.Assert.assertTrue -import org.junit.Test - -abstract class BaseFunctionTest(val analysisPlatform: Platform) { - protected val defaultModelConfig = ModelConfig(analysisPlatform = analysisPlatform) - @Test fun function() { - checkSourceExistsAndVerifyModel("testdata/functions/function.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("fn", name) - assertEquals(NodeKind.Function, kind) - assertEquals("Function fn", content.summary.toTestString()) - assertEquals("Unit", detail(NodeKind.Type).name) - assertTrue(members.none()) - assertTrue(links.none()) - } - } - } - - @Test fun functionWithReceiver() { - checkSourceExistsAndVerifyModel("testdata/functions/functionWithReceiver.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("kotlin.String", name) - assertEquals(NodeKind.ExternalClass, kind) - assertEquals(2, members.count()) - with(members[0]) { - assertEquals("fn", name) - assertEquals(NodeKind.Function, kind) - assertEquals("Function with receiver", content.summary.toTestString()) - assertEquals("public", details.elementAt(0).name) - assertEquals("final", details.elementAt(1).name) - with(details.elementAt(3)) { - assertEquals("<this>", name) - assertEquals(NodeKind.Receiver, kind) - assertEquals(Content.Empty, content) - assertEquals("String", details.single().name) - assertTrue(members.none()) - assertTrue(links.none()) - } - assertEquals("Unit", details.elementAt(4).name) - assertTrue(members.none()) - assertTrue(links.none()) - } - with(members[1]) { - assertEquals("fn", name) - assertEquals(NodeKind.Function, kind) - } - } - } - } - - @Test fun genericFunction() { - checkSourceExistsAndVerifyModel("testdata/functions/genericFunction.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("generic", name) - assertEquals(NodeKind.Function, kind) - assertEquals("generic function", content.summary.toTestString()) - - assertEquals("private", details.elementAt(0).name) - assertEquals("final", details.elementAt(1).name) - with(details.elementAt(3)) { - assertEquals("T", name) - assertEquals(NodeKind.TypeParameter, kind) - assertEquals(Content.Empty, content) - assertTrue(details.none()) - assertTrue(members.none()) - assertTrue(links.none()) - } - assertEquals("Unit", details.elementAt(4).name) - - assertTrue(members.none()) - assertTrue(links.none()) - } - } - } - @Test fun genericFunctionWithConstraints() { - checkSourceExistsAndVerifyModel("testdata/functions/genericFunctionWithConstraints.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("generic", name) - assertEquals(NodeKind.Function, kind) - assertEquals("generic function", content.summary.toTestString()) - - val functionDetails = details - assertEquals("public", functionDetails.elementAt(0).name) - assertEquals("final", functionDetails.elementAt(1).name) - with(functionDetails.elementAt(3)) { - assertEquals("T", name) - assertEquals(NodeKind.TypeParameter, kind) - assertEquals(Content.Empty, content) - with(details.single()) { - assertEquals("R", name) - assertEquals(NodeKind.UpperBound, kind) - assertEquals(Content.Empty, content) - assertTrue(details.none()) - assertTrue(members.none()) - assertTrue(links.singleOrNull() == functionDetails.elementAt(4)) - } - assertTrue(members.none()) - assertTrue(links.none()) - } - with(functionDetails.elementAt(4)) { - assertEquals("R", name) - assertEquals(NodeKind.TypeParameter, kind) - assertEquals(Content.Empty, content) - assertTrue(members.none()) - assertTrue(links.none()) - } - assertEquals("Unit", functionDetails.elementAt(5).name) - - assertTrue(members.none()) - assertTrue(links.none()) - } - } - } - - @Test fun functionWithParams() { - checkSourceExistsAndVerifyModel("testdata/functions/functionWithParams.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("function", name) - assertEquals(NodeKind.Function, kind) - assertEquals("Multiline", content.summary.toTestString()) - assertEquals("""Function -Documentation""", content.description.toTestString()) - - assertEquals("public", details.elementAt(0).name) - assertEquals("final", details.elementAt(1).name) - with(details.elementAt(3)) { - assertEquals("x", name) - assertEquals(NodeKind.Parameter, kind) - assertEquals("parameter", content.summary.toTestString()) - assertEquals("Int", detail(NodeKind.Type).name) - assertTrue(members.none()) - assertTrue(links.none()) - } - assertEquals("Unit", details.elementAt(4).name) - assertTrue(members.none()) - assertTrue(links.none()) - } - } - } - - @Test fun functionWithNotDocumentedAnnotation() { - verifyPackageMember("testdata/functions/functionWithNotDocumentedAnnotation.kt", defaultModelConfig) { func -> - assertEquals(0, func.annotations.count()) - } - } - - @Test fun inlineFunction() { - verifyPackageMember("testdata/functions/inlineFunction.kt", defaultModelConfig) { func -> - val modifiers = func.details(NodeKind.Modifier).map { it.name } - assertTrue("inline" in modifiers) - } - } - - @Test fun suspendFunction() { - verifyPackageMember("testdata/functions/suspendFunction.kt") { func -> - val modifiers = func.details(NodeKind.Modifier).map { it.name } - assertTrue("suspend" in modifiers) - } - } - - @Test fun suspendInlineFunctionOrder() { - verifyPackageMember("testdata/functions/suspendInlineFunction.kt") { func -> - val modifiers = func.details(NodeKind.Modifier).map { it.name }.filter { - it == "suspend" || it == "inline" - } - - assertEquals(listOf("suspend", "inline"), modifiers) - } - } - - @Test fun inlineSuspendFunctionOrderChanged() { - verifyPackageMember("testdata/functions/inlineSuspendFunction.kt") { func -> - val modifiers = func.details(NodeKind.Modifier).map { it.name }.filter { - it == "suspend" || it == "inline" - } - - assertEquals(listOf("suspend", "inline"), modifiers) - } - } - - @Test fun functionWithAnnotatedParam() { - checkSourceExistsAndVerifyModel("testdata/functions/functionWithAnnotatedParam.kt", defaultModelConfig) { model -> - with(model.members.single().members.single { it.name == "function" }) { - with(details(NodeKind.Parameter).first()) { - assertEquals(1, annotations.count()) - with(annotations[0]) { - assertEquals("Fancy", name) - assertEquals(Content.Empty, content) - assertEquals(NodeKind.Annotation, kind) - } - } - } - } - } - - @Test fun functionWithNoinlineParam() { - verifyPackageMember("testdata/functions/functionWithNoinlineParam.kt", defaultModelConfig) { func -> - with(func.details(NodeKind.Parameter).first()) { - val modifiers = details(NodeKind.Modifier).map { it.name } - assertTrue("noinline" in modifiers) - } - } - } - - @Test fun annotatedFunctionWithAnnotationParameters() { - checkSourceExistsAndVerifyModel( - "testdata/functions/annotatedFunctionWithAnnotationParameters.kt", - defaultModelConfig - ) { model -> - with(model.members.single().members.single { it.name == "f" }) { - assertEquals(1, annotations.count()) - with(annotations[0]) { - assertEquals("Fancy", name) - assertEquals(Content.Empty, content) - assertEquals(NodeKind.Annotation, kind) - assertEquals(1, details.count()) - with(details[0]) { - assertEquals(NodeKind.Parameter, kind) - assertEquals(1, details.count()) - with(details[0]) { - assertEquals(NodeKind.Value, kind) - assertEquals("1", name) - } - } - } - } - } - } - - @Test fun functionWithDefaultParameter() { - checkSourceExistsAndVerifyModel("testdata/functions/functionWithDefaultParameter.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - with(details.elementAt(3)) { - val value = details(NodeKind.Value) - assertEquals(1, value.count()) - with(value[0]) { - assertEquals("\"\"", name) - } - } - } - } - } - - @Test fun sinceKotlin() { - checkSourceExistsAndVerifyModel("testdata/functions/sinceKotlin.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("1.1", sinceKotlin) - } - } - } -} - -class JSFunctionTest: BaseFunctionTest(Platform.js) - -class JVMFunctionTest: BaseFunctionTest(Platform.jvm) { - @Test - fun annotatedFunction() { - verifyPackageMember("testdata/functions/annotatedFunction.kt", ModelConfig( - analysisPlatform = Platform.jvm, - withKotlinRuntime = true - )) { func -> - Assert.assertEquals(1, func.annotations.count()) - with(func.annotations[0]) { - Assert.assertEquals("Strictfp", name) - Assert.assertEquals(Content.Empty, content) - Assert.assertEquals(NodeKind.Annotation, kind) - } - } - } - -} - -class CommonFunctionTest: BaseFunctionTest(Platform.common)
\ No newline at end of file diff --git a/core/src/test/kotlin/model/JavaTest.kt b/core/src/test/kotlin/model/JavaTest.kt deleted file mode 100644 index da9da624..00000000 --- a/core/src/test/kotlin/model/JavaTest.kt +++ /dev/null @@ -1,210 +0,0 @@ -package org.jetbrains.dokka.tests - -import org.jetbrains.dokka.NodeKind -import org.jetbrains.dokka.Platform -import org.jetbrains.dokka.RefKind -import org.junit.Assert.* -import org.junit.Ignore -import org.junit.Test - -public class JavaTest { - private val defaultModelConfig = ModelConfig(analysisPlatform = Platform.jvm) - @Test fun function() { - verifyJavaPackageMember("testdata/java/member.java", defaultModelConfig) { cls -> - assertEquals("Test", cls.name) - assertEquals(NodeKind.Class, cls.kind) - with(cls.members(NodeKind.Function).single()) { - assertEquals("fn", name) - assertEquals("Summary for Function", content.summary.toTestString().trimEnd()) - assertEquals(3, content.sections.size) - with(content.sections[0]) { - assertEquals("Parameters", tag) - assertEquals("name", subjectName) - assertEquals("render(Type:String,SUMMARY): is String parameter", toTestString()) - } - with(content.sections[1]) { - assertEquals("Parameters", tag) - assertEquals("value", subjectName) - assertEquals("render(Type:Int,SUMMARY): is int parameter", toTestString()) - } - with(content.sections[2]) { - assertEquals("Author", tag) - assertEquals("yole", toTestString()) - } - assertEquals("Unit", detail(NodeKind.Type).name) - assertTrue(members.none()) - assertTrue(links.none()) - with(details.first { it.name == "name" }) { - assertEquals(NodeKind.Parameter, kind) - assertEquals("String", detail(NodeKind.Type).name) - } - with(details.first { it.name == "value" }) { - assertEquals(NodeKind.Parameter, kind) - assertEquals("Int", detail(NodeKind.Type).name) - } - } - } - } - - @Test fun memberWithModifiers() { - verifyJavaPackageMember("testdata/java/memberWithModifiers.java", defaultModelConfig) { cls -> - val modifiers = cls.details(NodeKind.Modifier).map { it.name } - assertTrue("abstract" in modifiers) - with(cls.members.single { it.name == "fn" }) { - assertEquals("protected", details[0].name) - } - with(cls.members.single { it.name == "openFn" }) { - assertEquals("open", details[1].name) - } - } - } - - @Test fun superClass() { - verifyJavaPackageMember("testdata/java/superClass.java", defaultModelConfig) { cls -> - val superTypes = cls.details(NodeKind.Supertype) - assertEquals(2, superTypes.size) - assertEquals("Exception", superTypes[0].name) - assertEquals("Cloneable", superTypes[1].name) - } - } - - @Test fun arrayType() { - verifyJavaPackageMember("testdata/java/arrayType.java", defaultModelConfig) { cls -> - with(cls.members(NodeKind.Function).single()) { - val type = detail(NodeKind.Type) - assertEquals("Array", type.name) - assertEquals("String", type.detail(NodeKind.Type).name) - with(details(NodeKind.Parameter).single()) { - val parameterType = detail(NodeKind.Type) - assertEquals("IntArray", parameterType.name) - } - } - } - } - - @Test fun typeParameter() { - verifyJavaPackageMember("testdata/java/typeParameter.java", defaultModelConfig) { cls -> - val typeParameters = cls.details(NodeKind.TypeParameter) - with(typeParameters.single()) { - assertEquals("T", name) - with(detail(NodeKind.UpperBound)) { - assertEquals("Comparable", name) - assertEquals("T", detail(NodeKind.Type).name) - } - } - with(cls.members(NodeKind.Function).single()) { - val methodTypeParameters = details(NodeKind.TypeParameter) - with(methodTypeParameters.single()) { - assertEquals("E", name) - } - } - } - } - - @Test fun constructors() { - verifyJavaPackageMember("testdata/java/constructors.java", defaultModelConfig) { cls -> - val constructors = cls.members(NodeKind.Constructor) - assertEquals(2, constructors.size) - with(constructors[0]) { - assertEquals("<init>", name) - } - } - } - - @Test fun innerClass() { - verifyJavaPackageMember("testdata/java/InnerClass.java", defaultModelConfig) { cls -> - val innerClass = cls.members(NodeKind.Class).single() - assertEquals("D", innerClass.name) - } - } - - @Test fun varargs() { - verifyJavaPackageMember("testdata/java/varargs.java", defaultModelConfig) { cls -> - val fn = cls.members(NodeKind.Function).single() - val param = fn.detail(NodeKind.Parameter) - assertEquals("vararg", param.details(NodeKind.Modifier).first().name) - val psiType = param.detail(NodeKind.Type) - assertEquals("String", psiType.name) - assertTrue(psiType.details(NodeKind.Type).isEmpty()) - } - } - - @Test fun fields() { - verifyJavaPackageMember("testdata/java/field.java", defaultModelConfig) { cls -> - val i = cls.members(NodeKind.Property).single { it.name == "i" } - assertEquals("Int", i.detail(NodeKind.Type).name) - assertTrue("var" in i.details(NodeKind.Modifier).map { it.name }) - - val s = cls.members(NodeKind.Property).single { it.name == "s" } - assertEquals("String", s.detail(NodeKind.Type).name) - assertFalse("var" in s.details(NodeKind.Modifier).map { it.name }) - assertTrue("static" in s.details(NodeKind.Modifier).map { it.name }) - } - } - - @Test fun staticMethod() { - verifyJavaPackageMember("testdata/java/staticMethod.java", defaultModelConfig) { cls -> - val m = cls.members(NodeKind.Function).single { it.name == "foo" } - assertTrue("static" in m.details(NodeKind.Modifier).map { it.name }) - } - } - - /** - * `@suppress` not supported in Java! - * - * [Proposed tags](https://www.oracle.com/technetwork/java/javase/documentation/proposed-tags-142378.html) - * Proposed tag `@exclude` for it, but not supported yet - */ - @Ignore("@suppress not supported in Java!") @Test fun suppressTag() { - verifyJavaPackageMember("testdata/java/suppressTag.java", defaultModelConfig) { cls -> - assertEquals(1, cls.members(NodeKind.Function).size) - } - } - - @Test fun annotatedAnnotation() { - verifyJavaPackageMember("testdata/java/annotatedAnnotation.java", defaultModelConfig) { cls -> - assertEquals(1, cls.annotations.size) - with(cls.annotations[0]) { - assertEquals(1, details.count()) - with(details[0]) { - assertEquals(NodeKind.Parameter, kind) - assertEquals(1, details.count()) - with(details[0]) { - assertEquals(NodeKind.Value, kind) - assertEquals("[AnnotationTarget.FIELD, AnnotationTarget.CLASS, AnnotationTarget.FILE, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER]", name) - } - } - } - } - } - - @Test fun deprecation() { - verifyJavaPackageMember("testdata/java/deprecation.java", defaultModelConfig) { cls -> - val fn = cls.members(NodeKind.Function).single() - assertEquals("This should no longer be used", fn.deprecation!!.content.toTestString()) - } - } - - @Test fun javaLangObject() { - verifyJavaPackageMember("testdata/java/javaLangObject.java", defaultModelConfig) { cls -> - val fn = cls.members(NodeKind.Function).single() - assertEquals("Any", fn.detail(NodeKind.Type).name) - } - } - - @Test fun enumValues() { - verifyJavaPackageMember("testdata/java/enumValues.java", defaultModelConfig) { cls -> - val superTypes = cls.details(NodeKind.Supertype) - assertEquals(1, superTypes.size) - assertEquals(1, cls.members(NodeKind.EnumItem).size) - } - } - - @Test fun inheritorLinks() { - verifyJavaPackageMember("testdata/java/InheritorLinks.java", defaultModelConfig) { cls -> - val fooClass = cls.members.single { it.name == "Foo" } - val inheritors = fooClass.references(RefKind.Inheritor) - assertEquals(1, inheritors.size) - } - } -} diff --git a/core/src/test/kotlin/model/KotlinAsJavaTest.kt b/core/src/test/kotlin/model/KotlinAsJavaTest.kt deleted file mode 100644 index c29776be..00000000 --- a/core/src/test/kotlin/model/KotlinAsJavaTest.kt +++ /dev/null @@ -1,64 +0,0 @@ -package org.jetbrains.dokka.tests - -import org.jetbrains.dokka.DocumentationModule -import org.jetbrains.dokka.NodeKind -import org.jetbrains.dokka.Platform -import org.jetbrains.dokka.RefKind -import org.junit.Assert -import org.junit.Assert.assertEquals -import org.junit.Test - -class KotlinAsJavaTest { - @Test fun function() { - verifyModelAsJava("testdata/functions/function.kt") { model -> - val pkg = model.members.single() - - val facadeClass = pkg.members.single { it.name == "FunctionKt" } - assertEquals(NodeKind.Class, facadeClass.kind) - - val fn = facadeClass.members.single { it.kind == NodeKind.Function} - assertEquals("fn", fn.name) - } - } - - @Test fun propertyWithComment() { - verifyModelAsJava("testdata/comments/oneLineDoc.kt") { model -> - val facadeClass = model.members.single().members.single { it.name == "OneLineDocKt" } - val getter = facadeClass.members.single { it.name == "getProperty" } - assertEquals(NodeKind.Function, getter.kind) - assertEquals("doc", getter.content.summary.toTestString()) - } - } - - - @Test fun constants() { - verifyModelAsJava("testdata/java/constants.java") { cls -> - selectNodes(cls) { - subgraphOf(RefKind.Member) - matching { it.name == "constStr" || it.name == "refConst" } - }.forEach { - assertEquals("In $it", "\"some value\"", it.detailOrNull(NodeKind.Value)?.name) - } - val nullConstNode = selectNodes(cls) { - subgraphOf(RefKind.Member) - withName("nullConst") - }.single() - - Assert.assertNull(nullConstNode.detailOrNull(NodeKind.Value)) - } - } -} - -fun verifyModelAsJava(source: String, - modelConfig: ModelConfig = ModelConfig(), - verifier: (DocumentationModule) -> Unit) { - checkSourceExistsAndVerifyModel( - source, - modelConfig = ModelConfig( - withJdk = modelConfig.withJdk, - withKotlinRuntime = modelConfig.withKotlinRuntime, - format = "html-as-java", - analysisPlatform = Platform.jvm), - verifier = verifier - ) -} diff --git a/core/src/test/kotlin/model/LinkTest.kt b/core/src/test/kotlin/model/LinkTest.kt deleted file mode 100644 index 6526a4db..00000000 --- a/core/src/test/kotlin/model/LinkTest.kt +++ /dev/null @@ -1,91 +0,0 @@ -package org.jetbrains.dokka.tests - -import org.jetbrains.dokka.ContentBlock -import org.jetbrains.dokka.ContentNodeLazyLink -import org.jetbrains.dokka.NodeKind -import org.jetbrains.dokka.Platform -import org.junit.Assert.assertEquals -import org.junit.Test - -abstract class BaseLinkTest(val analysisPlatform: Platform) { - private val defaultModelConfig = ModelConfig(analysisPlatform = analysisPlatform) - @Test fun linkToSelf() { - checkSourceExistsAndVerifyModel("testdata/links/linkToSelf.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("Foo", name) - assertEquals(NodeKind.Class, kind) - assertEquals("This is link to [Foo -> Class:Foo]", content.summary.toTestString()) - } - } - } - - @Test fun linkToExternalSite() { - checkSourceExistsAndVerifyModel("testdata/links/linkToExternalSite.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("Foo", name) - assertEquals(NodeKind.Class, kind) - assertEquals("This is link to http://example.com/#example", content.summary.toTestString()) - } - } - } - - @Test fun linkToMember() { - checkSourceExistsAndVerifyModel("testdata/links/linkToMember.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("Foo", name) - assertEquals(NodeKind.Class, kind) - assertEquals("This is link to [member -> Function:member]", content.summary.toTestString()) - } - } - } - - @Test fun linkToConstantWithUnderscores() { - checkSourceExistsAndVerifyModel("testdata/links/linkToConstantWithUnderscores.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("Foo", name) - assertEquals(NodeKind.Class, kind) - assertEquals("This is link to [MY_CONSTANT_VALUE -> CompanionObjectProperty:MY_CONSTANT_VALUE]", content.summary.toTestString()) - } - } - } - - @Test fun linkToQualifiedMember() { - checkSourceExistsAndVerifyModel("testdata/links/linkToQualifiedMember.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("Foo", name) - assertEquals(NodeKind.Class, kind) - assertEquals("This is link to [Foo.member -> Function:member]", content.summary.toTestString()) - } - } - } - - @Test fun linkToParam() { - checkSourceExistsAndVerifyModel("testdata/links/linkToParam.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("Foo", name) - assertEquals(NodeKind.Function, kind) - assertEquals("This is link to [param -> Parameter:param]", content.summary.toTestString()) - } - } - } - - @Test fun linkToPackage() { - checkSourceExistsAndVerifyModel("testdata/links/linkToPackage.kt", defaultModelConfig) { model -> - val packageNode = model.members.single() - with(packageNode) { - assertEquals(this.name, "test.magic") - } - with(packageNode.members.single()) { - assertEquals("Magic", name) - assertEquals(NodeKind.Class, kind) - assertEquals("Basic implementations of [Magic -> Class:Magic] are located in [test.magic -> Package:test.magic] package", content.summary.toTestString()) - assertEquals(packageNode, ((this.content.summary as ContentBlock).children.filterIsInstance<ContentNodeLazyLink>().last()).lazyNode.invoke()) - } - } - } - -} - -class JSLinkTest: BaseLinkTest(Platform.js) -class JVMLinkTest: BaseLinkTest(Platform.jvm) -class CommonLinkTest: BaseLinkTest(Platform.common)
\ No newline at end of file diff --git a/core/src/test/kotlin/model/PackageTest.kt b/core/src/test/kotlin/model/PackageTest.kt deleted file mode 100644 index 47c88385..00000000 --- a/core/src/test/kotlin/model/PackageTest.kt +++ /dev/null @@ -1,136 +0,0 @@ -package org.jetbrains.dokka.tests - -import org.jetbrains.dokka.* -import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot -import org.junit.Assert.* -import org.junit.Test - -abstract class BasePackageTest(val analysisPlatform: Platform) { - val defaultModelConfig = ModelConfig(analysisPlatform = analysisPlatform) - @Test fun rootPackage() { - checkSourceExistsAndVerifyModel("testdata/packages/rootPackage.kt", defaultModelConfig) { model -> - with(model.members.single()) { - assertEquals(NodeKind.Package, kind) - assertEquals("", name) - assertEquals(Content.Empty, content) - assertTrue(details.none()) - assertTrue(members.none()) - assertTrue(links.none()) - } - } - } - - @Test fun simpleNamePackage() { - checkSourceExistsAndVerifyModel("testdata/packages/simpleNamePackage.kt", defaultModelConfig) { model -> - with(model.members.single()) { - assertEquals(NodeKind.Package, kind) - assertEquals("simple", name) - assertEquals(Content.Empty, content) - assertTrue(details.none()) - assertTrue(members.none()) - assertTrue(links.none()) - } - } - } - - @Test fun dottedNamePackage() { - checkSourceExistsAndVerifyModel("testdata/packages/dottedNamePackage.kt", defaultModelConfig) { model -> - with(model.members.single()) { - assertEquals(NodeKind.Package, kind) - assertEquals("dot.name", name) - assertEquals(Content.Empty, content) - assertTrue(details.none()) - assertTrue(members.none()) - assertTrue(links.none()) - } - } - } - - @Test fun multipleFiles() { - verifyModel( - ModelConfig( - roots = arrayOf( - KotlinSourceRoot("testdata/packages/dottedNamePackage.kt", false), - KotlinSourceRoot("testdata/packages/simpleNamePackage.kt", false) - ), - analysisPlatform = analysisPlatform - ) - ) { model -> - assertEquals(2, model.members.count()) - with(model.members.single { it.name == "simple" }) { - assertEquals(NodeKind.Package, kind) - assertEquals("simple", name) - assertEquals(Content.Empty, content) - assertTrue(details.none()) - assertTrue(members.none()) - assertTrue(links.none()) - } - with(model.members.single { it.name == "dot.name" }) { - assertEquals(NodeKind.Package, kind) - assertEquals(Content.Empty, content) - assertTrue(details.none()) - assertTrue(members.none()) - assertTrue(links.none()) - } - } - } - - @Test fun multipleFilesSamePackage() { - verifyModel( - ModelConfig( - roots = arrayOf( - KotlinSourceRoot("testdata/packages/simpleNamePackage.kt", false), - KotlinSourceRoot("testdata/packages/simpleNamePackage2.kt", false) - ), - analysisPlatform = analysisPlatform - ) - ) { model -> - assertEquals(1, model.members.count()) - with(model.members.elementAt(0)) { - assertEquals(NodeKind.Package, kind) - assertEquals("simple", name) - assertEquals(Content.Empty, content) - assertTrue(details.none()) - assertTrue(members.none()) - assertTrue(links.none()) - } - } - } - - @Test fun classAtPackageLevel() { - verifyModel( - ModelConfig( - roots = arrayOf(KotlinSourceRoot("testdata/packages/classInPackage.kt", false)), - analysisPlatform = analysisPlatform - ) - ) { model -> - assertEquals(1, model.members.count()) - with(model.members.elementAt(0)) { - assertEquals(NodeKind.Package, kind) - assertEquals("simple.name", name) - assertEquals(Content.Empty, content) - assertTrue(details.none()) - assertEquals(1, members.size) - assertTrue(links.none()) - } - } - } - - @Test fun suppressAtPackageLevel() { - verifyModel( - ModelConfig( - roots = arrayOf(KotlinSourceRoot("testdata/packages/classInPackage.kt", false)), - perPackageOptions = listOf( - PackageOptionsImpl(prefix = "simple.name", suppress = true) - ), - analysisPlatform = analysisPlatform - ) - ) { model -> - assertEquals(0, model.members.count()) - } - } -} - -class JSPackageTest : BasePackageTest(Platform.js) -class JVMPackageTest : BasePackageTest(Platform.jvm) -class CommonPackageTest : BasePackageTest(Platform.common)
\ No newline at end of file diff --git a/core/src/test/kotlin/model/PropertyTest.kt b/core/src/test/kotlin/model/PropertyTest.kt deleted file mode 100644 index 9f070862..00000000 --- a/core/src/test/kotlin/model/PropertyTest.kt +++ /dev/null @@ -1,129 +0,0 @@ -package org.jetbrains.dokka.tests - -import org.jetbrains.dokka.* -import org.junit.Assert -import org.junit.Assert.assertEquals -import org.junit.Assert.assertTrue -import org.junit.Test - -abstract class BasePropertyTest(val analysisPlatform: Platform) { - - protected val defaultModelConfig = ModelConfig(analysisPlatform = analysisPlatform) - @Test fun valueProperty() { - checkSourceExistsAndVerifyModel("testdata/properties/valueProperty.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("property", name) - assertEquals(NodeKind.Property, kind) - assertEquals(Content.Empty, content) - assertEquals("String", detail(NodeKind.Type).name) - assertTrue(members.none()) - assertTrue(links.none()) - } - } - } - - @Test fun variableProperty() { - checkSourceExistsAndVerifyModel("testdata/properties/variableProperty.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("property", name) - assertEquals(NodeKind.Property, kind) - assertEquals(Content.Empty, content) - assertEquals("String", detail(NodeKind.Type).name) - assertTrue(members.none()) - assertTrue(links.none()) - } - } - } - - @Test fun valuePropertyWithGetter() { - checkSourceExistsAndVerifyModel("testdata/properties/valuePropertyWithGetter.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("property", name) - assertEquals(NodeKind.Property, kind) - assertEquals(Content.Empty, content) - assertEquals("String", detail(NodeKind.Type).name) - assertTrue(links.none()) - assertTrue(members.none()) - } - } - } - - @Test fun variablePropertyWithAccessors() { - checkSourceExistsAndVerifyModel("testdata/properties/variablePropertyWithAccessors.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("property", name) - assertEquals(NodeKind.Property, kind) - assertEquals(Content.Empty, content) - assertEquals("String", detail(NodeKind.Type).name) - val modifiers = details(NodeKind.Modifier).map { it.name } - assertTrue("final" in modifiers) - assertTrue("public" in modifiers) - assertTrue("var" in modifiers) - assertTrue(links.none()) - assertTrue(members.none()) - } - } - } - - @Test fun propertyWithReceiver() { - checkSourceExistsAndVerifyModel( - "testdata/properties/propertyWithReceiver.kt", - defaultModelConfig - ) { model -> - with(model.members.single().members.single()) { - assertEquals("kotlin.String", name) - assertEquals(NodeKind.ExternalClass, kind) - with(members.single()) { - assertEquals("foobar", name) - assertEquals(NodeKind.Property, kind) - } - } - } - } - - @Test fun propertyOverride() { - checkSourceExistsAndVerifyModel("testdata/properties/propertyOverride.kt", defaultModelConfig) { model -> - with(model.members.single().members.single { it.name == "Bar" }.members.single { it.name == "xyzzy"}) { - assertEquals("xyzzy", name) - val override = references(RefKind.Override).single().to - assertEquals("xyzzy", override.name) - assertEquals("Foo", override.owner!!.name) - } - } - } - - @Test fun sinceKotlin() { - checkSourceExistsAndVerifyModel("testdata/properties/sinceKotlin.kt", defaultModelConfig) { model -> - with(model.members.single().members.single()) { - assertEquals("1.1", sinceKotlin) - } - } - } -} - -class JSPropertyTest: BasePropertyTest(Platform.js) {} - -class JVMPropertyTest : BasePropertyTest(Platform.jvm) { - @Test - fun annotatedProperty() { - checkSourceExistsAndVerifyModel( - "testdata/properties/annotatedProperty.kt", - modelConfig = ModelConfig( - analysisPlatform = analysisPlatform, - withKotlinRuntime = true - ) - ) { model -> - with(model.members.single().members.single()) { - Assert.assertEquals(1, annotations.count()) - with(annotations[0]) { - Assert.assertEquals("Strictfp", name) - Assert.assertEquals(Content.Empty, content) - Assert.assertEquals(NodeKind.Annotation, kind) - } - } - } - } - -} - -class CommonPropertyTest: BasePropertyTest(Platform.common) {}
\ No newline at end of file diff --git a/core/src/test/kotlin/model/SourceLinksErrorTest.kt b/core/src/test/kotlin/model/SourceLinksErrorTest.kt deleted file mode 100644 index 9812569d..00000000 --- a/core/src/test/kotlin/model/SourceLinksErrorTest.kt +++ /dev/null @@ -1,35 +0,0 @@ -package org.jetbrains.dokka.tests.model - -import org.jetbrains.dokka.NodeKind -import org.jetbrains.dokka.SourceLinkDefinitionImpl -import org.jetbrains.dokka.tests.ModelConfig -import org.jetbrains.dokka.tests.checkSourceExistsAndVerifyModel -import org.junit.Assert -import org.junit.Test -import java.io.File - -class SourceLinksErrorTest { - - @Test - fun absolutePath_notMatching() { - val sourceLink = SourceLinkDefinitionImpl(File("testdata/nonExisting").absolutePath, "http://...", null) - verifyNoSourceUrl(sourceLink) - } - - @Test - fun relativePath_notMatching() { - val sourceLink = SourceLinkDefinitionImpl("testdata/nonExisting", "http://...", null) - verifyNoSourceUrl(sourceLink) - } - - private fun verifyNoSourceUrl(sourceLink: SourceLinkDefinitionImpl) { - checkSourceExistsAndVerifyModel("testdata/sourceLinks/dummy.kt", ModelConfig(sourceLinks = listOf(sourceLink))) { model -> - with(model.members.single().members.single()) { - Assert.assertEquals("foo", name) - Assert.assertEquals(NodeKind.Function, kind) - Assert.assertTrue("should not have source urls", details(NodeKind.SourceUrl).isEmpty()) - } - } - } -} - diff --git a/core/src/test/kotlin/model/SourceLinksTest.kt b/core/src/test/kotlin/model/SourceLinksTest.kt deleted file mode 100644 index a4ba870c..00000000 --- a/core/src/test/kotlin/model/SourceLinksTest.kt +++ /dev/null @@ -1,75 +0,0 @@ -package org.jetbrains.dokka.tests.model - -import org.jetbrains.dokka.NodeKind -import org.jetbrains.dokka.SourceLinkDefinitionImpl -import org.jetbrains.dokka.tests.ModelConfig -import org.jetbrains.dokka.tests.checkSourceExistsAndVerifyModel -import org.junit.Assert -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.Parameterized -import java.io.File - -@RunWith(Parameterized::class) -class SourceLinksTest( - private val srcLink: String, - private val url: String, - private val lineSuffix: String?, - private val expectedUrl: String -) { - - @Test - fun test() { - val link = if(srcLink.contains(sourceLinks)){ - srcLink.substringBeforeLast(sourceLinks) + sourceLinks - } else { - srcLink.substringBeforeLast(testdata) + testdata - } - val sourceLink = SourceLinkDefinitionImpl(link, url, lineSuffix) - - checkSourceExistsAndVerifyModel(filePath, ModelConfig(sourceLinks = listOf(sourceLink))) { model -> - with(model.members.single().members.single()) { - Assert.assertEquals("foo", name) - Assert.assertEquals(NodeKind.Function, kind) - Assert.assertEquals(expectedUrl, details(NodeKind.SourceUrl).single().name) - } - } - } - - companion object { - private const val testdata = "testdata" - private const val sourceLinks = "sourceLinks" - private const val dummy = "dummy.kt" - private const val pathSuffix = "$sourceLinks/$dummy" - private const val filePath = "$testdata/$pathSuffix" - private const val url = "https://example.com" - - @Parameterized.Parameters(name = "{index}: {0}, {1}, {2} = {3}") - @JvmStatic - fun data(): Collection<Array<String?>> { - val longestPath = File(testdata).absolutePath.removeSuffix("/") + "/../$testdata/" - val maxLength = longestPath.length - val list = listOf( - arrayOf(File(testdata).absolutePath.removeSuffix("/"), "$url/$pathSuffix"), - arrayOf(File("$testdata/$sourceLinks").absolutePath.removeSuffix("/") + "/", "$url/$dummy"), - arrayOf(longestPath, "$url/$pathSuffix"), - - arrayOf(testdata, "$url/$pathSuffix"), - arrayOf("./$testdata", "$url/$pathSuffix"), - arrayOf("../core/$testdata", "$url/$pathSuffix"), - arrayOf("$testdata/$sourceLinks", "$url/$dummy"), - arrayOf("./$testdata/../$testdata/$sourceLinks", "$url/$dummy") - ) - - return list.map { arrayOf(it[0].padEnd(maxLength, '_'), url, null, it[1]) } + - listOf( - // check that it also works if url ends with / - arrayOf((File(testdata).absolutePath.removeSuffix("/") + "/").padEnd(maxLength, '_'), "$url/", null, "$url/$pathSuffix"), - // check if line suffix work - arrayOf<String?>("../core/../core/./$testdata/$sourceLinks/".padEnd(maxLength, '_'), "$url/", "#L", "$url/$dummy#L4") - ) - } - } - -} - diff --git a/core/src/test/kotlin/model/TypeAliasTest.kt b/core/src/test/kotlin/model/TypeAliasTest.kt deleted file mode 100644 index 71976dc3..00000000 --- a/core/src/test/kotlin/model/TypeAliasTest.kt +++ /dev/null @@ -1,132 +0,0 @@ -package org.jetbrains.dokka.tests - -import junit.framework.TestCase.assertEquals -import org.jetbrains.dokka.Content -import org.jetbrains.dokka.NodeKind -import org.junit.Test - -class TypeAliasTest { - @Test - fun testSimple() { - checkSourceExistsAndVerifyModel("testdata/typealias/simple.kt") { - val pkg = it.members.single() - with(pkg.member(NodeKind.TypeAlias)) { - assertEquals(Content.Empty, content) - assertEquals("B", name) - assertEquals("A", detail(NodeKind.TypeAliasUnderlyingType).name) - } - } - } - - @Test - fun testInheritanceFromTypeAlias() { - checkSourceExistsAndVerifyModel("testdata/typealias/inheritanceFromTypeAlias.kt") { - val pkg = it.members.single() - with(pkg.member(NodeKind.TypeAlias)) { - assertEquals(Content.Empty, content) - assertEquals("Same", name) - assertEquals("Some", detail(NodeKind.TypeAliasUnderlyingType).name) - assertEquals("My", inheritors.single().name) - } - with(pkg.members(NodeKind.Class).find { it.name == "My" }!!) { - assertEquals("Same", detail(NodeKind.Supertype).name) - } - } - } - - @Test - fun testChain() { - checkSourceExistsAndVerifyModel("testdata/typealias/chain.kt") { - val pkg = it.members.single() - with(pkg.members(NodeKind.TypeAlias).find { it.name == "B" }!!) { - assertEquals(Content.Empty, content) - assertEquals("A", detail(NodeKind.TypeAliasUnderlyingType).name) - } - with(pkg.members(NodeKind.TypeAlias).find { it.name == "C" }!!) { - assertEquals(Content.Empty, content) - assertEquals("B", detail(NodeKind.TypeAliasUnderlyingType).name) - } - } - } - - @Test - fun testDocumented() { - checkSourceExistsAndVerifyModel("testdata/typealias/documented.kt") { - val pkg = it.members.single() - with(pkg.member(NodeKind.TypeAlias)) { - assertEquals("Just typealias", content.summary.toTestString()) - } - } - } - - @Test - fun testDeprecated() { - checkSourceExistsAndVerifyModel("testdata/typealias/deprecated.kt") { - val pkg = it.members.single() - with(pkg.member(NodeKind.TypeAlias)) { - assertEquals(Content.Empty, content) - assertEquals("Deprecated", deprecation!!.name) - assertEquals("\"Not mainstream now\"", deprecation!!.detail(NodeKind.Parameter).detail(NodeKind.Value).name) - } - } - } - - @Test - fun testGeneric() { - checkSourceExistsAndVerifyModel("testdata/typealias/generic.kt") { - val pkg = it.members.single() - with(pkg.members(NodeKind.TypeAlias).find { it.name == "B" }!!) { - assertEquals("Any", detail(NodeKind.TypeAliasUnderlyingType).detail(NodeKind.Type).name) - } - - with(pkg.members(NodeKind.TypeAlias).find { it.name == "C" }!!) { - assertEquals("T", detail(NodeKind.TypeAliasUnderlyingType).detail(NodeKind.Type).name) - assertEquals("T", detail(NodeKind.TypeParameter).name) - } - } - } - - @Test - fun testFunctional() { - checkSourceExistsAndVerifyModel("testdata/typealias/functional.kt") { - val pkg = it.members.single() - with(pkg.member(NodeKind.TypeAlias)) { - assertEquals("Function1", detail(NodeKind.TypeAliasUnderlyingType).name) - val typeParams = detail(NodeKind.TypeAliasUnderlyingType).details(NodeKind.Type) - assertEquals("A", typeParams.first().name) - assertEquals("B", typeParams.last().name) - } - - with(pkg.member(NodeKind.Function)) { - assertEquals("Spell", detail(NodeKind.Parameter).detail(NodeKind.Type).name) - } - } - } - - @Test - fun testAsTypeBoundWithVariance() { - checkSourceExistsAndVerifyModel("testdata/typealias/asTypeBoundWithVariance.kt") { - val pkg = it.members.single() - with(pkg.members(NodeKind.Class).find { it.name == "C" }!!) { - val tParam = detail(NodeKind.TypeParameter) - assertEquals("out", tParam.detail(NodeKind.Modifier).name) - assertEquals("B", tParam.detail(NodeKind.Type).link(NodeKind.TypeAlias).name) - } - - with(pkg.members(NodeKind.Class).find { it.name == "D" }!!) { - val tParam = detail(NodeKind.TypeParameter) - assertEquals("in", tParam.detail(NodeKind.Modifier).name) - assertEquals("B", tParam.detail(NodeKind.Type).link(NodeKind.TypeAlias).name) - } - } - } - - @Test - fun sinceKotlin() { - checkSourceExistsAndVerifyModel("testdata/typealias/sinceKotlin.kt") { model -> - with(model.members.single().members.single()) { - assertEquals("1.1", sinceKotlin) - } - } - } -}
\ No newline at end of file diff --git a/core/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/core/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker deleted file mode 100644 index ca6ee9ce..00000000 --- a/core/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker +++ /dev/null @@ -1 +0,0 @@ -mock-maker-inline
\ No newline at end of file diff --git a/diagrams/documentationNodeStructure.png b/diagrams/documentationNodeStructure.png Binary files differnew file mode 100644 index 00000000..9eb3db30 --- /dev/null +++ b/diagrams/documentationNodeStructure.png diff --git a/diagrams/documentationNodeStructure2.png b/diagrams/documentationNodeStructure2.png Binary files differnew file mode 100644 index 00000000..a9c7632f --- /dev/null +++ b/diagrams/documentationNodeStructure2.png diff --git a/diagrams/pageNodeAndContentNodeStructure.png b/diagrams/pageNodeAndContentNodeStructure.png Binary files differnew file mode 100644 index 00000000..921d496e --- /dev/null +++ b/diagrams/pageNodeAndContentNodeStructure.png diff --git a/diagrams/pageNodeStructure.png b/diagrams/pageNodeStructure.png Binary files differnew file mode 100644 index 00000000..23c12aaf --- /dev/null +++ b/diagrams/pageNodeStructure.png diff --git a/gradle.properties b/gradle.properties index e4745ed8..81b55546 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,25 +1,17 @@ -dokka_version_base=0.10.1 +dokka_version_base=1.4-M3 dokka_publication_channel=dokka # Kotlin compiler and plugin -bundled_kotlin_compiler_version=1.3.61 -kotlin_version=1.3.61 -kotlin_plugin_version=1.3.61-release-180 -idea_version=192.5728.98 -kotlin_for_gradle_runtime_version=1.3.61 -language_version=1.3 +kotlin_version=1.4-M3 +kotlin_plugin_version=1.4-M3-release-207 +coroutines_version=1.3.7-1.4-M3 -ant_version=1.9.6 +idea_version=193.6494.35 +language_version=1.4 -# Maven plugin dependencies -maven_version=3.5.0 -maven_archiver_version=2.5 -plexus_utils_version=3.0.22 -plexus_archiver_version=3.4 -maven_plugin_tools_version=3.5.2 +# Code style +kotlin.code.style=official -# For CI -mvn=mvn - -# For serialization -gson_version=2.8.5
\ No newline at end of file +# Gradle settings +org.gradle.jvmargs=-Xmx4g +org.gradle.parallel=true diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar Binary files differindex 28861d27..f3d88b1c 100644 --- a/gradle/wrapper/gradle-wrapper.jar +++ b/gradle/wrapper/gradle-wrapper.jar diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f73107db..bb8b2fc2 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists @@ -1,5 +1,21 @@ #!/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 @@ -28,7 +44,7 @@ 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="" +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" @@ -109,8 +125,8 @@ if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then +# 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"` @@ -138,19 +154,19 @@ if $cygwin ; then else eval `echo args$i`="\"$arg\"" fi - i=$((i+1)) + 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" ;; + 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 @@ -159,14 +175,9 @@ save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } -APP_ARGS=$(save "$@") +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" -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index e95643d6..24467a14 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,3 +1,19 @@ +@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
@@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME%
@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=
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
diff --git a/integration-tests/build.gradle.kts b/integration-tests/build.gradle.kts new file mode 100644 index 00000000..fb987c36 --- /dev/null +++ b/integration-tests/build.gradle.kts @@ -0,0 +1,43 @@ +subprojects { + sourceSets { + create("integrationTest") { + compileClasspath += sourceSets.main.get().output + runtimeClasspath += sourceSets.main.get().output + } + } + + configurations.getByName("integrationTestImplementation") { + extendsFrom(configurations.implementation.get()) + } + + configurations.getByName("integrationTestRuntimeOnly") { + extendsFrom(configurations.runtimeOnly.get()) + } + + dependencies { + implementation(project(":integration-tests")) + } + + val integrationTest by tasks.register<Test>("integrationTest") { + maxHeapSize = "2G" + description = "Runs integration tests." + group = "verification" + + testClassesDirs = sourceSets["integrationTest"].output.classesDirs + classpath = sourceSets["integrationTest"].runtimeClasspath + + useJUnit() + } + + tasks.check { + dependsOn(integrationTest) + } +} + +dependencies { + implementation(kotlin("stdlib")) + implementation(kotlin("test-junit")) + val coroutines_version: String by project + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version") + implementation("org.jsoup:jsoup:1.12.1") +} diff --git a/integration-tests/cli/build.gradle.kts b/integration-tests/cli/build.gradle.kts new file mode 100644 index 00000000..d9961f8f --- /dev/null +++ b/integration-tests/cli/build.gradle.kts @@ -0,0 +1,41 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar + + +plugins { + id("com.github.johnrengelman.shadow") +} + +val dokka_version: String by project +evaluationDependsOn(":runners:cli") +evaluationDependsOn(":plugins:base") + +dependencies { + implementation(kotlin("stdlib")) + implementation(kotlin("test-junit")) +} + +/* Create a fat base plugin jar for cli tests */ +val basePluginShadow: Configuration by configurations.creating { + attributes { + attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage::class.java, "java-runtime")) + } +} + +dependencies { + basePluginShadow(project(":plugins:base")) +} +val basePluginShadowJar by tasks.register("basePluginShadowJar", ShadowJar::class) { + configurations = listOf(basePluginShadow) + archiveFileName.set("fat-base-plugin-$dokka_version.jar") + archiveClassifier.set("") +} + +tasks.integrationTest { + inputs.dir(file("projects")) + val cliJar = tasks.getByPath(":runners:cli:shadowJar") as ShadowJar + environment("CLI_JAR_PATH", cliJar.archiveFile.get()) + environment("BASE_PLUGIN_JAR_PATH", basePluginShadowJar.archiveFile.get()) + dependsOn(cliJar) + dependsOn(basePluginShadowJar) +} + diff --git a/integration-tests/cli/projects/it-cli/src/main/java/it/basic/java/SampleJavaClass.java b/integration-tests/cli/projects/it-cli/src/main/java/it/basic/java/SampleJavaClass.java new file mode 100644 index 00000000..23b0202c --- /dev/null +++ b/integration-tests/cli/projects/it-cli/src/main/java/it/basic/java/SampleJavaClass.java @@ -0,0 +1,17 @@ +package it.basic.java; + +import it.basic.PublicClass; + +/** + * This class is, unlike {@link PublicClass}, written in Java + */ +@SuppressWarnings("unused") +public class SampleJavaClass { + + /** + * @return Empty instance of {@link PublicClass} + */ + public PublicClass publicDocumentedFunction() { + return new PublicClass(); + } +} diff --git a/integration-tests/cli/projects/it-cli/src/main/kotlin/it/basic/PublicClass.kt b/integration-tests/cli/projects/it-cli/src/main/kotlin/it/basic/PublicClass.kt new file mode 100644 index 00000000..71bc7e63 --- /dev/null +++ b/integration-tests/cli/projects/it-cli/src/main/kotlin/it/basic/PublicClass.kt @@ -0,0 +1,48 @@ +@file:Suppress("unused") + +package it.basic + +class PublicClass { + /** + * This function is public and documented + */ + fun publicDocumentedFunction(): String = "" + + fun publicUndocumentedFunction(): String = "" + + /** + * This function is internal and documented + */ + internal fun internalDocumentedFunction(): String = "" + + internal fun internalUndocumentedFunction(): String = "" + + /** + * This function is private and documented + */ + private fun privateDocumentedFunction(): String = "" + + private fun privateUndocumentedFunction(): String = "" + + + /** + * This property is public and documented + */ + val publicDocumentedProperty: Int = 0 + + val publicUndocumentedProperty: Int = 0 + + /** + * This property internal and documented + */ + val internalDocumentedProperty: Int = 0 + + val internalUndocumentedProperty: Int = 0 + + /** + * This property private and documented + */ + private val privateDocumentedProperty: Int = 0 + + private val privateUndocumentedProperty: Int = 0 +} diff --git a/integration-tests/cli/src/integrationTest/kotlin/org/jetbrains/dokka/it/cli/CliIntegrationTest.kt b/integration-tests/cli/src/integrationTest/kotlin/org/jetbrains/dokka/it/cli/CliIntegrationTest.kt new file mode 100644 index 00000000..cfa752d6 --- /dev/null +++ b/integration-tests/cli/src/integrationTest/kotlin/org/jetbrains/dokka/it/cli/CliIntegrationTest.kt @@ -0,0 +1,89 @@ +package org.jetbrains.dokka.it.cli + +import org.jetbrains.dokka.it.awaitProcessResult +import java.io.File +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class CliIntegrationTest : AbstractCliIntegrationTest() { + + @BeforeTest + fun copyProject() { + val templateProjectDir = File("projects", "it-cli") + templateProjectDir.copyRecursively(projectDir) + } + + @Test + fun runHelp() { + val process = ProcessBuilder("java", "-jar", cliJarFile.path, "-h") + .redirectErrorStream(true) + .start() + + val result = process.awaitProcessResult() + assertEquals(0, result.exitCode, "Expected exitCode 0 (Success)") + assertTrue("Usage: " in result.output) + } + + @Test + fun runCli() { + val dokkaOutputDir = File(projectDir, "output") + assertTrue(dokkaOutputDir.mkdirs()) + val process = ProcessBuilder( + "java", "-jar", cliJarFile.path, + "-outputDir", dokkaOutputDir.path, + "-pluginsClasspath", basePluginJarFile.path, + "-sourceSet", + buildString { + append(" -moduleName it-cli") + append(" -moduleDisplayName CLI-Example") + append(" -sourceSetName cliMain") + append(" -src ${File(projectDir, "src").path}") + append(" -jdkVersion 8") + append(" -analysisPlatform jvm") + append(" -reportUndocumented") + append(" -skipDeprecated") + } + ) + .redirectErrorStream(true) + .start() + + val result = process.awaitProcessResult() + assertEquals(0, result.exitCode, "Expected exitCode 0 (Success)") + + val extensionLoadedRegex = Regex("""Extension: org\.jetbrains\.dokka\.base\.DokkaBase""") + val amountOfExtensionsLoaded = extensionLoadedRegex.findAll(result.output).count() + + assertTrue( + amountOfExtensionsLoaded > 10, + "Expected more than 10 extensions being present (found $amountOfExtensionsLoaded)" + ) + + val undocumentedReportRegex = Regex("""Undocumented:""") + val amountOfUndocumentedReports = undocumentedReportRegex.findAll(result.output).count() + assertTrue( + amountOfUndocumentedReports > 0, + "Expected at least one report of undocumented code (found $amountOfUndocumentedReports)" + ) + + assertTrue(dokkaOutputDir.isDirectory, "Missing dokka output directory") + + val imagesDir = File(dokkaOutputDir, "images") + assertTrue(imagesDir.isDirectory, "Missing images directory") + + val scriptsDir = File(dokkaOutputDir, "scripts") + assertTrue(scriptsDir.isDirectory, "Missing scripts directory") + + val stylesDir = File(dokkaOutputDir, "styles") + assertTrue(stylesDir.isDirectory, "Missing styles directory") + + val navigationHtml = File(dokkaOutputDir, "navigation.html") + assertTrue(navigationHtml.isFile, "Missing navigation.html") + + projectDir.allHtmlFiles().forEach { file -> + assertContainsNoErrorClass(file) + assertNoUnresolvedLInks(file) + } + } +} diff --git a/integration-tests/cli/src/main/kotlin/org/jetbrains/dokka/it/cli/AbstractCliIntegrationTest.kt b/integration-tests/cli/src/main/kotlin/org/jetbrains/dokka/it/cli/AbstractCliIntegrationTest.kt new file mode 100644 index 00000000..7f6f9433 --- /dev/null +++ b/integration-tests/cli/src/main/kotlin/org/jetbrains/dokka/it/cli/AbstractCliIntegrationTest.kt @@ -0,0 +1,36 @@ +package org.jetbrains.dokka.it.cli + +import org.jetbrains.dokka.it.AbstractIntegrationTest +import java.io.File +import kotlin.test.BeforeTest +import kotlin.test.assertTrue + +abstract class AbstractCliIntegrationTest : AbstractIntegrationTest() { + + protected val cliJarFile: File by lazy { + File(temporaryTestFolder.root, "dokka.jar") + } + + protected val basePluginJarFile: File by lazy { + File(temporaryTestFolder.root, "base-plugin.jar") + } + + @BeforeTest + fun copyJarFiles() { + val cliJarPathEnvironmentKey = "CLI_JAR_PATH" + val cliJarFile = File(System.getenv(cliJarPathEnvironmentKey)) + assertTrue( + cliJarFile.exists() && cliJarFile.isFile, + "Missing path to CLI jar System.getenv($cliJarPathEnvironmentKey)" + ) + cliJarFile.copyTo(this.cliJarFile) + + val basePluginPathEnvironmentKey = "BASE_PLUGIN_JAR_PATH" + val basePluginJarFile = File(System.getenv(basePluginPathEnvironmentKey)) + assertTrue( + basePluginJarFile.exists() && basePluginJarFile.isFile, + "Missing path to base plugin jar System.getenv($basePluginPathEnvironmentKey)" + ) + basePluginJarFile.copyTo(this.basePluginJarFile) + } +} diff --git a/integration-tests/gradle/README.md b/integration-tests/gradle/README.md new file mode 100644 index 00000000..45828092 --- /dev/null +++ b/integration-tests/gradle/README.md @@ -0,0 +1,7 @@ +### Note +All Gradle projects inside the `project` subfolder can +also be imported to the IDE by clicking on the corresponding +build.gradle.kts file -> "import gradle project". + +Before importing: Make sure that you have dokka installed +locally (`./gradlew publishToMavenLocal`). diff --git a/integration-tests/gradle/build.gradle.kts b/integration-tests/gradle/build.gradle.kts new file mode 100644 index 00000000..3da416bb --- /dev/null +++ b/integration-tests/gradle/build.gradle.kts @@ -0,0 +1,16 @@ +import org.jetbrains.dependsOnMavenLocalPublication + +dependencies { + implementation(kotlin("stdlib")) + implementation(kotlin("test-junit")) + implementation(gradleTestKit()) +} + +tasks.integrationTest { + inputs.dir(file("projects")) + dependsOnMavenLocalPublication() +} + +tasks.clean { + delete(File(buildDir, "gradle-test-kit")) +} diff --git a/integration-tests/gradle/projects/it-android-0/build.gradle.kts b/integration-tests/gradle/projects/it-android-0/build.gradle.kts new file mode 100644 index 00000000..32c5c56c --- /dev/null +++ b/integration-tests/gradle/projects/it-android-0/build.gradle.kts @@ -0,0 +1,19 @@ +plugins { + id("com.android.library") + id("org.jetbrains.dokka") + kotlin("android") +} + +apply(from = "../template.root.gradle.kts") + +android { + defaultConfig { + minSdkVersion(21) + setCompileSdkVersion(29) + } +} + +dependencies { + implementation(kotlin("stdlib")) +} + diff --git a/integration-tests/gradle/projects/it-android-0/gradle.properties b/integration-tests/gradle/projects/it-android-0/gradle.properties new file mode 100644 index 00000000..1332de19 --- /dev/null +++ b/integration-tests/gradle/projects/it-android-0/gradle.properties @@ -0,0 +1,2 @@ +dokka_it_kotlin_version=1.3.72 +dokka_it_android_gradle_plugin_version=4.0.0 diff --git a/integration-tests/gradle/projects/it-android-0/gradle/wrapper/gradle-wrapper.jar b/integration-tests/gradle/projects/it-android-0/gradle/wrapper/gradle-wrapper.jar Binary files differnew file mode 100644 index 00000000..f3d88b1c --- /dev/null +++ b/integration-tests/gradle/projects/it-android-0/gradle/wrapper/gradle-wrapper.jar diff --git a/integration-tests/gradle/projects/it-android-0/gradle/wrapper/gradle-wrapper.properties b/integration-tests/gradle/projects/it-android-0/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..1b16c34a --- /dev/null +++ b/integration-tests/gradle/projects/it-android-0/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/integration-tests/gradle/projects/it-android-0/gradlew b/integration-tests/gradle/projects/it-android-0/gradlew new file mode 100755 index 00000000..2fe81a7d --- /dev/null +++ b/integration-tests/gradle/projects/it-android-0/gradlew @@ -0,0 +1,183 @@ +#!/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/integration-tests/gradle/projects/it-android-0/gradlew.bat b/integration-tests/gradle/projects/it-android-0/gradlew.bat new file mode 100644 index 00000000..24467a14 --- /dev/null +++ b/integration-tests/gradle/projects/it-android-0/gradlew.bat @@ -0,0 +1,100 @@ +@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 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/integration-tests/gradle/projects/it-android-0/settings.gradle.kts b/integration-tests/gradle/projects/it-android-0/settings.gradle.kts new file mode 100644 index 00000000..664d2cb7 --- /dev/null +++ b/integration-tests/gradle/projects/it-android-0/settings.gradle.kts @@ -0,0 +1,5 @@ +@file:Suppress("LocalVariableName", "UnstableApiUsage") + +apply(from = "../template.settings.gradle.kts") +rootProject.name = "it-android-0" + diff --git a/integration-tests/gradle/projects/it-android-0/src/main/AndroidManifest.xml b/integration-tests/gradle/projects/it-android-0/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a35f86be --- /dev/null +++ b/integration-tests/gradle/projects/it-android-0/src/main/AndroidManifest.xml @@ -0,0 +1 @@ +<manifest package="org.jetbrains.dokka.it.android"/> diff --git a/integration-tests/gradle/projects/it-android-0/src/main/java/it/android/AndroidSpecificClass.kt b/integration-tests/gradle/projects/it-android-0/src/main/java/it/android/AndroidSpecificClass.kt new file mode 100644 index 00000000..cb9046b1 --- /dev/null +++ b/integration-tests/gradle/projects/it-android-0/src/main/java/it/android/AndroidSpecificClass.kt @@ -0,0 +1,16 @@ +@file:Suppress("unused") + +package it.android + +import android.content.Context +import android.util.SparseIntArray +import android.view.View + +/** + * This class is specific to android and uses android classes like: + * [Context], [SparseIntArray] or [View] + */ +class AndroidSpecificClass { + fun sparseIntArray() = SparseIntArray() + fun createView(context: Context): View = View(context) +} diff --git a/integration-tests/gradle/projects/it-basic-groovy/build.gradle b/integration-tests/gradle/projects/it-basic-groovy/build.gradle new file mode 100644 index 00000000..1ffa75bc --- /dev/null +++ b/integration-tests/gradle/projects/it-basic-groovy/build.gradle @@ -0,0 +1,37 @@ +plugins { + id 'org.jetbrains.kotlin.jvm' + id("org.jetbrains.dokka") +} + +apply from: '../template.root.gradle.kts' + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib" +} + +dokkaHtml { + outputDirectory = "$buildDir/dokka/customHtml" + + failOnWarning = false + dokkaSourceSets { + customSourceSet { + sourceRoot { path = "$projectDir/src/main/java" } + sourceRoot { path = "$projectDir/src/main/kotlin" } + displayName = "custom" + reportUndocumented = true + } + } +} + +dokkaJavadoc { + outputDirectory = "$buildDir/dokka/customJavadoc" +} + +dokkaGfm { + outputDirectory = "$buildDir/dokka/customGfm" +} + +dokkaJekyll { + outputDirectory = "$buildDir/dokka/customJekyll" +} + diff --git a/integration-tests/gradle/projects/it-basic-groovy/gradle.properties b/integration-tests/gradle/projects/it-basic-groovy/gradle.properties new file mode 100644 index 00000000..7ebac3ad --- /dev/null +++ b/integration-tests/gradle/projects/it-basic-groovy/gradle.properties @@ -0,0 +1 @@ +dokka_it_kotlin_version=1.3.72 diff --git a/integration-tests/gradle/projects/it-basic-groovy/gradle/wrapper/gradle-wrapper.jar b/integration-tests/gradle/projects/it-basic-groovy/gradle/wrapper/gradle-wrapper.jar Binary files differnew file mode 100644 index 00000000..62d4c053 --- /dev/null +++ b/integration-tests/gradle/projects/it-basic-groovy/gradle/wrapper/gradle-wrapper.jar diff --git a/integration-tests/gradle/projects/it-basic-groovy/gradle/wrapper/gradle-wrapper.properties b/integration-tests/gradle/projects/it-basic-groovy/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..622ab64a --- /dev/null +++ b/integration-tests/gradle/projects/it-basic-groovy/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/integration-tests/gradle/projects/it-basic-groovy/gradlew b/integration-tests/gradle/projects/it-basic-groovy/gradlew new file mode 100755 index 00000000..fbd7c515 --- /dev/null +++ b/integration-tests/gradle/projects/it-basic-groovy/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/integration-tests/gradle/projects/it-basic-groovy/gradlew.bat b/integration-tests/gradle/projects/it-basic-groovy/gradlew.bat new file mode 100644 index 00000000..a9f778a7 --- /dev/null +++ b/integration-tests/gradle/projects/it-basic-groovy/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/integration-tests/gradle/projects/it-basic-groovy/settings.gradle.kts b/integration-tests/gradle/projects/it-basic-groovy/settings.gradle.kts new file mode 100644 index 00000000..cb6af4e9 --- /dev/null +++ b/integration-tests/gradle/projects/it-basic-groovy/settings.gradle.kts @@ -0,0 +1,5 @@ +@file:Suppress("LocalVariableName", "UnstableApiUsage") + +apply(from = "../template.settings.gradle.kts") +rootProject.name = "it-basic-groovy" + diff --git a/integration-tests/gradle/projects/it-basic-groovy/src/main/java/it/basic/java/SampleJavaClass.java b/integration-tests/gradle/projects/it-basic-groovy/src/main/java/it/basic/java/SampleJavaClass.java new file mode 100644 index 00000000..23b0202c --- /dev/null +++ b/integration-tests/gradle/projects/it-basic-groovy/src/main/java/it/basic/java/SampleJavaClass.java @@ -0,0 +1,17 @@ +package it.basic.java; + +import it.basic.PublicClass; + +/** + * This class is, unlike {@link PublicClass}, written in Java + */ +@SuppressWarnings("unused") +public class SampleJavaClass { + + /** + * @return Empty instance of {@link PublicClass} + */ + public PublicClass publicDocumentedFunction() { + return new PublicClass(); + } +} diff --git a/integration-tests/gradle/projects/it-basic-groovy/src/main/kotlin/it/basic/PublicClass.kt b/integration-tests/gradle/projects/it-basic-groovy/src/main/kotlin/it/basic/PublicClass.kt new file mode 100644 index 00000000..71bc7e63 --- /dev/null +++ b/integration-tests/gradle/projects/it-basic-groovy/src/main/kotlin/it/basic/PublicClass.kt @@ -0,0 +1,48 @@ +@file:Suppress("unused") + +package it.basic + +class PublicClass { + /** + * This function is public and documented + */ + fun publicDocumentedFunction(): String = "" + + fun publicUndocumentedFunction(): String = "" + + /** + * This function is internal and documented + */ + internal fun internalDocumentedFunction(): String = "" + + internal fun internalUndocumentedFunction(): String = "" + + /** + * This function is private and documented + */ + private fun privateDocumentedFunction(): String = "" + + private fun privateUndocumentedFunction(): String = "" + + + /** + * This property is public and documented + */ + val publicDocumentedProperty: Int = 0 + + val publicUndocumentedProperty: Int = 0 + + /** + * This property internal and documented + */ + val internalDocumentedProperty: Int = 0 + + val internalUndocumentedProperty: Int = 0 + + /** + * This property private and documented + */ + private val privateDocumentedProperty: Int = 0 + + private val privateUndocumentedProperty: Int = 0 +} diff --git a/integration-tests/gradle/projects/it-basic/build.gradle.kts b/integration-tests/gradle/projects/it-basic/build.gradle.kts new file mode 100644 index 00000000..1de92418 --- /dev/null +++ b/integration-tests/gradle/projects/it-basic/build.gradle.kts @@ -0,0 +1,20 @@ +import org.jetbrains.dokka.gradle.DokkaTask + +plugins { + kotlin("jvm") + id("org.jetbrains.dokka") +} + +apply(from = "../template.root.gradle.kts") + +dependencies { + implementation(kotlin("stdlib")) +} + +tasks.withType<DokkaTask> { + dokkaSourceSets { + configureEach { + moduleDisplayName = "Basic Project" + } + } +} diff --git a/integration-tests/gradle/projects/it-basic/gradle.properties b/integration-tests/gradle/projects/it-basic/gradle.properties new file mode 100644 index 00000000..7ebac3ad --- /dev/null +++ b/integration-tests/gradle/projects/it-basic/gradle.properties @@ -0,0 +1 @@ +dokka_it_kotlin_version=1.3.72 diff --git a/integration-tests/gradle/projects/it-basic/gradle/wrapper/gradle-wrapper.jar b/integration-tests/gradle/projects/it-basic/gradle/wrapper/gradle-wrapper.jar Binary files differnew file mode 100644 index 00000000..62d4c053 --- /dev/null +++ b/integration-tests/gradle/projects/it-basic/gradle/wrapper/gradle-wrapper.jar diff --git a/integration-tests/gradle/projects/it-basic/gradle/wrapper/gradle-wrapper.properties b/integration-tests/gradle/projects/it-basic/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..622ab64a --- /dev/null +++ b/integration-tests/gradle/projects/it-basic/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/integration-tests/gradle/projects/it-basic/gradlew b/integration-tests/gradle/projects/it-basic/gradlew new file mode 100755 index 00000000..fbd7c515 --- /dev/null +++ b/integration-tests/gradle/projects/it-basic/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/integration-tests/gradle/projects/it-basic/gradlew.bat b/integration-tests/gradle/projects/it-basic/gradlew.bat new file mode 100644 index 00000000..a9f778a7 --- /dev/null +++ b/integration-tests/gradle/projects/it-basic/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/integration-tests/gradle/projects/it-basic/settings.gradle.kts b/integration-tests/gradle/projects/it-basic/settings.gradle.kts new file mode 100644 index 00000000..833995e5 --- /dev/null +++ b/integration-tests/gradle/projects/it-basic/settings.gradle.kts @@ -0,0 +1,5 @@ +@file:Suppress("LocalVariableName", "UnstableApiUsage") + +apply(from = "../template.settings.gradle.kts") +rootProject.name = "it-basic" + diff --git a/integration-tests/gradle/projects/it-basic/src/main/java/it/basic/java/SampleJavaClass.java b/integration-tests/gradle/projects/it-basic/src/main/java/it/basic/java/SampleJavaClass.java new file mode 100644 index 00000000..23b0202c --- /dev/null +++ b/integration-tests/gradle/projects/it-basic/src/main/java/it/basic/java/SampleJavaClass.java @@ -0,0 +1,17 @@ +package it.basic.java; + +import it.basic.PublicClass; + +/** + * This class is, unlike {@link PublicClass}, written in Java + */ +@SuppressWarnings("unused") +public class SampleJavaClass { + + /** + * @return Empty instance of {@link PublicClass} + */ + public PublicClass publicDocumentedFunction() { + return new PublicClass(); + } +} diff --git a/integration-tests/gradle/projects/it-basic/src/main/kotlin/it/basic/PublicClass.kt b/integration-tests/gradle/projects/it-basic/src/main/kotlin/it/basic/PublicClass.kt new file mode 100644 index 00000000..71bc7e63 --- /dev/null +++ b/integration-tests/gradle/projects/it-basic/src/main/kotlin/it/basic/PublicClass.kt @@ -0,0 +1,48 @@ +@file:Suppress("unused") + +package it.basic + +class PublicClass { + /** + * This function is public and documented + */ + fun publicDocumentedFunction(): String = "" + + fun publicUndocumentedFunction(): String = "" + + /** + * This function is internal and documented + */ + internal fun internalDocumentedFunction(): String = "" + + internal fun internalUndocumentedFunction(): String = "" + + /** + * This function is private and documented + */ + private fun privateDocumentedFunction(): String = "" + + private fun privateUndocumentedFunction(): String = "" + + + /** + * This property is public and documented + */ + val publicDocumentedProperty: Int = 0 + + val publicUndocumentedProperty: Int = 0 + + /** + * This property internal and documented + */ + val internalDocumentedProperty: Int = 0 + + val internalUndocumentedProperty: Int = 0 + + /** + * This property private and documented + */ + private val privateDocumentedProperty: Int = 0 + + private val privateUndocumentedProperty: Int = 0 +} diff --git a/integration-tests/gradle/projects/it-multimodule-0/build.gradle.kts b/integration-tests/gradle/projects/it-multimodule-0/build.gradle.kts new file mode 100644 index 00000000..29b7550c --- /dev/null +++ b/integration-tests/gradle/projects/it-multimodule-0/build.gradle.kts @@ -0,0 +1 @@ +apply(from = "../template.root.gradle.kts") diff --git a/integration-tests/gradle/projects/it-multimodule-0/gradle.properties b/integration-tests/gradle/projects/it-multimodule-0/gradle.properties new file mode 100644 index 00000000..7ebac3ad --- /dev/null +++ b/integration-tests/gradle/projects/it-multimodule-0/gradle.properties @@ -0,0 +1 @@ +dokka_it_kotlin_version=1.3.72 diff --git a/integration-tests/gradle/projects/it-multimodule-0/gradle/wrapper/gradle-wrapper.jar b/integration-tests/gradle/projects/it-multimodule-0/gradle/wrapper/gradle-wrapper.jar Binary files differnew file mode 100644 index 00000000..f3d88b1c --- /dev/null +++ b/integration-tests/gradle/projects/it-multimodule-0/gradle/wrapper/gradle-wrapper.jar diff --git a/integration-tests/gradle/projects/it-multimodule-0/gradle/wrapper/gradle-wrapper.properties b/integration-tests/gradle/projects/it-multimodule-0/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..1b16c34a --- /dev/null +++ b/integration-tests/gradle/projects/it-multimodule-0/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/integration-tests/gradle/projects/it-multimodule-0/gradlew b/integration-tests/gradle/projects/it-multimodule-0/gradlew new file mode 100755 index 00000000..2fe81a7d --- /dev/null +++ b/integration-tests/gradle/projects/it-multimodule-0/gradlew @@ -0,0 +1,183 @@ +#!/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/integration-tests/gradle/projects/it-multimodule-0/gradlew.bat b/integration-tests/gradle/projects/it-multimodule-0/gradlew.bat new file mode 100644 index 00000000..24467a14 --- /dev/null +++ b/integration-tests/gradle/projects/it-multimodule-0/gradlew.bat @@ -0,0 +1,100 @@ +@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 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/integration-tests/gradle/projects/it-multimodule-0/moduleA/build.gradle.kts b/integration-tests/gradle/projects/it-multimodule-0/moduleA/build.gradle.kts new file mode 100644 index 00000000..ab86c333 --- /dev/null +++ b/integration-tests/gradle/projects/it-multimodule-0/moduleA/build.gradle.kts @@ -0,0 +1,6 @@ +plugins { + // TODO: File bug report for gradle: :moduleA:moduleB:dokkaHtml is missing kotlin gradle plugin from + // the runtime classpath during execution without this plugin in the parent project + kotlin("jvm") + id("org.jetbrains.dokka") +} diff --git a/integration-tests/gradle/projects/it-multimodule-0/moduleA/moduleB/README.md b/integration-tests/gradle/projects/it-multimodule-0/moduleA/moduleB/README.md new file mode 100644 index 00000000..f8c52880 --- /dev/null +++ b/integration-tests/gradle/projects/it-multimodule-0/moduleA/moduleB/README.md @@ -0,0 +1,2 @@ +# Module moduleB +Here is some description for module B diff --git a/integration-tests/gradle/projects/it-multimodule-0/moduleA/moduleB/build.gradle.kts b/integration-tests/gradle/projects/it-multimodule-0/moduleA/moduleB/build.gradle.kts new file mode 100644 index 00000000..9492fdc8 --- /dev/null +++ b/integration-tests/gradle/projects/it-multimodule-0/moduleA/moduleB/build.gradle.kts @@ -0,0 +1,8 @@ +plugins { + kotlin("jvm") + id("org.jetbrains.dokka") +} + +dependencies { + implementation(kotlin("stdlib")) +} diff --git a/integration-tests/gradle/projects/it-multimodule-0/moduleA/moduleB/src/main/kotlin/org/jetbrains/dokka/it/moduleB/ModuleB.kt b/integration-tests/gradle/projects/it-multimodule-0/moduleA/moduleB/src/main/kotlin/org/jetbrains/dokka/it/moduleB/ModuleB.kt new file mode 100644 index 00000000..430e2234 --- /dev/null +++ b/integration-tests/gradle/projects/it-multimodule-0/moduleA/moduleB/src/main/kotlin/org/jetbrains/dokka/it/moduleB/ModuleB.kt @@ -0,0 +1,6 @@ +package org.jetbrains.dokka.it.moduleB + +@Suppress("unused") +class ModuleB { + fun undocumentedPublicFunction() {} +} diff --git a/integration-tests/gradle/projects/it-multimodule-0/moduleA/moduleC/README.md b/integration-tests/gradle/projects/it-multimodule-0/moduleA/moduleC/README.md new file mode 100644 index 00000000..4ead5671 --- /dev/null +++ b/integration-tests/gradle/projects/it-multimodule-0/moduleA/moduleC/README.md @@ -0,0 +1,2 @@ +# Module moduleC +Here is some description for module C diff --git a/integration-tests/gradle/projects/it-multimodule-0/moduleA/moduleC/build.gradle.kts b/integration-tests/gradle/projects/it-multimodule-0/moduleA/moduleC/build.gradle.kts new file mode 100644 index 00000000..9492fdc8 --- /dev/null +++ b/integration-tests/gradle/projects/it-multimodule-0/moduleA/moduleC/build.gradle.kts @@ -0,0 +1,8 @@ +plugins { + kotlin("jvm") + id("org.jetbrains.dokka") +} + +dependencies { + implementation(kotlin("stdlib")) +} diff --git a/integration-tests/gradle/projects/it-multimodule-0/moduleA/moduleC/src/main/kotlin/org/jetbrains/dokka/it/moduleC/ModuleC.kt b/integration-tests/gradle/projects/it-multimodule-0/moduleA/moduleC/src/main/kotlin/org/jetbrains/dokka/it/moduleC/ModuleC.kt new file mode 100644 index 00000000..e14d68e0 --- /dev/null +++ b/integration-tests/gradle/projects/it-multimodule-0/moduleA/moduleC/src/main/kotlin/org/jetbrains/dokka/it/moduleC/ModuleC.kt @@ -0,0 +1,6 @@ +package org.jetbrains.dokka.it.moduleC + +@Suppress("unused") +class ModuleC { + fun undocumentedPublicFunction() {} +} diff --git a/integration-tests/gradle/projects/it-multimodule-0/settings.gradle.kts b/integration-tests/gradle/projects/it-multimodule-0/settings.gradle.kts new file mode 100644 index 00000000..a5c89291 --- /dev/null +++ b/integration-tests/gradle/projects/it-multimodule-0/settings.gradle.kts @@ -0,0 +1,5 @@ +apply(from = "../template.settings.gradle.kts") +rootProject.name = "it-multimodule-0" +include(":moduleA") +include(":moduleA:moduleB") +include(":moduleA:moduleC") diff --git a/integration-tests/gradle/projects/it-multiplatform-0/build.gradle.kts b/integration-tests/gradle/projects/it-multiplatform-0/build.gradle.kts new file mode 100644 index 00000000..247e4c15 --- /dev/null +++ b/integration-tests/gradle/projects/it-multiplatform-0/build.gradle.kts @@ -0,0 +1,25 @@ +import org.jetbrains.dokka.gradle.DokkaTask + +plugins { + kotlin("multiplatform") + id("org.jetbrains.dokka") +} + +apply(from = "../template.root.gradle.kts") + +kotlin { + jvm() + linuxX64("linux") + macosX64("macos") + js() +} + +tasks.withType<DokkaTask> { + dokkaSourceSets { + create("commonMain") + create("jvmMain") + create("linuxMain") + create("macosMain") + create("jsMain") + } +} diff --git a/integration-tests/gradle/projects/it-multiplatform-0/gradle.properties b/integration-tests/gradle/projects/it-multiplatform-0/gradle.properties new file mode 100644 index 00000000..7ebac3ad --- /dev/null +++ b/integration-tests/gradle/projects/it-multiplatform-0/gradle.properties @@ -0,0 +1 @@ +dokka_it_kotlin_version=1.3.72 diff --git a/integration-tests/gradle/projects/it-multiplatform-0/gradle/wrapper/gradle-wrapper.jar b/integration-tests/gradle/projects/it-multiplatform-0/gradle/wrapper/gradle-wrapper.jar Binary files differnew file mode 100644 index 00000000..f3d88b1c --- /dev/null +++ b/integration-tests/gradle/projects/it-multiplatform-0/gradle/wrapper/gradle-wrapper.jar diff --git a/integration-tests/gradle/projects/it-multiplatform-0/gradle/wrapper/gradle-wrapper.properties b/integration-tests/gradle/projects/it-multiplatform-0/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..1b16c34a --- /dev/null +++ b/integration-tests/gradle/projects/it-multiplatform-0/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/integration-tests/gradle/projects/it-multiplatform-0/gradlew b/integration-tests/gradle/projects/it-multiplatform-0/gradlew new file mode 100755 index 00000000..2fe81a7d --- /dev/null +++ b/integration-tests/gradle/projects/it-multiplatform-0/gradlew @@ -0,0 +1,183 @@ +#!/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/integration-tests/gradle/projects/it-multiplatform-0/gradlew.bat b/integration-tests/gradle/projects/it-multiplatform-0/gradlew.bat new file mode 100644 index 00000000..24467a14 --- /dev/null +++ b/integration-tests/gradle/projects/it-multiplatform-0/gradlew.bat @@ -0,0 +1,100 @@ +@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 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/integration-tests/gradle/projects/it-multiplatform-0/settings.gradle.kts b/integration-tests/gradle/projects/it-multiplatform-0/settings.gradle.kts new file mode 100644 index 00000000..1894bed8 --- /dev/null +++ b/integration-tests/gradle/projects/it-multiplatform-0/settings.gradle.kts @@ -0,0 +1,2 @@ +apply(from = "../template.settings.gradle.kts") +rootProject.name = "it-multiplatform-0" diff --git a/integration-tests/gradle/projects/it-multiplatform-0/src/commonMain/kotlin/it/mpp0/CommonMainClass.kt b/integration-tests/gradle/projects/it-multiplatform-0/src/commonMain/kotlin/it/mpp0/CommonMainClass.kt new file mode 100644 index 00000000..499a4f1e --- /dev/null +++ b/integration-tests/gradle/projects/it-multiplatform-0/src/commonMain/kotlin/it/mpp0/CommonMainClass.kt @@ -0,0 +1,8 @@ +package it.mpp0 + +/** + * This class is defined in commonMain + */ +class CommonMainClass { + fun publicFunction(): String = "public" +} diff --git a/integration-tests/gradle/projects/it-multiplatform-0/src/commonMain/kotlin/it/mpp0/ExpectedClass.kt b/integration-tests/gradle/projects/it-multiplatform-0/src/commonMain/kotlin/it/mpp0/ExpectedClass.kt new file mode 100644 index 00000000..e610b09a --- /dev/null +++ b/integration-tests/gradle/projects/it-multiplatform-0/src/commonMain/kotlin/it/mpp0/ExpectedClass.kt @@ -0,0 +1,5 @@ +package it.mpp0 + +expect class ExpectedClass { + val platform: String +} diff --git a/integration-tests/gradle/projects/it-multiplatform-0/src/jsMain/kotlin/it/mpp0/ExpectedClass.kt b/integration-tests/gradle/projects/it-multiplatform-0/src/jsMain/kotlin/it/mpp0/ExpectedClass.kt new file mode 100644 index 00000000..1e4a6d22 --- /dev/null +++ b/integration-tests/gradle/projects/it-multiplatform-0/src/jsMain/kotlin/it/mpp0/ExpectedClass.kt @@ -0,0 +1,5 @@ +package it.mpp0 + +actual class ExpectedClass { + actual val platform: String = "js" +} diff --git a/integration-tests/gradle/projects/it-multiplatform-0/src/jvmMain/kotlin/it/mpp0/ExpectedClass.kt b/integration-tests/gradle/projects/it-multiplatform-0/src/jvmMain/kotlin/it/mpp0/ExpectedClass.kt new file mode 100644 index 00000000..8e7fa96e --- /dev/null +++ b/integration-tests/gradle/projects/it-multiplatform-0/src/jvmMain/kotlin/it/mpp0/ExpectedClass.kt @@ -0,0 +1,5 @@ +package it.mpp0 + +actual class ExpectedClass { + actual val platform: String = "jvm" +} diff --git a/integration-tests/gradle/projects/it-multiplatform-0/src/linuxMain/kotlin/it/mpp0/ExpectedClass.kt b/integration-tests/gradle/projects/it-multiplatform-0/src/linuxMain/kotlin/it/mpp0/ExpectedClass.kt new file mode 100644 index 00000000..19070a96 --- /dev/null +++ b/integration-tests/gradle/projects/it-multiplatform-0/src/linuxMain/kotlin/it/mpp0/ExpectedClass.kt @@ -0,0 +1,5 @@ +package it.mpp0 + +actual class ExpectedClass { + actual val platform: String = "linux" +} diff --git a/integration-tests/gradle/projects/it-multiplatform-0/src/macosMain/kotlin/it/mpp0/ExpectedClass.kt b/integration-tests/gradle/projects/it-multiplatform-0/src/macosMain/kotlin/it/mpp0/ExpectedClass.kt new file mode 100644 index 00000000..7a4a8f75 --- /dev/null +++ b/integration-tests/gradle/projects/it-multiplatform-0/src/macosMain/kotlin/it/mpp0/ExpectedClass.kt @@ -0,0 +1,5 @@ +package it.mpp0 + +actual class ExpectedClass { + actual val platform: String = "macos" +} diff --git a/integration-tests/gradle/projects/template.root.gradle.kts b/integration-tests/gradle/projects/template.root.gradle.kts new file mode 100644 index 00000000..51d199ff --- /dev/null +++ b/integration-tests/gradle/projects/template.root.gradle.kts @@ -0,0 +1,18 @@ +allprojects { + repositories { + maven("https://dl.bintray.com/kotlin/kotlin-eap/") + maven("https://dl.bintray.com/kotlin/kotlin-dev/") + jcenter() + mavenLocal() + mavenCentral() + google() + } +} + +afterEvaluate { + logger.quiet("Gradle version: ${gradle.gradleVersion}") + logger.quiet("Kotlin version: ${properties["dokka_it_kotlin_version"]}") + properties["dokka_it_android_gradle_plugin_version"]?.let { androidVersion -> + logger.quiet("Android version: $androidVersion") + } +} diff --git a/integration-tests/gradle/projects/template.settings.gradle.kts b/integration-tests/gradle/projects/template.settings.gradle.kts new file mode 100644 index 00000000..7fe3c510 --- /dev/null +++ b/integration-tests/gradle/projects/template.settings.gradle.kts @@ -0,0 +1,37 @@ +@file:Suppress("LocalVariableName", "UnstableApiUsage") + +pluginManagement { + val dokka_it_kotlin_version: String by settings + val dokka_it_android_gradle_plugin_version: String? by settings + + plugins { + id("org.jetbrains.kotlin.jvm") version dokka_it_kotlin_version + id("org.jetbrains.kotlin.android") version dokka_it_kotlin_version + id("org.jetbrains.kotlin.multiplatform") version dokka_it_kotlin_version + } + + resolutionStrategy { + eachPlugin { + if (requested.id.id == "org.jetbrains.dokka") { + useModule("org.jetbrains.dokka:dokka-gradle-plugin:for-integration-tests-SNAPSHOT") + } + + if (requested.id.id == "com.android.library") { + useModule("com.android.tools.build:gradle:$dokka_it_android_gradle_plugin_version") + } + + if (requested.id.id == "com.android.application") { + useModule("com.android.tools.build:gradle:$dokka_it_android_gradle_plugin_version") + } + } + } + repositories { + maven("https://dl.bintray.com/kotlin/kotlin-eap") + maven("https://dl.bintray.com/kotlin/kotlin-dev/") + mavenLocal() + mavenCentral() + jcenter() + gradlePluginPortal() + google() + } +} diff --git a/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/Android0GradleIntegrationTest.kt b/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/Android0GradleIntegrationTest.kt new file mode 100644 index 00000000..2a9d7a70 --- /dev/null +++ b/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/Android0GradleIntegrationTest.kt @@ -0,0 +1,77 @@ +package org.jetbrains.dokka.it.gradle + +import org.gradle.testkit.runner.TaskOutcome +import org.jetbrains.dokka.it.isAndroidSdkInstalled +import org.jetbrains.dokka.it.isCI +import org.junit.Assume +import org.junit.runners.Parameterized.Parameters +import java.io.File +import kotlin.test.* + +class Android0GradleIntegrationTest(override val versions: BuildVersions) : AbstractGradleIntegrationTest() { + + companion object { + @get:JvmStatic + @get:Parameters(name = "{0}") + val versions = BuildVersions.permutations( + gradleVersions = listOf("6.5.1", "5.6.4"), + kotlinVersions = listOf("1.3.72", "1.4-M3"), + androidGradlePluginVersions = listOf("3.5.3", "3.6.3") + ) + BuildVersions.permutations( + gradleVersions = listOf("6.5.1", "6.1.1"), + kotlinVersions = listOf("1.3.72", "1.4-M3"), + androidGradlePluginVersions = listOf("4.0.0") + ) + BuildVersions.permutations( + gradleVersions = listOf("6.5.1"), + kotlinVersions = listOf("1.3.72", "1.4-M3"), + androidGradlePluginVersions = listOf("4.1.0-beta02") + ) + } + + @BeforeTest + fun assumeAndroidInstallation() { + if (isCI) { + return + } + Assume.assumeTrue("Missing ANDROID_SDK_ROOT", isAndroidSdkInstalled) + } + + @BeforeTest + fun prepareProjectFiles() { + val templateProjectDir = File("projects", "it-android-0") + + templateProjectDir.listFiles().orEmpty() + .filter { it.isFile } + .filterNot { it.name == "local.properties" } + .filterNot { it.name.startsWith("gradlew") } + .forEach { topLevelFile -> topLevelFile.copyTo(File(projectDir, topLevelFile.name)) } + + File(templateProjectDir, "src").copyRecursively(File(projectDir, "src")) + } + + @Test + fun execute() { + val result = createGradleRunner("dokkaHtml", "-i", "-s").buildRelaxed() + assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":dokkaHtml")).outcome) + + val htmlOutputDir = File(projectDir, "build/dokka/html") + assertTrue(htmlOutputDir.isDirectory, "Missing html output directory") + + assertTrue( + htmlOutputDir.allHtmlFiles().count() > 0, + "Expected html files in html output directory" + ) + + htmlOutputDir.allHtmlFiles().forEach { file -> + assertContainsNoErrorClass(file) + assertNoUnresolvedLInks(file) + assertNoHrefToMissingLocalFileOrDirectory(file) + } + + assertTrue( + htmlOutputDir.allHtmlFiles().any { file -> + "https://developer.android.com/reference/android/content/Context.html" in file.readText() + }, "Expected link to developer.android.com" + ) + } +} diff --git a/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/BasicGradleIntegrationTest.kt b/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/BasicGradleIntegrationTest.kt new file mode 100644 index 00000000..30b560e7 --- /dev/null +++ b/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/BasicGradleIntegrationTest.kt @@ -0,0 +1,99 @@ +package org.jetbrains.dokka.it.gradle + +import org.gradle.testkit.runner.TaskOutcome +import org.junit.runners.Parameterized.Parameters +import java.io.File +import kotlin.test.* + +class BasicGradleIntegrationTest(override val versions: BuildVersions) : AbstractGradleIntegrationTest() { + + companion object { + @get:JvmStatic + @get:Parameters(name = "{0}") + val versions = BuildVersions.permutations( + gradleVersions = listOf("6.5.1", "6.4.1", "6.3", "6.2.2", "6.1.1", "6.0", "5.6.4"), + kotlinVersions = listOf("1.3.30", "1.3.72", "1.4-M3") + ) + } + + @BeforeTest + fun prepareProjectFiles() { + val templateProjectDir = File("projects", "it-basic") + + templateProjectDir.listFiles().orEmpty() + .filter { it.isFile } + .forEach { topLevelFile -> topLevelFile.copyTo(File(projectDir, topLevelFile.name)) } + + File(templateProjectDir, "src").copyRecursively(File(projectDir, "src")) + } + + @Test + fun execute() { + val result = createGradleRunner("dokkaHtml", "dokkaJavadoc", "dokkaGfm", "dokkaJekyll", "-i", "-s") + .buildRelaxed() + + assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":dokkaHtml")).outcome) + assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":dokkaJavadoc")).outcome) + assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":dokkaGfm")).outcome) + assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":dokkaJekyll")).outcome) + + File(projectDir, "build/dokka/html").assertKdocOutputDir() + File(projectDir, "build/dokka/javadoc").assertJavadocOutputDir() + File(projectDir, "build/dokka/gfm").assertGfmOutputDir() + File(projectDir, "build/dokka/jekyll").assertJekyllOutputDir() + } + + private fun File.assertKdocOutputDir() { + assertTrue(isDirectory, "Missing dokka html output directory") + + val imagesDir = File(this, "images") + assertTrue(imagesDir.isDirectory, "Missing images directory") + + val scriptsDir = File(this, "scripts") + assertTrue(scriptsDir.isDirectory, "Missing scripts directory") + + val stylesDir = File(this, "styles") + assertTrue(stylesDir.isDirectory, "Missing styles directory") + + val navigationHtml = File(this, "navigation.html") + assertTrue(navigationHtml.isFile, "Missing navigation.html") + + val moduleOutputDir = File(this, "-basic -project") + assertTrue(moduleOutputDir.isDirectory, "Missing module directory") + + val moduleIndexHtml = File(moduleOutputDir, "index.html") + assertTrue(moduleIndexHtml.isFile, "Missing module index.html") + + val modulePackageDir = File(moduleOutputDir, "it.basic") + assertTrue(modulePackageDir.isDirectory, "Missing it.basic package directory") + + val modulePackageIndexHtml = File(modulePackageDir, "index.html") + assertTrue(modulePackageIndexHtml.isFile, "Missing module package index.html") + + val moduleJavaPackageDir = File(moduleOutputDir, "it.basic.java") + assertTrue(moduleJavaPackageDir.isDirectory, "Missing it.basic.java package directory") + + allHtmlFiles().forEach { file -> + assertContainsNoErrorClass(file) + assertNoUnresolvedLInks(file) + assertNoHrefToMissingLocalFileOrDirectory(file) + } + + assertTrue( + allHtmlFiles().any { file -> "Basic Project" in file.readText() }, + "Expected configured moduleDisplayName to be present in html" + ) + } + + private fun File.assertJavadocOutputDir() { + assertTrue(isDirectory, "Missing dokka javadoc output directory") + } + + private fun File.assertGfmOutputDir() { + assertTrue(isDirectory, "Missing dokka gfm output directory") + } + + private fun File.assertJekyllOutputDir() { + assertTrue(isDirectory, "Missing dokka jekyll output directory") + } +} diff --git a/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/BasicGroovyIntegrationTest.kt b/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/BasicGroovyIntegrationTest.kt new file mode 100644 index 00000000..92b7ec40 --- /dev/null +++ b/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/BasicGroovyIntegrationTest.kt @@ -0,0 +1,98 @@ +package org.jetbrains.dokka.it.gradle + +import org.gradle.testkit.runner.TaskOutcome +import org.junit.runners.Parameterized +import java.io.File +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertTrue +import kotlin.test.BeforeTest +import kotlin.test.Test + +class BasicGroovyIntegrationTest(override val versions: BuildVersions) : AbstractGradleIntegrationTest() { + + companion object { + @get:JvmStatic + @get:Parameterized.Parameters(name = "{0}") + val versions = BuildVersions.permutations( + gradleVersions = listOf("6.5.1", "5.6.4"), + kotlinVersions = listOf("1.4-M3") + ) + } + + @BeforeTest + fun prepareProjectFiles() { + val templateProjectDir = File("projects", "it-basic-groovy") + + templateProjectDir.listFiles().orEmpty() + .filter { it.isFile } + .forEach { topLevelFile -> topLevelFile.copyTo(File(projectDir, topLevelFile.name)) } + + File(templateProjectDir, "src").copyRecursively(File(projectDir, "src")) + } + + @Test + fun execute() { + val result = createGradleRunner("dokkaHtml", "dokkaJavadoc", "dokkaGfm", "dokkaJekyll", "-i", "-s") + .buildRelaxed() + + assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":dokkaHtml")).outcome) + assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":dokkaJavadoc")).outcome) + assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":dokkaGfm")).outcome) + assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":dokkaJekyll")).outcome) + + File(projectDir, "build/dokka/customHtml").assertKdocOutputDir() + File(projectDir, "build/dokka/customJavadoc").assertJavadocOutputDir() + File(projectDir, "build/dokka/customGfm").assertGfmOutputDir() + File(projectDir, "build/dokka/customJekyll").assertJekyllOutputDir() + } + + private fun File.assertKdocOutputDir() { + assertTrue(isDirectory, "Missing dokka html output directory") + + val imagesDir = File(this, "images") + assertTrue(imagesDir.isDirectory, "Missing images directory") + + val scriptsDir = File(this, "scripts") + assertTrue(scriptsDir.isDirectory, "Missing scripts directory") + + val stylesDir = File(this, "styles") + assertTrue(stylesDir.isDirectory, "Missing styles directory") + + val navigationHtml = File(this, "navigation.html") + assertTrue(navigationHtml.isFile, "Missing navigation.html") + + val moduleOutputDir = File(this, "it-basic-groovy") + assertTrue(moduleOutputDir.isDirectory, "Missing module directory") + + val moduleIndexHtml = File(moduleOutputDir, "index.html") + assertTrue(moduleIndexHtml.isFile, "Missing module index.html") + + val modulePackageDir = File(moduleOutputDir, "it.basic") + assertTrue(modulePackageDir.isDirectory, "Missing it.basic package directory") + + val modulePackageIndexHtml = File(modulePackageDir, "index.html") + assertTrue(modulePackageIndexHtml.isFile, "Missing module package index.html") + + val moduleJavaPackageDir = File(moduleOutputDir, "it.basic.java") + assertTrue(moduleJavaPackageDir.isDirectory, "Missing it.basic.java package directory") + + allHtmlFiles().forEach { file -> + assertContainsNoErrorClass(file) + assertNoUnresolvedLInks(file) + assertNoHrefToMissingLocalFileOrDirectory(file) + } + } + + private fun File.assertJavadocOutputDir() { + assertTrue(isDirectory, "Missing dokka javadoc output directory") + } + + private fun File.assertGfmOutputDir() { + assertTrue(isDirectory, "Missing dokka gfm output directory") + } + + private fun File.assertJekyllOutputDir() { + assertTrue(isDirectory, "Missing dokka jekyll output directory") + } +} diff --git a/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/Multimodule0IntegrationTest.kt b/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/Multimodule0IntegrationTest.kt new file mode 100644 index 00000000..390db3a0 --- /dev/null +++ b/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/Multimodule0IntegrationTest.kt @@ -0,0 +1,61 @@ +package org.jetbrains.dokka.it.gradle + +import org.gradle.testkit.runner.TaskOutcome +import org.junit.runners.Parameterized +import java.io.File +import kotlin.test.* + +class Multimodule0IntegrationTest(override val versions: BuildVersions) : AbstractGradleIntegrationTest() { + companion object { + @get:JvmStatic + @get:Parameterized.Parameters(name = "{0}") + val versions = BuildVersions.permutations( + gradleVersions = listOf("6.5.1", "6.1.1"), + kotlinVersions = listOf("1.4-M3") + ) + } + + @BeforeTest + fun prepareProjectFiles() { + val templateProjectDir = File("projects", "it-multimodule-0") + templateProjectDir.listFiles().orEmpty() + .filter { it.isFile } + .forEach { topLevelFile -> topLevelFile.copyTo(File(projectDir, topLevelFile.name)) } + File(templateProjectDir, "moduleA").copyRecursively(File(projectDir, "moduleA")) + } + + @Test + fun execute() { + val result = createGradleRunner(":moduleA:dokkaHtmlMultimodule", "-i","-s").buildRelaxed() + + assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":moduleA:dokkaHtmlMultimodule")).outcome) + + val outputDir = File(projectDir, "moduleA/build/dokka/htmlMultimodule") + assertTrue(outputDir.isDirectory, "Missing dokka output directory") + + assertTrue( + outputDir.allHtmlFiles().any(), + "Expected at least one html file being generated" + ) + + outputDir.allHtmlFiles().forEach { file -> + assertContainsNoErrorClass(file) + assertNoUnresolvedLInks(file) + assertNoHrefToMissingLocalFileOrDirectory(file) + } + + val modulesFile = File(outputDir, "-modules.html") + assertTrue(modulesFile.isFile, "Missing -modules.html file") + + val modulesFileText = modulesFile.readText() + assertTrue( + "moduleB" in modulesFileText, + "Expected moduleB being mentioned in -modules.html" + ) + assertTrue( + "moduleC" in modulesFileText, + "Expected moduleC being mentioned in -modules.html" + ) + + } +} diff --git a/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/Multiplatform0GradleIntegrationTest.kt b/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/Multiplatform0GradleIntegrationTest.kt new file mode 100644 index 00000000..6a3b9c83 --- /dev/null +++ b/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/Multiplatform0GradleIntegrationTest.kt @@ -0,0 +1,43 @@ +package org.jetbrains.dokka.it.gradle + +import org.gradle.testkit.runner.TaskOutcome +import org.junit.runners.Parameterized +import java.io.File +import kotlin.test.* + +class Multiplatform0GradleIntegrationTest(override val versions: BuildVersions) : AbstractGradleIntegrationTest() { + + companion object { + @get:JvmStatic + @get:Parameterized.Parameters(name = "{0}") + val versions = BuildVersions.permutations( + gradleVersions = listOf("6.5.1", "6.1.1"), + kotlinVersions = listOf("1.3.30", "1.3.72", "1.4-M3") + ) + } + + @BeforeTest + fun prepareProjectFiles() { + val templateProjectDir = File("projects", "it-multiplatform-0") + templateProjectDir.listFiles().orEmpty() + .filter { it.isFile } + .forEach { topLevelFile -> topLevelFile.copyTo(File(projectDir, topLevelFile.name)) } + File(templateProjectDir, "src").copyRecursively(File(projectDir, "src")) + } + + @Test + fun execute() { + val result = createGradleRunner("dokkaHtml", "-i", "-s").buildRelaxed() + + assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":dokkaHtml")).outcome) + + val dokkaOutputDir = File(projectDir, "build/dokka/html") + assertTrue(dokkaOutputDir.isDirectory, "Missing dokka output directory") + + dokkaOutputDir.allHtmlFiles().forEach { file -> + assertContainsNoErrorClass(file) + assertNoUnresolvedLInks(file) + assertNoHrefToMissingLocalFileOrDirectory(file) + } + } +} diff --git a/integration-tests/gradle/src/main/kotlin/org/jetbrains/dokka/it/gradle/AbstractGradleIntegrationTest.kt b/integration-tests/gradle/src/main/kotlin/org/jetbrains/dokka/it/gradle/AbstractGradleIntegrationTest.kt new file mode 100644 index 00000000..f852dc8b --- /dev/null +++ b/integration-tests/gradle/src/main/kotlin/org/jetbrains/dokka/it/gradle/AbstractGradleIntegrationTest.kt @@ -0,0 +1,74 @@ +package org.jetbrains.dokka.it.gradle + +import org.gradle.testkit.runner.BuildResult +import org.gradle.testkit.runner.GradleRunner +import org.gradle.testkit.runner.internal.DefaultGradleRunner +import org.gradle.tooling.GradleConnectionException +import org.jetbrains.dokka.it.AbstractIntegrationTest +import org.junit.Assume +import org.junit.Assume.assumeFalse +import org.junit.AssumptionViolatedException +import org.junit.runner.RunWith +import org.junit.runners.Parameterized +import java.io.File +import kotlin.test.BeforeTest + +@RunWith(Parameterized::class) +abstract class AbstractGradleIntegrationTest : AbstractIntegrationTest() { + + abstract val versions: BuildVersions + + @BeforeTest + fun copyTemplates() { + File("projects").listFiles().orEmpty() + .filter { it.isFile } + .filter { it.name.startsWith("template.") } + .forEach { file -> file.copyTo(File(temporaryTestFolder.root, file.name)) } + } + + fun createGradleRunner( + vararg arguments: String + ): GradleRunner { + return GradleRunner.create() + .withProjectDir(projectDir) + .withGradleVersion(versions.gradleVersion.version) + .forwardOutput() + .withTestKitDir(File("build", "gradle-test-kit").absoluteFile) + .withArguments( + listOfNotNull( + "-Pkotlin_version=${versions.kotlinVersion}", + "-Pdokka_it_kotlin_version=${versions.kotlinVersion}", + versions.androidGradlePluginVersion?.let { androidVersion -> + "-Pdokka_it_android_gradle_plugin_version=$androidVersion" + }, + * arguments + ) + ).run { this as DefaultGradleRunner } + .withJvmArguments("-Xmx4G", "-XX:MaxMetaspaceSize=2G") + } + + fun GradleRunner.buildRelaxed(): BuildResult { + return try { + build() + } catch (e: Throwable) { + val gradleConnectionException = e.withAllCauses().find { it is GradleConnectionException } + if (gradleConnectionException != null) { + gradleConnectionException.printStackTrace() + throw AssumptionViolatedException("Assumed Gradle connection", gradleConnectionException) + + } + throw e + } + } +} + +private fun Throwable.withAllCauses(): Sequence<Throwable> { + val root = this + return sequence { + yield(root) + val cause = root.cause + if (cause != null && cause != root) { + yieldAll(cause.withAllCauses()) + } + } +} diff --git a/integration-tests/gradle/src/main/kotlin/org/jetbrains/dokka/it/gradle/BuildVersions.kt b/integration-tests/gradle/src/main/kotlin/org/jetbrains/dokka/it/gradle/BuildVersions.kt new file mode 100644 index 00000000..84a7f1e8 --- /dev/null +++ b/integration-tests/gradle/src/main/kotlin/org/jetbrains/dokka/it/gradle/BuildVersions.kt @@ -0,0 +1,48 @@ +package org.jetbrains.dokka.it.gradle + +import org.gradle.util.GradleVersion + +data class BuildVersions( + val gradleVersion: GradleVersion, + val kotlinVersion: String, + val androidGradlePluginVersion: String? = null, +) { + constructor( + gradleVersion: String, + kotlinVersion: String, + androidGradlePluginVersion: String? = null + ) : this( + gradleVersion = GradleVersion.version(gradleVersion), + kotlinVersion = kotlinVersion, + androidGradlePluginVersion = androidGradlePluginVersion + ) + + override fun toString(): String { + return buildString { + append("Gradle ${gradleVersion.version}, Kotlin $kotlinVersion") + if (androidGradlePluginVersion != null) { + append(", Android $androidGradlePluginVersion") + } + } + } + + companion object { + fun permutations( + gradleVersions: List<String>, + kotlinVersions: List<String>, + androidGradlePluginVersions: List<String?> = listOf(null) + ): List<BuildVersions> { + return gradleVersions.distinct().flatMap { gradleVersion -> + kotlinVersions.distinct().flatMap { kotlinVersion -> + androidGradlePluginVersions.distinct().map { androidVersion -> + BuildVersions( + gradleVersion = gradleVersion, + kotlinVersion = kotlinVersion, + androidGradlePluginVersion = androidVersion + ) + } + } + } + } + } +} diff --git a/integration-tests/maven/build.gradle.kts b/integration-tests/maven/build.gradle.kts new file mode 100644 index 00000000..1c747bbc --- /dev/null +++ b/integration-tests/maven/build.gradle.kts @@ -0,0 +1,20 @@ +import org.jetbrains.SetupMaven +import org.jetbrains.dependsOnMavenLocalPublication + +evaluationDependsOn(":runners:maven-plugin") + +dependencies { + implementation(kotlin("stdlib")) + implementation(kotlin("test-junit")) +} + +tasks.integrationTest { + dependsOnMavenLocalPublication() + + val setupMavenTask = project(":runners:maven-plugin").tasks.withType<SetupMaven>().single() + dependsOn(setupMavenTask) + + val dokka_version: String by project + environment("DOKKA_VERSION", dokka_version) + environment("MVN_BINARY_PATH", setupMavenTask.mvn.absolutePath) +} diff --git a/integration-tests/maven/projects/it-maven/pom.xml b/integration-tests/maven/projects/it-maven/pom.xml new file mode 100644 index 00000000..c6e0ef45 --- /dev/null +++ b/integration-tests/maven/projects/it-maven/pom.xml @@ -0,0 +1,172 @@ +<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>org.jetbrains.dokka</groupId> + <artifactId>it-maven</artifactId> + <version>1.0-SNAPSHOT</version> + + <properties> + <kotlin.version>1.3.72</kotlin.version> + </properties> + <build> + <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/kotlin</sourceDir> + <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/kotlin</sourceDir> + <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> + <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.jetbrains.dokka</groupId> + <artifactId>dokka-maven-plugin</artifactId> + <version>$dokka_version</version> + <executions> + <execution> + <phase>pre-site</phase> + <goals> + <goal>dokka</goal> + </goals> + </execution> + </executions> + <configuration> + + <!-- Set to true to skip dokka task, default: false --> + <skip>false</skip> + + <!-- Default: ${project.artifactId} --> + <moduleDisplayName>Maven Integration Test Module</moduleDisplayName> + + <!-- Default: ${project.basedir}/target/dokka --> + <outputDir>${project.basedir}/output</outputDir> + + <!-- Use default or set to custom path to cache directory to enable package-list caching. --> + <!-- When set to default, caches stored in $USER_HOME/.cache/dokka --> + <cacheRoot>default</cacheRoot> + + + <!-- Used for linking to JDK, default: 6 --> + <jdkVersion>8</jdkVersion> + + <!-- Do not output deprecated members, applies globally, can be overridden by packageOptions --> + <skipDeprecated>false</skipDeprecated> + <!-- Emit warnings about not documented members, applies globally, also can be overridden by packageOptions --> + <reportUndocumented>true</reportUndocumented> + <!-- Do not create index pages for empty packages --> + <skipEmptyPackages>true</skipEmptyPackages> + + <!-- Short form list of sourceRoots, by default, set to ${project.compileSourceRoots} --> + <sourceDirectories> + <dir>${project.basedir}/src/main/kotlin</dir> + <dir>${project.basedir}/src/main/java</dir> + </sourceDirectories> + + + <!-- Disable linking to online kotlin-stdlib documentation --> + <noStdlibLink>false</noStdlibLink> + + <!-- Disable linking to online JDK documentation --> + <noJdkLink>false</noJdkLink> + + <!-- Allows to customize documentation generation options on a per-package basis --> + <perPackageOptions> + <packageOptions> + <!-- Will match kotlin and all sub-packages of it --> + <prefix>kotlin</prefix> + + <!-- All options are optional, default values are below: --> + <skipDeprecated>false</skipDeprecated> + <!-- Emit warnings about not documented members --> + <reportUndocumented>true</reportUndocumented> + <includeNonPublic>false</includeNonPublic> + </packageOptions> + </perPackageOptions> + </configuration> + </plugin> + </plugins> + <sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory> + <testSourceDirectory>${project.basedir}/src/test/kotlin</testSourceDirectory> + </build> + + <pluginRepositories> + <pluginRepository> + <id>kotlin-eap</id> + <url>https://dl.bintray.com/kotlin/kotlin-eap/</url> + </pluginRepository> + <pluginRepository> + <id>kotlin-dev</id> + <url>https://dl.bintray.com/kotlin/kotlin-dev/</url> + </pluginRepository> + <pluginRepository> + <id>jcenter</id> + <name>JCenter</name> + <url>https://jcenter.bintray.com/</url> + </pluginRepository> + </pluginRepositories> + + <dependencies> + <dependency> + <groupId>org.jetbrains.kotlin</groupId> + <artifactId>kotlin-stdlib</artifactId> + <version>${kotlin.version}</version> + </dependency> + </dependencies> + + +</project> diff --git a/integration-tests/maven/projects/it-maven/src/main/java/it/basic/java/SampleJavaClass.java b/integration-tests/maven/projects/it-maven/src/main/java/it/basic/java/SampleJavaClass.java new file mode 100644 index 00000000..e08bb66a --- /dev/null +++ b/integration-tests/maven/projects/it-maven/src/main/java/it/basic/java/SampleJavaClass.java @@ -0,0 +1,22 @@ +package it.basic.java; + +import it.basic.PublicClass; + +/** + * This class is, unlike {@link PublicClass}, written in Java + */ +@SuppressWarnings("unused") +public class SampleJavaClass { + + /** + * @return Empty instance of {@link PublicClass} + */ + public PublicClass publicDocumentedFunction() { + return new PublicClass(); + } + + + public PublicClass publicUndocumentedFunction() { + return new PublicClass(); + } +} diff --git a/integration-tests/maven/projects/it-maven/src/main/kotlin/it/basic/PublicClass.kt b/integration-tests/maven/projects/it-maven/src/main/kotlin/it/basic/PublicClass.kt new file mode 100644 index 00000000..71bc7e63 --- /dev/null +++ b/integration-tests/maven/projects/it-maven/src/main/kotlin/it/basic/PublicClass.kt @@ -0,0 +1,48 @@ +@file:Suppress("unused") + +package it.basic + +class PublicClass { + /** + * This function is public and documented + */ + fun publicDocumentedFunction(): String = "" + + fun publicUndocumentedFunction(): String = "" + + /** + * This function is internal and documented + */ + internal fun internalDocumentedFunction(): String = "" + + internal fun internalUndocumentedFunction(): String = "" + + /** + * This function is private and documented + */ + private fun privateDocumentedFunction(): String = "" + + private fun privateUndocumentedFunction(): String = "" + + + /** + * This property is public and documented + */ + val publicDocumentedProperty: Int = 0 + + val publicUndocumentedProperty: Int = 0 + + /** + * This property internal and documented + */ + val internalDocumentedProperty: Int = 0 + + val internalUndocumentedProperty: Int = 0 + + /** + * This property private and documented + */ + private val privateDocumentedProperty: Int = 0 + + private val privateUndocumentedProperty: Int = 0 +} diff --git a/integration-tests/maven/src/integrationTest/kotlin/org/jetbrains/dokka/it/maven/MavenIntegrationTest.kt b/integration-tests/maven/src/integrationTest/kotlin/org/jetbrains/dokka/it/maven/MavenIntegrationTest.kt new file mode 100644 index 00000000..86cc2f41 --- /dev/null +++ b/integration-tests/maven/src/integrationTest/kotlin/org/jetbrains/dokka/it/maven/MavenIntegrationTest.kt @@ -0,0 +1,132 @@ +package org.jetbrains.dokka.it.maven + +import org.jetbrains.dokka.it.AbstractIntegrationTest +import org.jetbrains.dokka.it.awaitProcessResult +import org.jetbrains.dokka.it.ProcessResult +import java.io.File +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class MavenIntegrationTest : AbstractIntegrationTest() { + + private val currentDokkaVersion: String = checkNotNull(System.getenv("DOKKA_VERSION")) + + private val mavenBinaryFile: File = File(checkNotNull(System.getenv("MVN_BINARY_PATH"))) + + @BeforeTest + fun prepareProjectFiles() { + val templateProjectDir = File("projects", "it-maven") + templateProjectDir.copyRecursively(projectDir) + val pomXml = File(projectDir, "pom.xml") + assertTrue(pomXml.isFile) + pomXml.apply { + writeText(readText().replace("\$dokka_version", currentDokkaVersion)) + } + } + + @Test + fun `dokka dokka`() { + val result = ProcessBuilder().directory(projectDir) + .command(mavenBinaryFile.absolutePath, "dokka:dokka", "-U", "-e").start().awaitProcessResult() + + diagnosticAsserts(result) + + val dokkaOutputDir = File(projectDir, "output") + assertTrue(dokkaOutputDir.isDirectory, "Missing dokka output directory") + + val imagesDir = File(dokkaOutputDir, "images") + assertTrue(imagesDir.isDirectory, "Missing images directory") + + val scriptsDir = File(dokkaOutputDir, "scripts") + assertTrue(scriptsDir.isDirectory, "Missing scripts directory") + + val stylesDir = File(dokkaOutputDir, "styles") + assertTrue(stylesDir.isDirectory, "Missing styles directory") + + val navigationHtml = File(dokkaOutputDir, "navigation.html") + assertTrue(navigationHtml.isFile, "Missing navigation.html") + + projectDir.allHtmlFiles().forEach { file -> + assertContainsNoErrorClass(file) + assertNoUnresolvedLInks(file) + } + } + + @Test + fun `dokka javadoc`() { + val result = ProcessBuilder().directory(projectDir) + .command(mavenBinaryFile.absolutePath, "dokka:javadoc", "-U", "-e").start().awaitProcessResult() + + diagnosticAsserts(result) + + val dokkaOutputDir = File(projectDir, "output") + assertTrue(dokkaOutputDir.isDirectory, "Missing dokka output directory") + + val scriptsDir = File(dokkaOutputDir, "jquery") + assertTrue(scriptsDir.isDirectory, "Missing jquery directory") + + val stylesDir = File(dokkaOutputDir, "resources") + assertTrue(stylesDir.isDirectory, "Missing resources directory") + + projectDir.allHtmlFiles().forEach { file -> + assertContainsNoErrorClass(file) + assertNoUnresolvedLInks(file) + } + } + + @Test + fun `dokka javadocJar`() { + val result = ProcessBuilder().directory(projectDir) + .command(mavenBinaryFile.absolutePath, "dokka:javadocJar", "-U", "-e").start().awaitProcessResult() + + diagnosticAsserts(result) + + val dokkaOutputDir = File(projectDir, "output") + assertTrue(dokkaOutputDir.isDirectory, "Missing dokka output directory") + + val scriptsDir = File(dokkaOutputDir, "jquery") + assertTrue(scriptsDir.isDirectory, "Missing jquery directory") + + val stylesDir = File(dokkaOutputDir, "resources") + assertTrue(stylesDir.isDirectory, "Missing resources directory") + + val dokkaTargetDir = File(projectDir, "target") + assertTrue(dokkaOutputDir.isDirectory, "Missing dokka target directory") + + val jarFile = File(dokkaTargetDir, "it-maven-1.0-SNAPSHOT-javadoc.jar") + assertTrue(jarFile.isFile, "Missing dokka jar file") + + projectDir.allHtmlFiles().forEach { file -> + assertContainsNoErrorClass(file) + assertNoUnresolvedLInks(file) + } + } + + private fun diagnosticAsserts(result: ProcessResult) { + assertEquals(0, result.exitCode, "Expected exitCode 0 (Success)") + + val extensionLoadedRegex = Regex("""Extension: org\.jetbrains\.dokka\.base\.DokkaBase""") + val amountOfExtensionsLoaded = extensionLoadedRegex.findAll(result.output).count() + + assertTrue( + amountOfExtensionsLoaded > 10, + "Expected more than 10 extensions being present (found $amountOfExtensionsLoaded)" + ) + + val undocumentedReportRegex = Regex("""Undocumented:""") + val amountOfUndocumentedReports = undocumentedReportRegex.findAll(result.output).count() + assertTrue( + amountOfUndocumentedReports > 0, + "Expected at least one report of undocumented code (found $amountOfUndocumentedReports)" + ) + + val undocumentedJavaReportRegex = Regex("""Undocumented: it\.basic\.java""") + val amountOfUndocumentedJavaReports = undocumentedJavaReportRegex.findAll(result.output).count() + assertTrue( + amountOfUndocumentedJavaReports > 0, + "Expected at least one report of undocumented java code (found $amountOfUndocumentedJavaReports)" + ) + } +} diff --git a/integration-tests/src/main/kotlin/org/jetbrains/dokka/it/AbstractIntegrationTest.kt b/integration-tests/src/main/kotlin/org/jetbrains/dokka/it/AbstractIntegrationTest.kt new file mode 100644 index 00000000..aeebe552 --- /dev/null +++ b/integration-tests/src/main/kotlin/org/jetbrains/dokka/it/AbstractIntegrationTest.kt @@ -0,0 +1,71 @@ +package org.jetbrains.dokka.it + +import org.jsoup.Jsoup +import org.junit.Rule +import org.junit.rules.TemporaryFolder +import java.io.File +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +abstract class AbstractIntegrationTest { + + @get:Rule + val temporaryTestFolder = TemporaryFolder() + + val projectDir get() = File(temporaryTestFolder.root, "project") + + fun File.allDescendentsWithExtension(extension: String): Sequence<File> { + return this.walkTopDown().filter { it.isFile && it.extension == extension } + } + + fun File.allHtmlFiles(): Sequence<File> { + return allDescendentsWithExtension("html") + } + + protected fun assertContainsNoErrorClass(file: File) { + val fileText = file.readText() + assertFalse( + fileText.contains("ERROR CLASS", ignoreCase = true), + "Unexpected `ERROR CLASS` in ${file.path}\n" + fileText + ) + } + + protected fun assertNoUnresolvedLInks(file: File) { + val regex = Regex("[\"']#[\"']") + val fileText = file.readText() + assertFalse( + fileText.contains(regex), + "Unexpected unresolved link in ${file.path}\n" + fileText + ) + } + + protected fun assertNoHrefToMissingLocalFileOrDirectory( + file: File, fileExtensions: Set<String> = setOf("html") + ) { + val fileText = file.readText() + val html = Jsoup.parse(fileText) + html.allElements.toList().forEach { element -> + val href = (element.attr("href") ?: return@forEach) + if (href.startsWith("https")) return@forEach + if (href.startsWith("http")) return@forEach + + val hrefWithoutAnchors = if (href.contains("#")) { + val hrefSplits = href.split("#") + if (hrefSplits.count() != 2) return@forEach + hrefSplits.first() + } else href + + val targetFile = File(file.parent, hrefWithoutAnchors) + if (targetFile.extension.isNotEmpty() && targetFile.extension !in fileExtensions) return@forEach + + if ( + targetFile.extension.isEmpty() || targetFile.extension == "html" && !href.startsWith("#")) { + assertTrue( + targetFile.exists(), + "${file.relativeTo(projectDir).path}: href=\"$href\"\n" + + "file does not exist: ${targetFile.relativeTo(projectDir).path}" + ) + } + } + } +} diff --git a/integration-tests/src/main/kotlin/org/jetbrains/dokka/it/environmentUtils.kt b/integration-tests/src/main/kotlin/org/jetbrains/dokka/it/environmentUtils.kt new file mode 100644 index 00000000..eadf5a8c --- /dev/null +++ b/integration-tests/src/main/kotlin/org/jetbrains/dokka/it/environmentUtils.kt @@ -0,0 +1,16 @@ +package org.jetbrains.dokka.it + +import java.io.File + +/** + * Indicating whether or not the current machine executing the test is a CI + */ +val isCI: Boolean get() = System.getenv("CI") == "true" + +val isAndroidSdkInstalled: Boolean = System.getenv("ANDROID_SDK_ROOT") != null || + System.getenv("ANDROID_HOME") != null + +val isMavenInstalled: Boolean = System.getenv("PATH").orEmpty() + .split(File.pathSeparator) + .flatMap { pathElement -> File(pathElement).listFiles().orEmpty().toList() } + .any { pathElement -> "mvn" == pathElement.name } diff --git a/integration-tests/src/main/kotlin/org/jetbrains/dokka/it/processUtils.kt b/integration-tests/src/main/kotlin/org/jetbrains/dokka/it/processUtils.kt new file mode 100644 index 00000000..d2c048ac --- /dev/null +++ b/integration-tests/src/main/kotlin/org/jetbrains/dokka/it/processUtils.kt @@ -0,0 +1,51 @@ +package org.jetbrains.dokka.it + +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.async +import kotlinx.coroutines.runBlocking +import kotlin.concurrent.thread + +class ProcessResult( + val exitCode: Int, + val output: String +) + +fun Process.awaitProcessResult() = runBlocking { + val exitCode = async { awaitExitCode() } + val output = async { awaitOutput() } + ProcessResult( + exitCode.await(), + output.await() + ) +} + +private suspend fun Process.awaitExitCode(): Int { + val deferred = CompletableDeferred<Int>() + thread { + try { + deferred.complete(this.waitFor()) + } catch (e: Throwable) { + deferred.completeExceptionally(e) + } + } + + return deferred.await() +} + +private suspend fun Process.awaitOutput(): String { + val deferred = CompletableDeferred<String>() + thread { + try { + var string = "" + this.inputStream.bufferedReader().forEachLine { line -> + println(line) + string += line + System.lineSeparator() + } + deferred.complete(string) + } catch (e: Throwable) { + deferred.completeExceptionally(e) + } + } + + return deferred.await() +} diff --git a/integration/build.gradle b/integration/build.gradle deleted file mode 100644 index ce25d9bf..00000000 --- a/integration/build.gradle +++ /dev/null @@ -1,25 +0,0 @@ -buildscript { - repositories { jcenter() } - - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - -apply plugin: 'kotlin' - -sourceCompatibility = 1.8 - -tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { - kotlinOptions { - languageVersion = language_version - apiVersion = language_version - jvmTarget = "1.8" - } -} - -dependencies { - compileOnly group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib', version: kotlin_for_gradle_runtime_version - compileOnly group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: kotlin_for_gradle_runtime_version - implementation "com.google.code.gson:gson:$gson_version" -}
\ No newline at end of file diff --git a/integration/src/main/kotlin/org/jetbrains/dokka/DokkaBootstrap.kt b/integration/src/main/kotlin/org/jetbrains/dokka/DokkaBootstrap.kt deleted file mode 100644 index b78eb9c6..00000000 --- a/integration/src/main/kotlin/org/jetbrains/dokka/DokkaBootstrap.kt +++ /dev/null @@ -1,10 +0,0 @@ -package org.jetbrains.dokka - -import java.util.function.BiConsumer - -interface DokkaBootstrap { - - fun configure(logger: BiConsumer<String, String>, serializedConfigurationJSON: String) - - fun generate() -}
\ No newline at end of file diff --git a/kotlin-analysis/build.gradle.kts b/kotlin-analysis/build.gradle.kts new file mode 100644 index 00000000..8458ecc4 --- /dev/null +++ b/kotlin-analysis/build.gradle.kts @@ -0,0 +1,19 @@ +import org.jetbrains.registerDokkaArtifactPublication + +plugins { + id("com.github.johnrengelman.shadow") + `maven-publish` + id("com.jfrog.bintray") +} + +dependencies { + compileOnly(project(":core")) + + val kotlin_version: String by project + api("org.jetbrains.kotlin:kotlin-compiler:$kotlin_version") + api(project(":kotlin-analysis:dependencies", configuration = "shadow")) +} + +registerDokkaArtifactPublication("dokkaAnalysis") { + artifactId = "dokka-analysis" +} diff --git a/kotlin-analysis/dependencies/build.gradle.kts b/kotlin-analysis/dependencies/build.gradle.kts new file mode 100644 index 00000000..a12e697d --- /dev/null +++ b/kotlin-analysis/dependencies/build.gradle.kts @@ -0,0 +1,56 @@ +import org.jetbrains.DokkaPublicationBuilder.Component.Shadow +import org.jetbrains.registerDokkaArtifactPublication + +plugins { + id("com.github.johnrengelman.shadow") + `maven-publish` + id("com.jfrog.bintray") +} + +repositories { + maven(url = "https://www.jetbrains.com/intellij-repository/snapshots") + maven(url = "https://www.jetbrains.com/intellij-repository/releases") + maven(url = "https://kotlin.bintray.com/kotlin-plugin") +} + +val intellijCore: Configuration by configurations.creating + +fun intellijCoreAnalysis() = zipTree(intellijCore.singleFile).matching { + include("intellij-core-analysis.jar") +} + +dependencies { + val kotlin_plugin_version: String by project + api("org.jetbrains.kotlin:ide-common-ij193:$kotlin_plugin_version") + api("org.jetbrains.kotlin:kotlin-plugin-ij193:$kotlin_plugin_version") { + //TODO: parametrize ij version after 1.3.70 + isTransitive = false + } + + val idea_version: String by project + intellijCore("com.jetbrains.intellij.idea:intellij-core:$idea_version") + implementation(intellijCoreAnalysis()) +} + +tasks { + shadowJar { + val dokka_version: String by project + archiveFileName.set("dokka-kotlin-analysis-dependencies-$dokka_version.jar") + archiveClassifier.set("") + + exclude("colorScheme/**") + exclude("fileTemplates/**") + exclude("inspectionDescriptions/**") + exclude("intentionDescriptions/**") + exclude("tips/**") + exclude("messages/**") + exclude("src/**") + exclude("**/*.kotlin_metadata") + exclude("**/*.kotlin_builtins") + } +} + +registerDokkaArtifactPublication("kotlinAnalysisDependencies"){ + artifactId = "kotlin-analysis-dependencies" + component = Shadow +} diff --git a/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/AnalysisEnvironment.kt index f5705c89..c39621f9 100644 --- a/core/src/main/kotlin/Analysis/AnalysisEnvironment.kt +++ b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/AnalysisEnvironment.kt @@ -1,12 +1,10 @@ -package org.jetbrains.dokka +package org.jetbrains.dokka.analysis import com.google.common.collect.ImmutableMap import com.intellij.core.CoreApplicationEnvironment import com.intellij.core.CoreModuleManager -import com.intellij.mock.MockApplication import com.intellij.mock.MockComponentManager import com.intellij.openapi.Disposable -import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.extensions.Extensions import com.intellij.openapi.module.Module import com.intellij.openapi.module.ModuleManager @@ -17,7 +15,12 @@ import com.intellij.openapi.roots.ProjectRootManager import com.intellij.openapi.util.Disposer import com.intellij.openapi.vfs.StandardFileSystems import com.intellij.psi.PsiElement +import com.intellij.psi.impl.source.javadoc.JavadocManagerImpl +import com.intellij.psi.javadoc.CustomJavadocTagProvider +import com.intellij.psi.javadoc.JavadocManager +import com.intellij.psi.javadoc.JavadocTagInfo import com.intellij.psi.search.GlobalSearchScope +import org.jetbrains.dokka.Platform import org.jetbrains.kotlin.analyzer.* import org.jetbrains.kotlin.analyzer.common.CommonAnalysisParameters import org.jetbrains.kotlin.analyzer.common.CommonPlatformAnalyzerServices @@ -25,7 +28,7 @@ import org.jetbrains.kotlin.analyzer.common.CommonResolverForModuleFactory import org.jetbrains.kotlin.builtins.DefaultBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.jvm.JvmBuiltIns -import org.jetbrains.kotlin.caches.resolve.KotlinCacheService +import org.jetbrains.kotlin.caches.resolve.* import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys import org.jetbrains.kotlin.cli.common.config.ContentRoot import org.jetbrains.kotlin.cli.common.config.KotlinSourceRoot @@ -45,22 +48,28 @@ import org.jetbrains.kotlin.context.withModule import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl -import org.jetbrains.kotlin.ide.konan.NativeLibraryInfo +import org.jetbrains.kotlin.descriptors.konan.KlibModuleOrigin +import org.jetbrains.kotlin.extensions.ApplicationExtensionDescriptor +import org.jetbrains.kotlin.ide.konan.NativePlatformKindResolution import org.jetbrains.kotlin.ide.konan.analyzer.NativeResolverForModuleFactory -import org.jetbrains.kotlin.ide.konan.decompiler.KotlinNativeLoadingMetadataCache import org.jetbrains.kotlin.idea.resolve.ResolutionFacade import org.jetbrains.kotlin.js.config.JSConfigurationKeys import org.jetbrains.kotlin.js.resolve.JsPlatformAnalyzerServices -import org.jetbrains.kotlin.js.resolve.JsResolverForModuleFactory import org.jetbrains.kotlin.library.impl.createKotlinLibrary import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl +import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaClass import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.platform.CommonPlatforms +import org.jetbrains.kotlin.platform.IdePlatformKind import org.jetbrains.kotlin.platform.TargetPlatform +import org.jetbrains.kotlin.platform.impl.CommonIdePlatformKind +import org.jetbrains.kotlin.platform.impl.JsIdePlatformKind +import org.jetbrains.kotlin.platform.impl.JvmIdePlatformKind +import org.jetbrains.kotlin.platform.impl.NativeIdePlatformKind import org.jetbrains.kotlin.platform.js.JsPlatforms import org.jetbrains.kotlin.platform.jvm.JvmPlatforms import org.jetbrains.kotlin.platform.jvm.JvmPlatforms.unspecifiedJvmPlatform -import org.jetbrains.kotlin.platform.konan.KonanPlatforms +import org.jetbrains.kotlin.platform.konan.NativePlatforms import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingTrace @@ -97,7 +106,7 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl } fun createCoreEnvironment(): KotlinCoreEnvironment { - System.setProperty("idea.io.use.fallback", "true") + System.setProperty("idea.io.use.nio2", "true") val configFiles = when (analysisPlatform) { Platform.jvm, Platform.common -> EnvironmentConfigFiles.JVM_CONFIG_FILES @@ -122,12 +131,21 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl ModuleManager::class.java, moduleManager ) - Extensions.registerAreaClass("IDEA_MODULE", null) CoreApplicationEnvironment.registerExtensionPoint( Extensions.getRootArea(), OrderEnumerationHandler.EP_NAME, OrderEnumerationHandler.Factory::class.java ) + CoreApplicationEnvironment.registerExtensionPoint( + environment.project.extensionArea, + JavadocTagInfo.EP_NAME, JavadocTagInfo::class.java + ) + + CoreApplicationEnvironment.registerExtensionPoint( + Extensions.getRootArea(), + CustomJavadocTagProvider.EP_NAME, CustomJavadocTagProvider::class.java + ) + projectComponentManager.registerService( ProjectFileIndex::class.java, projectFileIndex @@ -138,6 +156,36 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl CoreProjectRootManager(projectFileIndex) ) + projectComponentManager.registerService( + JavadocManager::class.java, + JavadocManagerImpl(environment.project) + ) + + projectComponentManager.registerService( + CustomJavadocTagProvider::class.java, + CustomJavadocTagProvider { emptyList() } + ) + + registerExtensionPoint( + ApplicationExtensionDescriptor("org.jetbrains.kotlin.idePlatformKind", IdePlatformKind::class.java), + listOf( + CommonIdePlatformKind, + JvmIdePlatformKind, + JsIdePlatformKind, + NativeIdePlatformKind + ) + ) + + registerExtensionPoint( + IdePlatformKindResolution, + listOf( + CommonPlatformKindResolution(), + JvmPlatformKindResolution(), + JsPlatformKindResolution(), + NativePlatformKindResolution() + ) + ) + return environment } @@ -158,7 +206,7 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl val targetPlatform = when (analysisPlatform) { Platform.js -> JsPlatforms.defaultJsPlatform Platform.common -> CommonPlatforms.defaultCommonPlatform - Platform.native -> KonanPlatforms.defaultKonanPlatform + Platform.native -> NativePlatforms.unspecifiedNativePlatform Platform.jvm -> JvmPlatforms.defaultJvmPlatform } @@ -229,10 +277,21 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl resolverForProject.resolverForModule(library) // Required before module to initialize library properly val resolverForModule = resolverForProject.resolverForModule(module) val libraryResolutionFacade = - DokkaResolutionFacade(environment.project, libraryModuleDescriptor, resolverForLibrary) - val created = DokkaResolutionFacade(environment.project, moduleDescriptor, resolverForModule) + DokkaResolutionFacade( + environment.project, + libraryModuleDescriptor, + resolverForLibrary + ) + val created = + DokkaResolutionFacade( + environment.project, + moduleDescriptor, + resolverForModule + ) val projectComponentManager = environment.project as MockComponentManager - projectComponentManager.registerService(KotlinCacheService::class.java, CoreKotlinCacheService(created)) + projectComponentManager.registerService(KotlinCacheService::class.java, + CoreKotlinCacheService(created) + ) return created to libraryResolutionFacade } @@ -245,16 +304,16 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl } private fun createNativeLibraryModuleInfo(libraryFile: File): LibraryModuleInfo { - val kotlinLibrary = createKotlinLibrary(org.jetbrains.kotlin.konan.file.File(libraryFile.absolutePath), false) + val kotlinLibrary = createKotlinLibrary(org.jetbrains.kotlin.konan.file.File(libraryFile.absolutePath), "",false) return object : LibraryModuleInfo { override val analyzerServices: PlatformDependentAnalyzerServices = analysisPlatform.analyzerServices() override val name: Name = Name.special("<klib>") - override val platform: TargetPlatform = KonanPlatforms.defaultKonanPlatform + override val platform: TargetPlatform = NativePlatforms.unspecifiedNativePlatform override fun dependencies(): List<ModuleInfo> = listOf(this) override fun getLibraryRoots(): Collection<String> = listOf(libraryFile.absolutePath) override val capabilities: Map<ModuleDescriptor.Capability<*>, Any?> - get() = super.capabilities + (NativeLibraryInfo.NATIVE_LIBRARY_CAPABILITY to kotlinLibrary) + get() = super.capabilities + (KlibModuleOrigin.CAPABILITY to kotlinLibrary) } } @@ -344,15 +403,11 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl descriptor: ModuleDescriptor, moduleInfo: ModuleInfo ): ResolverForModule { - (ApplicationManager.getApplication() as MockApplication).addComponent( - KotlinNativeLoadingMetadataCache::class.java, - KotlinNativeLoadingMetadataCache() - ) return NativeResolverForModuleFactory( PlatformAnalysisParameters.Empty, CompilerEnvironment, - KonanPlatforms.defaultKonanPlatform + NativePlatforms.unspecifiedNativePlatform ).createResolverForModule( descriptor as ModuleDescriptorImpl, projectContext.withModule(descriptor), @@ -379,7 +434,7 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl val javaRoots = classpath .mapNotNull { val rootFile = when (it.extension) { - "jar" -> StandardFileSystems.jar().findFileByPath("${it.absolutePath}${JAR_SEPARATOR}") + "jar" -> StandardFileSystems.jar().findFileByPath("${it.absolutePath}$JAR_SEPARATOR") else -> StandardFileSystems.local().findFileByPath(it.absolutePath) } rootFile?.let { JavaRoot(it, JavaRoot.RootType.BINARY) } @@ -412,14 +467,14 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl addRoots(javaRoots, messageCollector) } }, { - val file = (it as JavaClassImpl).psi.containingFile.virtualFile + val file = (it as? BinaryJavaClass)?.virtualFile ?: (it as JavaClassImpl).psi.containingFile.virtualFile if (file in sourcesScope) module else library }), CompilerEnvironment, - KonanPlatforms.defaultKonanPlatform + unspecifiedJvmPlatform ).createResolverForModule( descriptor as ModuleDescriptorImpl, projectContext.withModule(descriptor), @@ -499,6 +554,19 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl override fun dispose() { Disposer.dispose(this) } + + companion object { + private fun <T : Any> registerExtensionPoint( + appExtension: ApplicationExtensionDescriptor<T>, + instances: List<T> + ) { + if (Extensions.getRootArea().hasExtensionPoint(appExtension.extensionPointName)) + return + + appExtension.registerExtensionPoint() + instances.forEach(appExtension::registerExtension) + } + } } fun contentRootFromPath(path: String): ContentRoot { @@ -589,4 +657,8 @@ class DokkaResolutionFacade( throw UnsupportedOperationException() } + override fun getResolverForProject(): ResolverForProject<out ModuleInfo> { + throw UnsupportedOperationException() + } + } diff --git a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/CallableFactory.kt b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/CallableFactory.kt new file mode 100644 index 00000000..ebfe20a5 --- /dev/null +++ b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/CallableFactory.kt @@ -0,0 +1,31 @@ +package org.jetbrains.dokka.analysis + +import com.intellij.psi.PsiField +import com.intellij.psi.PsiMethod +import org.jetbrains.dokka.links.Callable +import org.jetbrains.dokka.links.JavaClassReference +import org.jetbrains.dokka.links.TypeReference +import org.jetbrains.kotlin.descriptors.CallableDescriptor + +fun Callable.Companion.from(descriptor: CallableDescriptor) = with(descriptor) { + Callable( + name.asString(), + extensionReceiverParameter?.let { TypeReference.from(it) }, + valueParameters.mapNotNull { TypeReference.from(it) } + ) +} + +fun Callable.Companion.from(psi: PsiMethod) = with(psi) { + Callable( + name, + null, + parameterList.parameters.map { param -> JavaClassReference(param.type.canonicalText) }) +} + +fun Callable.Companion.from(psi: PsiField): Callable { + return Callable( + name = psi.name, + receiver = null, + params = emptyList() + ) +} diff --git a/core/src/main/kotlin/Analysis/CoreKotlinCacheService.kt b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/CoreKotlinCacheService.kt index d9093760..68415875 100644 --- a/core/src/main/kotlin/Analysis/CoreKotlinCacheService.kt +++ b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/CoreKotlinCacheService.kt @@ -1,6 +1,7 @@ -package org.jetbrains.dokka +package org.jetbrains.dokka.analysis import com.intellij.psi.PsiFile +import org.jetbrains.dokka.analysis.DokkaResolutionFacade import org.jetbrains.kotlin.analyzer.ModuleInfo import org.jetbrains.kotlin.caches.resolve.KotlinCacheService import org.jetbrains.kotlin.idea.resolve.ResolutionFacade @@ -39,4 +40,3 @@ class CoreKotlinCacheService(private val resolutionFacade: DokkaResolutionFacade } } - diff --git a/core/src/main/kotlin/Analysis/CoreProjectFileIndex.kt b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/CoreProjectFileIndex.kt index ef45e840..d0e0bb4f 100644 --- a/core/src/main/kotlin/Analysis/CoreProjectFileIndex.kt +++ b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/CoreProjectFileIndex.kt @@ -1,4 +1,4 @@ -package org.jetbrains.dokka +package org.jetbrains.dokka.analysis import com.intellij.openapi.Disposable import com.intellij.openapi.components.BaseComponent @@ -53,14 +53,6 @@ class CoreProjectFileIndex(private val project: Project, contentRoots: List<Cont throw UnsupportedOperationException() } - override fun getOptionValue(p0: String): String? { - throw UnsupportedOperationException() - } - - override fun clearOption(p0: String) { - throw UnsupportedOperationException() - } - override fun getName(): String = "<Dokka module>" override fun getModuleWithLibrariesScope(): GlobalSearchScope { @@ -71,18 +63,6 @@ class CoreProjectFileIndex(private val project: Project, contentRoots: List<Cont throw UnsupportedOperationException() } - override fun getModuleContentScope(): GlobalSearchScope { - throw UnsupportedOperationException() - } - - override fun isLoaded(): Boolean { - throw UnsupportedOperationException() - } - - override fun setOption(p0: String, p1: String?) { - throw UnsupportedOperationException() - } - override fun getModuleWithDependenciesScope(): GlobalSearchScope { throw UnsupportedOperationException() } @@ -91,16 +71,6 @@ class CoreProjectFileIndex(private val project: Project, contentRoots: List<Cont throw UnsupportedOperationException() } - override fun getProject(): Project = this@CoreProjectFileIndex.project - - override fun getModuleContentWithDependenciesScope(): GlobalSearchScope { - throw UnsupportedOperationException() - } - - override fun getModuleFilePath(): String { - throw UnsupportedOperationException() - } - override fun getModuleTestsWithDependentsScope(): GlobalSearchScope { throw UnsupportedOperationException() } @@ -109,30 +79,14 @@ class CoreProjectFileIndex(private val project: Project, contentRoots: List<Cont throw UnsupportedOperationException() } - override fun getModuleScope(p0: Boolean): GlobalSearchScope { - throw UnsupportedOperationException() - } - override fun getModuleRuntimeScope(p0: Boolean): GlobalSearchScope { throw UnsupportedOperationException() } - override fun getModuleFile(): VirtualFile? { - throw UnsupportedOperationException() - } - override fun <T : Any?> getExtensions(p0: ExtensionPointName<T>): Array<out T> { throw UnsupportedOperationException() } - override fun getComponent(p0: String): BaseComponent? { - throw UnsupportedOperationException() - } - - override fun <T : Any?> getComponent(p0: Class<T>, p1: T): T { - throw UnsupportedOperationException() - } - override fun <T : Any?> getComponent(interfaceClass: Class<T>): T? { if (interfaceClass == ModuleRootManager::class.java) { return moduleRootManager as T @@ -144,18 +98,10 @@ class CoreProjectFileIndex(private val project: Project, contentRoots: List<Cont throw UnsupportedOperationException() } - override fun <T : Any?> getComponents(p0: Class<T>): Array<out T> { - throw UnsupportedOperationException() - } - override fun getPicoContainer(): PicoContainer { throw UnsupportedOperationException() } - override fun hasComponent(p0: Class<*>): Boolean { - throw UnsupportedOperationException() - } - override fun getMessageBus(): MessageBus { throw UnsupportedOperationException() } diff --git a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/DRIFactory.kt b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/DRIFactory.kt new file mode 100644 index 00000000..513817f3 --- /dev/null +++ b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/DRIFactory.kt @@ -0,0 +1,38 @@ +package org.jetbrains.dokka.analysis + +import com.intellij.psi.PsiClass +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiField +import com.intellij.psi.PsiMethod +import org.jetbrains.dokka.links.Callable +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.links.DriTarget +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf +import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf +import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull + +fun DRI.Companion.from(descriptor: DeclarationDescriptor) = descriptor.parentsWithSelf.run { + val callable = firstIsInstanceOrNull<CallableDescriptor>() + DRI( + firstIsInstanceOrNull<PackageFragmentDescriptor>()?.fqName?.asString(), + (filterIsInstance<ClassDescriptor>() + filterIsInstance<TypeAliasDescriptor>()).toList() + .takeIf { it.isNotEmpty() } + ?.asReversed() + ?.joinToString(separator = ".") { it.name.asString() }, + callable?.let { Callable.from(it) }, + DriTarget.from(descriptor) + ) +} + +fun DRI.Companion.from(psi: PsiElement) = psi.parentsWithSelf.run { + val psiMethod = firstIsInstanceOrNull<PsiMethod>() + val psiField = firstIsInstanceOrNull<PsiField>() + val classes = filterIsInstance<PsiClass>().toList() + DRI( + classes.lastOrNull()?.qualifiedName?.substringBeforeLast('.', ""), + classes.toList().takeIf { it.isNotEmpty() }?.asReversed()?.mapNotNull { it.name }?.joinToString("."), + psiMethod?.let { Callable.from(it) } ?: psiField?.let { Callable.from(it) }, + DriTarget.from(psi) + ) +} diff --git a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/DRITargetFactory.kt b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/DRITargetFactory.kt new file mode 100644 index 00000000..031a5a32 --- /dev/null +++ b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/DRITargetFactory.kt @@ -0,0 +1,44 @@ +package org.jetbrains.dokka.analysis + +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiMethod +import com.intellij.psi.PsiParameter +import com.intellij.psi.PsiTypeParameter +import org.jetbrains.dokka.links.DriTarget +import org.jetbrains.dokka.links.PointingToCallableParameters +import org.jetbrains.dokka.links.PointingToDeclaration +import org.jetbrains.dokka.links.PointingToGenericParameters +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.ParameterDescriptor +import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor +import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf +import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf +import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull + +fun DriTarget.Companion.from(descriptor: DeclarationDescriptor): DriTarget = descriptor.parentsWithSelf.run { + return when (descriptor) { + is TypeParameterDescriptor -> PointingToGenericParameters(descriptor.index) + else -> { + val callable = firstIsInstanceOrNull<CallableDescriptor>() + val params = + callable?.let { listOfNotNull(it.extensionReceiverParameter) + it.valueParameters }.orEmpty() + val parameterDescriptor = firstIsInstanceOrNull<ParameterDescriptor>() + + parameterDescriptor?.let { PointingToCallableParameters(params.indexOf(it)) } + ?: PointingToDeclaration + } + } +} + + +fun DriTarget.Companion.from(psi: PsiElement): DriTarget = psi.parentsWithSelf.run { + return when (psi) { + is PsiTypeParameter -> PointingToGenericParameters(psi.index) + else -> firstIsInstanceOrNull<PsiParameter>()?.let { + val callable = firstIsInstanceOrNull<PsiMethod>() + val params = (callable?.parameterList?.parameters).orEmpty() + PointingToCallableParameters(params.indexOf(it)) + } ?: PointingToDeclaration + } +} diff --git a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/Documentable.kt b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/Documentable.kt new file mode 100644 index 00000000..0c55fed4 --- /dev/null +++ b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/Documentable.kt @@ -0,0 +1,14 @@ +package org.jetbrains.dokka.analysis + +import com.intellij.psi.PsiNamedElement +import org.jetbrains.dokka.model.DocumentableSource +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.load.kotlin.toSourceElement + +class DescriptorDocumentableSource(val descriptor: DeclarationDescriptor) : DocumentableSource { + override val path = descriptor.toSourceElement.containingFile.toString() +} + +class PsiDocumentableSource(val psi: PsiNamedElement) : DocumentableSource { + override val path = psi.containingFile.virtualFile.path +} diff --git a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/EnvironmentAndFacade.kt b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/EnvironmentAndFacade.kt new file mode 100644 index 00000000..d9a89194 --- /dev/null +++ b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/EnvironmentAndFacade.kt @@ -0,0 +1,56 @@ +package org.jetbrains.dokka.analysis + +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.Platform +import org.jetbrains.dokka.utilities.DokkaLogger +import org.jetbrains.kotlin.cli.common.messages.* +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment +import org.jetbrains.kotlin.utils.PathUtil +import java.io.File + +internal fun createEnvironmentAndFacade( + logger: DokkaLogger, + configuration: DokkaConfiguration, + sourceSet: DokkaConfiguration.DokkaSourceSet +): EnvironmentAndFacade = + AnalysisEnvironment(DokkaMessageCollector(logger), sourceSet.analysisPlatform).run { + if (analysisPlatform == Platform.jvm) { + addClasspath(PathUtil.getJdkClassesRootsFromCurrentJre()) + } + sourceSet.classpath.forEach { addClasspath(File(it)) } + + addSources( + (sourceSet.sourceRoots + configuration.sourceSets.filter { it.sourceSetID in sourceSet.dependentSourceSets } + .flatMap { it.sourceRoots }) + .map { it.path } + ) + + loadLanguageVersionSettings(sourceSet.languageVersion, sourceSet.apiVersion) + + val environment = createCoreEnvironment() + val (facade, _) = createResolutionFacade(environment) + EnvironmentAndFacade(environment, facade) + } + +class DokkaMessageCollector(private val logger: DokkaLogger) : MessageCollector { + override fun clear() { + seenErrors = false + } + + private var seenErrors = false + + override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageSourceLocation?) { + if (severity == CompilerMessageSeverity.ERROR) { + seenErrors = true + } + logger.info(MessageRenderer.PLAIN_FULL_PATHS.render(severity, message, location)) + } + + override fun hasErrors() = seenErrors +} + +// It is not data class due to ill-defined equals +class EnvironmentAndFacade(val environment: KotlinCoreEnvironment, val facade: DokkaResolutionFacade) { + operator fun component1() = environment + operator fun component2() = facade +} diff --git a/core/src/main/kotlin/Analysis/JavaResolveExtension.kt b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/JavaResolveExtension.kt index 4a4c78e5..ab6bec9c 100644 --- a/core/src/main/kotlin/Analysis/JavaResolveExtension.kt +++ b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/JavaResolveExtension.kt @@ -16,7 +16,7 @@ @file:JvmName("JavaResolutionUtils") -package org.jetbrains.dokka +package org.jetbrains.dokka.analysis import com.intellij.psi.* import org.jetbrains.kotlin.asJava.unwrapped diff --git a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/KotlinAnalysis.kt b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/KotlinAnalysis.kt new file mode 100644 index 00000000..e723768c --- /dev/null +++ b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/KotlinAnalysis.kt @@ -0,0 +1,39 @@ +@file:Suppress("FunctionName") + +package org.jetbrains.dokka.analysis + +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.DokkaSourceSetID +import org.jetbrains.dokka.model.SourceSetDependent +import org.jetbrains.dokka.plugability.DokkaContext + +fun KotlinAnalysis(context: DokkaContext): KotlinAnalysis { + val environments = context.configuration.sourceSets + .associate { sourceSet -> + sourceSet to createEnvironmentAndFacade( + logger = context.logger, + configuration = context.configuration, + sourceSet = sourceSet + ) + } + + return KotlinAnalysisImpl(environments) +} + +interface KotlinAnalysis : SourceSetDependent<EnvironmentAndFacade> { + override fun get(key: DokkaSourceSet): EnvironmentAndFacade + operator fun get(sourceSetID: DokkaSourceSetID): EnvironmentAndFacade +} + +internal class KotlinAnalysisImpl( + private val environments: SourceSetDependent<EnvironmentAndFacade> +) : KotlinAnalysis, SourceSetDependent<EnvironmentAndFacade> by environments { + + override fun get(key: DokkaSourceSet): EnvironmentAndFacade { + return environments[key] ?: throw IllegalStateException("Missing EnvironmentAndFacade for sourceSet $key") + } + + override fun get(sourceSetID: DokkaSourceSetID): EnvironmentAndFacade { + return environments.entries.first { (sourceSet, _) -> sourceSet.sourceSetID == sourceSetID }.value + } +} diff --git a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/TypeReferenceFactory.kt b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/TypeReferenceFactory.kt new file mode 100644 index 00000000..e07672d4 --- /dev/null +++ b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/TypeReferenceFactory.kt @@ -0,0 +1,51 @@ +package org.jetbrains.dokka.analysis + +import com.intellij.psi.PsiClass +import org.jetbrains.dokka.links.* +import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor +import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.resolve.scopes.receivers.ExtensionReceiver +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.TypeProjection + +fun TypeReference.Companion.from(d: ReceiverParameterDescriptor): TypeReference? = + when (d.value) { + is ExtensionReceiver -> fromPossiblyNullable(d.type) + else -> run { + println("Unknown value type for $d") + null + } + } + +fun TypeReference.Companion.from(d: ValueParameterDescriptor): TypeReference? = + fromPossiblyNullable(d.type) + +fun TypeReference.Companion.from(p: PsiClass) = TypeReference + +private fun TypeReference.Companion.fromPossiblyNullable(t: KotlinType, self: KotlinType? = null): TypeReference = + from(t, self).let { if (t.isMarkedNullable) Nullable(it) else it } + +private fun TypeReference.Companion.from(t: KotlinType, self: KotlinType? = null): TypeReference = + if (self is KotlinType && self.constructor == t.constructor && self.arguments == t.arguments) + SelfType + else when (val d = t.constructor.declarationDescriptor) { + is TypeParameterDescriptor -> TypeParam( + d.upperBounds.map { fromPossiblyNullable(it, self ?: t) } + ) + else -> TypeConstructor( + t.constructorName.orEmpty(), + t.arguments.map { fromProjection(it, self) } + ) + } + +private fun TypeReference.Companion.fromProjection(t: TypeProjection, r: KotlinType? = null): TypeReference = + if (t.isStarProjection) { + StarProjection + } else { + fromPossiblyNullable(t.type, r) + } + +private val KotlinType.constructorName + get() = constructor.declarationDescriptor?.fqNameSafe?.asString() diff --git a/plugins/.gitignore b/plugins/.gitignore new file mode 100644 index 00000000..b9e934b8 --- /dev/null +++ b/plugins/.gitignore @@ -0,0 +1,2 @@ +base/src/main/resources/dokka/scripts/main.js +base/src/main/resources/dokka/scripts/main.js.map
\ No newline at end of file diff --git a/plugins/base/.gitignore b/plugins/base/.gitignore new file mode 100644 index 00000000..d68571db --- /dev/null +++ b/plugins/base/.gitignore @@ -0,0 +1,3 @@ +src/main/resources/dokka/scripts/main.js +src/main/resources/dokka/scripts/main.js.map +search-component/dist/
\ No newline at end of file diff --git a/plugins/base/build.gradle.kts b/plugins/base/build.gradle.kts new file mode 100644 index 00000000..0fc7f55c --- /dev/null +++ b/plugins/base/build.gradle.kts @@ -0,0 +1,35 @@ +import org.jetbrains.registerDokkaArtifactPublication + +plugins { + id("com.jfrog.bintray") +} + +dependencies { + val coroutines_version: String by project + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version") + + api(project(":kotlin-analysis")) + implementation("org.jsoup:jsoup:1.12.1") + implementation("org.jetbrains.kotlinx:kotlinx-html-jvm:0.6.10") + testImplementation(project(":test-tools")) + testImplementation(project(":plugins:base:test-utils")) +} + +task("copy_frontend", Copy::class) { + from(File(project(":plugins:base:frontend").projectDir, "dist/")) + destinationDir = File(sourceSets.main.get().resources.sourceDirectories.singleFile, "dokka/scripts") +}.dependsOn(":plugins:base:frontend:generateFrontendFiles") + +tasks { + processResources { + dependsOn("copy_frontend") + } + + test { + maxHeapSize = "4G" + } +} + +registerDokkaArtifactPublication("dokkaBase") { + artifactId = "dokka-base" +} diff --git a/plugins/base/frontend/.gitignore b/plugins/base/frontend/.gitignore new file mode 100644 index 00000000..81ce55f2 --- /dev/null +++ b/plugins/base/frontend/.gitignore @@ -0,0 +1,2 @@ +/dist/ +/node_modules/
\ No newline at end of file diff --git a/plugins/base/frontend/build.gradle.kts b/plugins/base/frontend/build.gradle.kts new file mode 100644 index 00000000..a184c296 --- /dev/null +++ b/plugins/base/frontend/build.gradle.kts @@ -0,0 +1,20 @@ +plugins { + id("com.github.node-gradle.node") version "2.2.4" +} + +val npmRunBuild = tasks.getByName("npm_run_build") { + inputs.dir(file("src/main")) + inputs.files(file("package.json"), file("webpack.config.js")) + outputs.dir(file("dist/")) + outputs.cacheIf { true } +} + +task("generateFrontendFiles") { + dependsOn(npmRunBuild) +} + +tasks { + clean { + delete(file("node_modules"), file("dist")) + } +} diff --git a/plugins/base/frontend/package-lock.json b/plugins/base/frontend/package-lock.json new file mode 100644 index 00000000..3c00ffb1 --- /dev/null +++ b/plugins/base/frontend/package-lock.json @@ -0,0 +1,15428 @@ +{ + "name": "search", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/code-frame": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.1.tgz", + "integrity": "sha512-IGhtTmpjGbYzcEDOw7DcQtbQSXcG9ftmAXtWTu9V936vDye4xjjekktFAtgZsWpzTj/X01jocB46mTywm/4SZw==", + "requires": { + "@babel/highlight": "^7.10.1" + } + }, + "@babel/compat-data": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.10.1.tgz", + "integrity": "sha512-CHvCj7So7iCkGKPRFUfryXIkU2gSBw7VSZFYLsqVhrS47269VK2Hfi9S/YcublPMW8k1u2bQBlbDruoQEm4fgw==", + "requires": { + "browserslist": "^4.12.0", + "invariant": "^2.2.4", + "semver": "^5.5.0" + } + }, + "@babel/core": { + "version": "7.10.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.10.2.tgz", + "integrity": "sha512-KQmV9yguEjQsXqyOUGKjS4+3K8/DlOCE2pZcq4augdQmtTy5iv5EHtmMSJ7V4c1BIPjuwtZYqYLCq9Ga+hGBRQ==", + "requires": { + "@babel/code-frame": "^7.10.1", + "@babel/generator": "^7.10.2", + "@babel/helper-module-transforms": "^7.10.1", + "@babel/helpers": "^7.10.1", + "@babel/parser": "^7.10.2", + "@babel/template": "^7.10.1", + "@babel/traverse": "^7.10.1", + "@babel/types": "^7.10.2", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.13", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + } + }, + "@babel/generator": { + "version": "7.10.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.10.2.tgz", + "integrity": "sha512-AxfBNHNu99DTMvlUPlt1h2+Hn7knPpH5ayJ8OqDWSeLld+Fi2AYBTC/IejWDM9Edcii4UzZRCsbUt0WlSDsDsA==", + "requires": { + "@babel/types": "^7.10.2", + "jsesc": "^2.5.1", + "lodash": "^4.17.13", + "source-map": "^0.5.0" + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.1.tgz", + "integrity": "sha512-ewp3rvJEwLaHgyWGe4wQssC2vjks3E80WiUe2BpMb0KhreTjMROCbxXcEovTrbeGVdQct5VjQfrv9EgC+xMzCw==", + "requires": { + "@babel/types": "^7.10.1" + } + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.1.tgz", + "integrity": "sha512-cQpVq48EkYxUU0xozpGCLla3wlkdRRqLWu1ksFMXA9CM5KQmyyRpSEsYXbao7JUkOw/tAaYKCaYyZq6HOFYtyw==", + "requires": { + "@babel/helper-explode-assignable-expression": "^7.10.1", + "@babel/types": "^7.10.1" + } + }, + "@babel/helper-builder-react-jsx": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.10.1.tgz", + "integrity": "sha512-KXzzpyWhXgzjXIlJU1ZjIXzUPdej1suE6vzqgImZ/cpAsR/CC8gUcX4EWRmDfWz/cs6HOCPMBIJ3nKoXt3BFuw==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.1", + "@babel/types": "^7.10.1" + } + }, + "@babel/helper-builder-react-jsx-experimental": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-react-jsx-experimental/-/helper-builder-react-jsx-experimental-7.10.1.tgz", + "integrity": "sha512-irQJ8kpQUV3JasXPSFQ+LCCtJSc5ceZrPFVj6TElR6XCHssi3jV8ch3odIrNtjJFRZZVbrOEfJMI79TPU/h1pQ==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.1", + "@babel/helper-module-imports": "^7.10.1", + "@babel/types": "^7.10.1" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.10.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.10.2.tgz", + "integrity": "sha512-hYgOhF4To2UTB4LTaZepN/4Pl9LD4gfbJx8A34mqoluT8TLbof1mhUlYuNWTEebONa8+UlCC4X0TEXu7AOUyGA==", + "requires": { + "@babel/compat-data": "^7.10.1", + "browserslist": "^4.12.0", + "invariant": "^2.2.4", + "levenary": "^1.1.1", + "semver": "^5.5.0" + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.10.2", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.2.tgz", + "integrity": "sha512-5C/QhkGFh1vqcziq1vAL6SI9ymzUp8BCYjFpvYVhWP4DlATIb3u5q3iUd35mvlyGs8fO7hckkW7i0tmH+5+bvQ==", + "requires": { + "@babel/helper-function-name": "^7.10.1", + "@babel/helper-member-expression-to-functions": "^7.10.1", + "@babel/helper-optimise-call-expression": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1", + "@babel/helper-replace-supers": "^7.10.1", + "@babel/helper-split-export-declaration": "^7.10.1" + } + }, + "@babel/helper-create-regexp-features-plugin": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.1.tgz", + "integrity": "sha512-Rx4rHS0pVuJn5pJOqaqcZR4XSgeF9G/pO/79t+4r7380tXFJdzImFnxMU19f83wjSrmKHq6myrM10pFHTGzkUA==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.1", + "@babel/helper-regex": "^7.10.1", + "regexpu-core": "^4.7.0" + } + }, + "@babel/helper-define-map": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.10.1.tgz", + "integrity": "sha512-+5odWpX+OnvkD0Zmq7panrMuAGQBu6aPUgvMzuMGo4R+jUOvealEj2hiqI6WhxgKrTpFoFj0+VdsuA8KDxHBDg==", + "requires": { + "@babel/helper-function-name": "^7.10.1", + "@babel/types": "^7.10.1", + "lodash": "^4.17.13" + } + }, + "@babel/helper-explode-assignable-expression": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.10.1.tgz", + "integrity": "sha512-vcUJ3cDjLjvkKzt6rHrl767FeE7pMEYfPanq5L16GRtrXIoznc0HykNW2aEYkcnP76P0isoqJ34dDMFZwzEpJg==", + "requires": { + "@babel/traverse": "^7.10.1", + "@babel/types": "^7.10.1" + } + }, + "@babel/helper-function-name": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.1.tgz", + "integrity": "sha512-fcpumwhs3YyZ/ttd5Rz0xn0TpIwVkN7X0V38B9TWNfVF42KEkhkAAuPCQ3oXmtTRtiPJrmZ0TrfS0GKF0eMaRQ==", + "requires": { + "@babel/helper-get-function-arity": "^7.10.1", + "@babel/template": "^7.10.1", + "@babel/types": "^7.10.1" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.1.tgz", + "integrity": "sha512-F5qdXkYGOQUb0hpRaPoetF9AnsXknKjWMZ+wmsIRsp5ge5sFh4c3h1eH2pRTTuy9KKAA2+TTYomGXAtEL2fQEw==", + "requires": { + "@babel/types": "^7.10.1" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.1.tgz", + "integrity": "sha512-vLm5srkU8rI6X3+aQ1rQJyfjvCBLXP8cAGeuw04zeAM2ItKb1e7pmVmLyHb4sDaAYnLL13RHOZPLEtcGZ5xvjg==", + "requires": { + "@babel/types": "^7.10.1" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.10.1.tgz", + "integrity": "sha512-u7XLXeM2n50gb6PWJ9hoO5oO7JFPaZtrh35t8RqKLT1jFKj9IWeD1zrcrYp1q1qiZTdEarfDWfTIP8nGsu0h5g==", + "requires": { + "@babel/types": "^7.10.1" + } + }, + "@babel/helper-module-imports": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.1.tgz", + "integrity": "sha512-SFxgwYmZ3HZPyZwJRiVNLRHWuW2OgE5k2nrVs6D9Iv4PPnXVffuEHy83Sfx/l4SqF+5kyJXjAyUmrG7tNm+qVg==", + "requires": { + "@babel/types": "^7.10.1" + } + }, + "@babel/helper-module-transforms": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.10.1.tgz", + "integrity": "sha512-RLHRCAzyJe7Q7sF4oy2cB+kRnU4wDZY/H2xJFGof+M+SJEGhZsb+GFj5j1AD8NiSaVBJ+Pf0/WObiXu/zxWpFg==", + "requires": { + "@babel/helper-module-imports": "^7.10.1", + "@babel/helper-replace-supers": "^7.10.1", + "@babel/helper-simple-access": "^7.10.1", + "@babel/helper-split-export-declaration": "^7.10.1", + "@babel/template": "^7.10.1", + "@babel/types": "^7.10.1", + "lodash": "^4.17.13" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.1.tgz", + "integrity": "sha512-a0DjNS1prnBsoKx83dP2falChcs7p3i8VMzdrSbfLhuQra/2ENC4sbri34dz/rWmDADsmF1q5GbfaXydh0Jbjg==", + "requires": { + "@babel/types": "^7.10.1" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.1.tgz", + "integrity": "sha512-fvoGeXt0bJc7VMWZGCAEBEMo/HAjW2mP8apF5eXK0wSqwLAVHAISCWRoLMBMUs2kqeaG77jltVqu4Hn8Egl3nA==" + }, + "@babel/helper-regex": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.10.1.tgz", + "integrity": "sha512-7isHr19RsIJWWLLFn21ubFt223PjQyg1HY7CZEMRr820HttHPpVvrsIN3bUOo44DEfFV4kBXO7Abbn9KTUZV7g==", + "requires": { + "lodash": "^4.17.13" + } + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.10.1.tgz", + "integrity": "sha512-RfX1P8HqsfgmJ6CwaXGKMAqbYdlleqglvVtht0HGPMSsy2V6MqLlOJVF/0Qyb/m2ZCi2z3q3+s6Pv7R/dQuZ6A==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.1", + "@babel/helper-wrap-function": "^7.10.1", + "@babel/template": "^7.10.1", + "@babel/traverse": "^7.10.1", + "@babel/types": "^7.10.1" + } + }, + "@babel/helper-replace-supers": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.10.1.tgz", + "integrity": "sha512-SOwJzEfpuQwInzzQJGjGaiG578UYmyi2Xw668klPWV5n07B73S0a9btjLk/52Mlcxa+5AdIYqws1KyXRfMoB7A==", + "requires": { + "@babel/helper-member-expression-to-functions": "^7.10.1", + "@babel/helper-optimise-call-expression": "^7.10.1", + "@babel/traverse": "^7.10.1", + "@babel/types": "^7.10.1" + } + }, + "@babel/helper-simple-access": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.10.1.tgz", + "integrity": "sha512-VSWpWzRzn9VtgMJBIWTZ+GP107kZdQ4YplJlCmIrjoLVSi/0upixezHCDG8kpPVTBJpKfxTH01wDhh+jS2zKbw==", + "requires": { + "@babel/template": "^7.10.1", + "@babel/types": "^7.10.1" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.1.tgz", + "integrity": "sha512-UQ1LVBPrYdbchNhLwj6fetj46BcFwfS4NllJo/1aJsT+1dLTEnXJL0qHqtY7gPzF8S2fXBJamf1biAXV3X077g==", + "requires": { + "@babel/types": "^7.10.1" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.1.tgz", + "integrity": "sha512-5vW/JXLALhczRCWP0PnFDMCJAchlBvM7f4uk/jXritBnIa6E1KmqmtrS3yn1LAnxFBypQ3eneLuXjsnfQsgILw==" + }, + "@babel/helper-wrap-function": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.10.1.tgz", + "integrity": "sha512-C0MzRGteVDn+H32/ZgbAv5r56f2o1fZSA/rj/TYo8JEJNHg+9BdSmKBUND0shxWRztWhjlT2cvHYuynpPsVJwQ==", + "requires": { + "@babel/helper-function-name": "^7.10.1", + "@babel/template": "^7.10.1", + "@babel/traverse": "^7.10.1", + "@babel/types": "^7.10.1" + } + }, + "@babel/helpers": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.10.1.tgz", + "integrity": "sha512-muQNHF+IdU6wGgkaJyhhEmI54MOZBKsFfsXFhboz1ybwJ1Kl7IHlbm2a++4jwrmY5UYsgitt5lfqo1wMFcHmyw==", + "requires": { + "@babel/template": "^7.10.1", + "@babel/traverse": "^7.10.1", + "@babel/types": "^7.10.1" + } + }, + "@babel/highlight": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.1.tgz", + "integrity": "sha512-8rMof+gVP8mxYZApLF/JgNDAkdKa+aJt3ZYxF8z6+j/hpeXL7iMsKCPHa2jNMHu/qqBwzQF4OHNoYi8dMA/rYg==", + "requires": { + "@babel/helper-validator-identifier": "^7.10.1", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.10.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.10.2.tgz", + "integrity": "sha512-PApSXlNMJyB4JiGVhCOlzKIif+TKFTvu0aQAhnTvfP/z3vVSN6ZypH5bfUNwFXXjRQtUEBNFd2PtmCmG2Py3qQ==" + }, + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.1.tgz", + "integrity": "sha512-vzZE12ZTdB336POZjmpblWfNNRpMSua45EYnRigE2XsZxcXcIyly2ixnTJasJE4Zq3U7t2d8rRF7XRUuzHxbOw==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.1", + "@babel/helper-remap-async-to-generator": "^7.10.1", + "@babel/plugin-syntax-async-generators": "^7.8.0" + } + }, + "@babel/plugin-proposal-class-properties": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.10.1.tgz", + "integrity": "sha512-sqdGWgoXlnOdgMXU+9MbhzwFRgxVLeiGBqTrnuS7LC2IBU31wSsESbTUreT2O418obpfPdGUR2GbEufZF1bpqw==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1" + } + }, + "@babel/plugin-proposal-dynamic-import": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.1.tgz", + "integrity": "sha512-Cpc2yUVHTEGPlmiQzXj026kqwjEQAD9I4ZC16uzdbgWgitg/UHKHLffKNCQZ5+y8jpIZPJcKcwsr2HwPh+w3XA==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.1", + "@babel/plugin-syntax-dynamic-import": "^7.8.0" + } + }, + "@babel/plugin-proposal-json-strings": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.1.tgz", + "integrity": "sha512-m8r5BmV+ZLpWPtMY2mOKN7wre6HIO4gfIiV+eOmsnZABNenrt/kzYBwrh+KOfgumSWpnlGs5F70J8afYMSJMBg==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.1", + "@babel/plugin-syntax-json-strings": "^7.8.0" + } + }, + "@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.10.1.tgz", + "integrity": "sha512-56cI/uHYgL2C8HVuHOuvVowihhX0sxb3nnfVRzUeVHTWmRHTZrKuAh/OBIMggGU/S1g/1D2CRCXqP+3u7vX7iA==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.1", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0" + } + }, + "@babel/plugin-proposal-numeric-separator": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.10.1.tgz", + "integrity": "sha512-jjfym4N9HtCiNfyyLAVD8WqPYeHUrw4ihxuAynWj6zzp2gf9Ey2f7ImhFm6ikB3CLf5Z/zmcJDri6B4+9j9RsA==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.1", + "@babel/plugin-syntax-numeric-separator": "^7.10.1" + } + }, + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.10.1.tgz", + "integrity": "sha512-Z+Qri55KiQkHh7Fc4BW6o+QBuTagbOp9txE+4U1i79u9oWlf2npkiDx+Rf3iK3lbcHBuNy9UOkwuR5wOMH3LIQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.1", + "@babel/plugin-syntax-object-rest-spread": "^7.8.0", + "@babel/plugin-transform-parameters": "^7.10.1" + } + }, + "@babel/plugin-proposal-optional-catch-binding": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.10.1.tgz", + "integrity": "sha512-VqExgeE62YBqI3ogkGoOJp1R6u12DFZjqwJhqtKc2o5m1YTUuUWnos7bZQFBhwkxIFpWYJ7uB75U7VAPPiKETA==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.1", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" + } + }, + "@babel/plugin-proposal-optional-chaining": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.10.1.tgz", + "integrity": "sha512-dqQj475q8+/avvok72CF3AOSV/SGEcH29zT5hhohqqvvZ2+boQoOr7iGldBG5YXTO2qgCgc2B3WvVLUdbeMlGA==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.1", + "@babel/plugin-syntax-optional-chaining": "^7.8.0" + } + }, + "@babel/plugin-proposal-private-methods": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.10.1.tgz", + "integrity": "sha512-RZecFFJjDiQ2z6maFprLgrdnm0OzoC23Mx89xf1CcEsxmHuzuXOdniEuI+S3v7vjQG4F5sa6YtUp+19sZuSxHg==", + "requires": { + "@babel/helper-create-class-features-plugin": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1" + } + }, + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.1.tgz", + "integrity": "sha512-JjfngYRvwmPwmnbRZyNiPFI8zxCZb8euzbCG/LxyKdeTb59tVciKo9GK9bi6JYKInk1H11Dq9j/zRqIH4KigfQ==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1" + } + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.1.tgz", + "integrity": "sha512-Gf2Yx/iRs1JREDtVZ56OrjjgFHCaldpTnuy9BHla10qyVT3YkIIGEtoDWhyop0ksu1GvNjHIoYRBqm3zoR1jyQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.1" + } + }, + "@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-jsx": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.10.1.tgz", + "integrity": "sha512-+OxyOArpVFXQeXKLO9o+r2I4dIoVoy6+Uu0vKELrlweDM3QJADZj+Z+5ERansZqIZBcLj42vHnDI8Rz9BnRIuQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.1" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.1.tgz", + "integrity": "sha512-uTd0OsHrpe3tH5gRPTxG8Voh99/WCU78vIm5NMRYPAqC8lR4vajt6KkCAknCHrx24vkPdd/05yfdGSB4EIY2mg==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.1" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.10.1.tgz", + "integrity": "sha512-hgA5RYkmZm8FTFT3yu2N9Bx7yVVOKYT6yEdXXo6j2JTm0wNxgqaGeQVaSHRjhfnQbX91DtjFB6McRFSlcJH3xQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.1" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.1.tgz", + "integrity": "sha512-6AZHgFJKP3DJX0eCNJj01RpytUa3SOGawIxweHkNX2L6PYikOZmoh5B0d7hIHaIgveMjX990IAa/xK7jRTN8OA==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.1" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.1.tgz", + "integrity": "sha512-XCgYjJ8TY2slj6SReBUyamJn3k2JLUIiiR5b6t1mNCMSvv7yx+jJpaewakikp0uWFQSF7ChPPoe3dHmXLpISkg==", + "requires": { + "@babel/helper-module-imports": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1", + "@babel/helper-remap-async-to-generator": "^7.10.1" + } + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.10.1.tgz", + "integrity": "sha512-B7K15Xp8lv0sOJrdVAoukKlxP9N59HS48V1J3U/JGj+Ad+MHq+am6xJVs85AgXrQn4LV8vaYFOB+pr/yIuzW8Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.1" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.10.1.tgz", + "integrity": "sha512-8bpWG6TtF5akdhIm/uWTyjHqENpy13Fx8chg7pFH875aNLwX8JxIxqm08gmAT+Whe6AOmaTeLPe7dpLbXt+xUw==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.1", + "lodash": "^4.17.13" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.1.tgz", + "integrity": "sha512-P9V0YIh+ln/B3RStPoXpEQ/CoAxQIhRSUn7aXqQ+FZJ2u8+oCtjIXR3+X0vsSD8zv+mb56K7wZW1XiDTDGiDRQ==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.1", + "@babel/helper-define-map": "^7.10.1", + "@babel/helper-function-name": "^7.10.1", + "@babel/helper-optimise-call-expression": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1", + "@babel/helper-replace-supers": "^7.10.1", + "@babel/helper-split-export-declaration": "^7.10.1", + "globals": "^11.1.0" + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.10.1.tgz", + "integrity": "sha512-mqSrGjp3IefMsXIenBfGcPXxJxweQe2hEIwMQvjtiDQ9b1IBvDUjkAtV/HMXX47/vXf14qDNedXsIiNd1FmkaQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.1" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.1.tgz", + "integrity": "sha512-V/nUc4yGWG71OhaTH705pU8ZSdM6c1KmmLP8ys59oOYbT7RpMYAR3MsVOt6OHL0WzG7BlTU076va9fjJyYzJMA==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.1" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.1.tgz", + "integrity": "sha512-19VIMsD1dp02RvduFUmfzj8uknaO3uiHHF0s3E1OHnVsNj8oge8EQ5RzHRbJjGSetRnkEuBYO7TG1M5kKjGLOA==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1" + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.1.tgz", + "integrity": "sha512-wIEpkX4QvX8Mo9W6XF3EdGttrIPZWozHfEaDTU0WJD/TDnXMvdDh30mzUl/9qWhnf7naicYartcEfUghTCSNpA==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.1" + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.1.tgz", + "integrity": "sha512-lr/przdAbpEA2BUzRvjXdEDLrArGRRPwbaF9rvayuHRvdQ7lUTTkZnhZrJ4LE2jvgMRFF4f0YuPQ20vhiPYxtA==", + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1" + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.1.tgz", + "integrity": "sha512-US8KCuxfQcn0LwSCMWMma8M2R5mAjJGsmoCBVwlMygvmDUMkTCykc84IqN1M7t+agSfOmLYTInLCHJM+RUoz+w==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.1" + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.10.1.tgz", + "integrity": "sha512-//bsKsKFBJfGd65qSNNh1exBy5Y9gD9ZN+DvrJ8f7HXr4avE5POW6zB7Rj6VnqHV33+0vXWUwJT0wSHubiAQkw==", + "requires": { + "@babel/helper-function-name": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1" + } + }, + "@babel/plugin-transform-literals": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.10.1.tgz", + "integrity": "sha512-qi0+5qgevz1NHLZroObRm5A+8JJtibb7vdcPQF1KQE12+Y/xxl8coJ+TpPW9iRq+Mhw/NKLjm+5SHtAHCC7lAw==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.1" + } + }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.10.1.tgz", + "integrity": "sha512-UmaWhDokOFT2GcgU6MkHC11i0NQcL63iqeufXWfRy6pUOGYeCGEKhvfFO6Vz70UfYJYHwveg62GS83Rvpxn+NA==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.1" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.1.tgz", + "integrity": "sha512-31+hnWSFRI4/ACFr1qkboBbrTxoBIzj7qA69qlq8HY8p7+YCzkCT6/TvQ1a4B0z27VeWtAeJd6pr5G04dc1iHw==", + "requires": { + "@babel/helper-module-transforms": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.1.tgz", + "integrity": "sha512-AQG4fc3KOah0vdITwt7Gi6hD9BtQP/8bhem7OjbaMoRNCH5Djx42O2vYMfau7QnAzQCa+RJnhJBmFFMGpQEzrg==", + "requires": { + "@babel/helper-module-transforms": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1", + "@babel/helper-simple-access": "^7.10.1", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.1.tgz", + "integrity": "sha512-ewNKcj1TQZDL3YnO85qh9zo1YF1CHgmSTlRQgHqe63oTrMI85cthKtZjAiZSsSNjPQ5NCaYo5QkbYqEw1ZBgZA==", + "requires": { + "@babel/helper-hoist-variables": "^7.10.1", + "@babel/helper-module-transforms": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.1.tgz", + "integrity": "sha512-EIuiRNMd6GB6ulcYlETnYYfgv4AxqrswghmBRQbWLHZxN4s7mupxzglnHqk9ZiUpDI4eRWewedJJNj67PWOXKA==", + "requires": { + "@babel/helper-module-transforms": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1" + } + }, + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.8.3.tgz", + "integrity": "sha512-f+tF/8UVPU86TrCb06JoPWIdDpTNSGGcAtaD9mLP0aYGA0OS0j7j7DHJR0GTFrUZPUU6loZhbsVZgTh0N+Qdnw==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.8.3" + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.1.tgz", + "integrity": "sha512-MBlzPc1nJvbmO9rPr1fQwXOM2iGut+JC92ku6PbiJMMK7SnQc1rytgpopveE3Evn47gzvGYeCdgfCDbZo0ecUw==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.1" + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.1.tgz", + "integrity": "sha512-WnnStUDN5GL+wGQrJylrnnVlFhFmeArINIR9gjhSeYyvroGhBrSAXYg/RHsnfzmsa+onJrTJrEClPzgNmmQ4Gw==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.1", + "@babel/helper-replace-supers": "^7.10.1" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.1.tgz", + "integrity": "sha512-tJ1T0n6g4dXMsL45YsSzzSDZCxiHXAQp/qHrucOq5gEHncTA3xDxnd5+sZcoQp+N1ZbieAaB8r/VUCG0gqseOg==", + "requires": { + "@babel/helper-get-function-arity": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1" + } + }, + "@babel/plugin-transform-property-literals": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.10.1.tgz", + "integrity": "sha512-Kr6+mgag8auNrgEpbfIWzdXYOvqDHZOF0+Bx2xh4H2EDNwcbRb9lY6nkZg8oSjsX+DH9Ebxm9hOqtKW+gRDeNA==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.1" + } + }, + "@babel/plugin-transform-react-display-name": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.10.1.tgz", + "integrity": "sha512-rBjKcVwjk26H3VX8pavMxGf33LNlbocMHdSeldIEswtQ/hrjyTG8fKKILW1cSkODyRovckN/uZlGb2+sAV9JUQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.1" + } + }, + "@babel/plugin-transform-react-jsx": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.10.1.tgz", + "integrity": "sha512-MBVworWiSRBap3Vs39eHt+6pJuLUAaK4oxGc8g+wY+vuSJvLiEQjW1LSTqKb8OUPtDvHCkdPhk7d6sjC19xyFw==", + "requires": { + "@babel/helper-builder-react-jsx": "^7.10.1", + "@babel/helper-builder-react-jsx-experimental": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1", + "@babel/plugin-syntax-jsx": "^7.10.1" + } + }, + "@babel/plugin-transform-react-jsx-development": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.10.1.tgz", + "integrity": "sha512-XwDy/FFoCfw9wGFtdn5Z+dHh6HXKHkC6DwKNWpN74VWinUagZfDcEJc3Y8Dn5B3WMVnAllX8Kviaw7MtC5Epwg==", + "requires": { + "@babel/helper-builder-react-jsx-experimental": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1", + "@babel/plugin-syntax-jsx": "^7.10.1" + } + }, + "@babel/plugin-transform-react-jsx-self": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.10.1.tgz", + "integrity": "sha512-4p+RBw9d1qV4S749J42ZooeQaBomFPrSxa9JONLHJ1TxCBo3TzJ79vtmG2S2erUT8PDDrPdw4ZbXGr2/1+dILA==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.1", + "@babel/plugin-syntax-jsx": "^7.10.1" + } + }, + "@babel/plugin-transform-react-jsx-source": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.10.1.tgz", + "integrity": "sha512-neAbaKkoiL+LXYbGDvh6PjPG+YeA67OsZlE78u50xbWh2L1/C81uHiNP5d1fw+uqUIoiNdCC8ZB+G4Zh3hShJA==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.1", + "@babel/plugin-syntax-jsx": "^7.10.1" + } + }, + "@babel/plugin-transform-react-pure-annotations": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.10.1.tgz", + "integrity": "sha512-mfhoiai083AkeewsBHUpaS/FM1dmUENHBMpS/tugSJ7VXqXO5dCN1Gkint2YvM1Cdv1uhmAKt1ZOuAjceKmlLA==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.1.tgz", + "integrity": "sha512-B3+Y2prScgJ2Bh/2l9LJxKbb8C8kRfsG4AdPT+n7ixBHIxJaIG8bi8tgjxUMege1+WqSJ+7gu1YeoMVO3gPWzw==", + "requires": { + "regenerator-transform": "^0.14.2" + } + }, + "@babel/plugin-transform-reserved-words": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.1.tgz", + "integrity": "sha512-qN1OMoE2nuqSPmpTqEM7OvJ1FkMEV+BjVeZZm9V9mq/x1JLKQ4pcv8riZJMNN3u2AUGl0ouOMjRr2siecvHqUQ==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.1" + } + }, + "@babel/plugin-transform-runtime": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.10.1.tgz", + "integrity": "sha512-4w2tcglDVEwXJ5qxsY++DgWQdNJcCCsPxfT34wCUwIf2E7dI7pMpH8JczkMBbgBTNzBX62SZlNJ9H+De6Zebaw==", + "requires": { + "@babel/helper-module-imports": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1", + "resolve": "^1.8.1", + "semver": "^5.5.1" + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.1.tgz", + "integrity": "sha512-AR0E/lZMfLstScFwztApGeyTHJ5u3JUKMjneqRItWeEqDdHWZwAOKycvQNCasCK/3r5YXsuNG25funcJDu7Y2g==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.1" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.10.1.tgz", + "integrity": "sha512-8wTPym6edIrClW8FI2IoaePB91ETOtg36dOkj3bYcNe7aDMN2FXEoUa+WrmPc4xa1u2PQK46fUX2aCb+zo9rfw==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.1" + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.1.tgz", + "integrity": "sha512-j17ojftKjrL7ufX8ajKvwRilwqTok4q+BjkknmQw9VNHnItTyMP5anPFzxFJdCQs7clLcWpCV3ma+6qZWLnGMA==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.1", + "@babel/helper-regex": "^7.10.1" + } + }, + "@babel/plugin-transform-strict-mode": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-strict-mode/-/plugin-transform-strict-mode-7.10.1.tgz", + "integrity": "sha512-Q8GDTT75F35KgGGW01KcCn3OhEAIJIcMSxMZGcmxjeY8Asj0reAGodt6wN5wfrvSSuPLhmtU1zOaOy3vR8uCRg==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.1" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.1.tgz", + "integrity": "sha512-t7B/3MQf5M1T9hPCRG28DNGZUuxAuDqLYS03rJrIk2prj/UV7Z6FOneijhQhnv/Xa039vidXeVbvjK2SK5f7Gg==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1" + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.1.tgz", + "integrity": "sha512-qX8KZcmbvA23zDi+lk9s6hC1FM7jgLHYIjuLgULgc8QtYnmB3tAVIYkNoKRQ75qWBeyzcoMoK8ZQmogGtC/w0g==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.1" + } + }, + "@babel/plugin-transform-unicode-escapes": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.10.1.tgz", + "integrity": "sha512-zZ0Poh/yy1d4jeDWpx/mNwbKJVwUYJX73q+gyh4bwtG0/iUlzdEu0sLMda8yuDFS6LBQlT/ST1SJAR6zYwXWgw==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.1" + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.1.tgz", + "integrity": "sha512-Y/2a2W299k0VIUdbqYm9X2qS6fE0CUBhhiPpimK6byy7OJ/kORLlIX+J6UrjgNu5awvs62k+6RSslxhcvVw2Tw==", + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1" + } + }, + "@babel/preset-env": { + "version": "7.10.2", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.10.2.tgz", + "integrity": "sha512-MjqhX0RZaEgK/KueRzh+3yPSk30oqDKJ5HP5tqTSB1e2gzGS3PLy7K0BIpnp78+0anFuSwOeuCf1zZO7RzRvEA==", + "requires": { + "@babel/compat-data": "^7.10.1", + "@babel/helper-compilation-targets": "^7.10.2", + "@babel/helper-module-imports": "^7.10.1", + "@babel/helper-plugin-utils": "^7.10.1", + "@babel/plugin-proposal-async-generator-functions": "^7.10.1", + "@babel/plugin-proposal-class-properties": "^7.10.1", + "@babel/plugin-proposal-dynamic-import": "^7.10.1", + "@babel/plugin-proposal-json-strings": "^7.10.1", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.10.1", + "@babel/plugin-proposal-numeric-separator": "^7.10.1", + "@babel/plugin-proposal-object-rest-spread": "^7.10.1", + "@babel/plugin-proposal-optional-catch-binding": "^7.10.1", + "@babel/plugin-proposal-optional-chaining": "^7.10.1", + "@babel/plugin-proposal-private-methods": "^7.10.1", + "@babel/plugin-proposal-unicode-property-regex": "^7.10.1", + "@babel/plugin-syntax-async-generators": "^7.8.0", + "@babel/plugin-syntax-class-properties": "^7.10.1", + "@babel/plugin-syntax-dynamic-import": "^7.8.0", + "@babel/plugin-syntax-json-strings": "^7.8.0", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0", + "@babel/plugin-syntax-numeric-separator": "^7.10.1", + "@babel/plugin-syntax-object-rest-spread": "^7.8.0", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.0", + "@babel/plugin-syntax-top-level-await": "^7.10.1", + "@babel/plugin-transform-arrow-functions": "^7.10.1", + "@babel/plugin-transform-async-to-generator": "^7.10.1", + "@babel/plugin-transform-block-scoped-functions": "^7.10.1", + "@babel/plugin-transform-block-scoping": "^7.10.1", + "@babel/plugin-transform-classes": "^7.10.1", + "@babel/plugin-transform-computed-properties": "^7.10.1", + "@babel/plugin-transform-destructuring": "^7.10.1", + "@babel/plugin-transform-dotall-regex": "^7.10.1", + "@babel/plugin-transform-duplicate-keys": "^7.10.1", + "@babel/plugin-transform-exponentiation-operator": "^7.10.1", + "@babel/plugin-transform-for-of": "^7.10.1", + "@babel/plugin-transform-function-name": "^7.10.1", + "@babel/plugin-transform-literals": "^7.10.1", + "@babel/plugin-transform-member-expression-literals": "^7.10.1", + "@babel/plugin-transform-modules-amd": "^7.10.1", + "@babel/plugin-transform-modules-commonjs": "^7.10.1", + "@babel/plugin-transform-modules-systemjs": "^7.10.1", + "@babel/plugin-transform-modules-umd": "^7.10.1", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.8.3", + "@babel/plugin-transform-new-target": "^7.10.1", + "@babel/plugin-transform-object-super": "^7.10.1", + "@babel/plugin-transform-parameters": "^7.10.1", + "@babel/plugin-transform-property-literals": "^7.10.1", + "@babel/plugin-transform-regenerator": "^7.10.1", + "@babel/plugin-transform-reserved-words": "^7.10.1", + "@babel/plugin-transform-shorthand-properties": "^7.10.1", + "@babel/plugin-transform-spread": "^7.10.1", + "@babel/plugin-transform-sticky-regex": "^7.10.1", + "@babel/plugin-transform-template-literals": "^7.10.1", + "@babel/plugin-transform-typeof-symbol": "^7.10.1", + "@babel/plugin-transform-unicode-escapes": "^7.10.1", + "@babel/plugin-transform-unicode-regex": "^7.10.1", + "@babel/preset-modules": "^0.1.3", + "@babel/types": "^7.10.2", + "browserslist": "^4.12.0", + "core-js-compat": "^3.6.2", + "invariant": "^2.2.2", + "levenary": "^1.1.1", + "semver": "^5.5.0" + } + }, + "@babel/preset-modules": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.3.tgz", + "integrity": "sha512-Ra3JXOHBq2xd56xSF7lMKXdjBn3T772Y1Wet3yWnkDly9zHvJki029tAFzvAAK5cf4YV3yoxuP61crYRol6SVg==", + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + } + }, + "@babel/preset-react": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.10.1.tgz", + "integrity": "sha512-Rw0SxQ7VKhObmFjD/cUcKhPTtzpeviEFX1E6PgP+cYOhQ98icNqtINNFANlsdbQHrmeWnqdxA4Tmnl1jy5tp3Q==", + "requires": { + "@babel/helper-plugin-utils": "^7.10.1", + "@babel/plugin-transform-react-display-name": "^7.10.1", + "@babel/plugin-transform-react-jsx": "^7.10.1", + "@babel/plugin-transform-react-jsx-development": "^7.10.1", + "@babel/plugin-transform-react-jsx-self": "^7.10.1", + "@babel/plugin-transform-react-jsx-source": "^7.10.1", + "@babel/plugin-transform-react-pure-annotations": "^7.10.1" + } + }, + "@babel/runtime": { + "version": "7.10.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.10.2.tgz", + "integrity": "sha512-6sF3uQw2ivImfVIl62RZ7MXhO2tap69WeWK57vAaimT6AZbE4FbqjdEJIN1UqoD6wI6B+1n9UiagafH1sxjOtg==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/template": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.1.tgz", + "integrity": "sha512-OQDg6SqvFSsc9A0ej6SKINWrpJiNonRIniYondK2ViKhB06i3c0s+76XUft71iqBEe9S1OKsHwPAjfHnuvnCig==", + "requires": { + "@babel/code-frame": "^7.10.1", + "@babel/parser": "^7.10.1", + "@babel/types": "^7.10.1" + } + }, + "@babel/traverse": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.10.1.tgz", + "integrity": "sha512-C/cTuXeKt85K+p08jN6vMDz8vSV0vZcI0wmQ36o6mjbuo++kPMdpOYw23W2XH04dbRt9/nMEfA4W3eR21CD+TQ==", + "requires": { + "@babel/code-frame": "^7.10.1", + "@babel/generator": "^7.10.1", + "@babel/helper-function-name": "^7.10.1", + "@babel/helper-split-export-declaration": "^7.10.1", + "@babel/parser": "^7.10.1", + "@babel/types": "^7.10.1", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" + } + }, + "@babel/types": { + "version": "7.10.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.2.tgz", + "integrity": "sha512-AD3AwWBSz0AWF0AkCN9VPiWrvldXq+/e3cHa4J89vo4ymjz1XwrBFFVZmkJTsQIPNk+ZVomPSXUJqq8yyjZsng==", + "requires": { + "@babel/helper-validator-identifier": "^7.10.1", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "@csstools/convert-colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@csstools/convert-colors/-/convert-colors-1.4.0.tgz", + "integrity": "sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw==" + }, + "@hypnosphi/react-virtualized": { + "version": "9.21.2", + "resolved": "https://registry.npmjs.org/@hypnosphi/react-virtualized/-/react-virtualized-9.21.2.tgz", + "integrity": "sha512-6pd6R1Iz0sD/IwlMQdja6FHvm9WqqOUrnDP42Z/179abCb5m3isv9kNiWTF6wAJYprHkEDG+SCkChzJ7uUIUoA==", + "requires": { + "@babel/runtime": "^7.7.2", + "clsx": "^1.0.4", + "dom-helpers": "^5.1.3", + "loose-envify": "^1.4.0", + "prop-types": "^15.7.2", + "react-lifecycles-compat": "^3.0.4" + } + }, + "@jetbrains/angular-elastic": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@jetbrains/angular-elastic/-/angular-elastic-2.5.1.tgz", + "integrity": "sha512-/XU38+J5c3vKKoiwGmqze0UaKt7mnrR0mQJg1WxuZFBSTf6e1co8rN8bgxik0jAX5s8yXUMWhPhmrIYKaR140Q==", + "requires": { + "angular": ">=1.0.6" + } + }, + "@jetbrains/babel-preset-jetbrains": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@jetbrains/babel-preset-jetbrains/-/babel-preset-jetbrains-2.1.4.tgz", + "integrity": "sha512-0W3p5gczcrcUxymF6p5Y9gq3duBqJvkANpHLfHYz5CrP8Y0PmerJiYFvS200+xMeal1wpcuu90jD+AWwgf0o6w==", + "requires": { + "@babel/plugin-proposal-class-properties": "^7.4.0", + "@babel/plugin-proposal-object-rest-spread": "^7.4.3", + "@babel/plugin-proposal-optional-chaining": "^7.2.0", + "@babel/plugin-transform-runtime": "^7.4.3", + "@babel/plugin-transform-strict-mode": "^7.2.0", + "@babel/preset-env": "^7.4.3", + "@babel/preset-react": "^7.0.0", + "@babel/runtime": "^7.4.3", + "babel-plugin-angularjs-annotate": "^0.9.0" + } + }, + "@jetbrains/icons": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/@jetbrains/icons/-/icons-3.7.0.tgz", + "integrity": "sha512-i7+U7iceYiewMEXhz227I0P461iN9v7008RllzXcxh7ZybMduKRqpvzpbIpx2oJXYTlVOTsuFinaBT+yNl3WDQ==" + }, + "@jetbrains/logos": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@jetbrains/logos/-/logos-1.1.5.tgz", + "integrity": "sha512-Hm0aRFrH1+z0P+OSKCp65B1LOsQJ2VdEiaE/ZV2JDt1MA8ooutkXy3tjTmYx69yYul58RbHhWusiVF4hBaM5sg==" + }, + "@jetbrains/postcss-require-hover": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@jetbrains/postcss-require-hover/-/postcss-require-hover-0.1.2.tgz", + "integrity": "sha512-U094mXSp0KOfqZLTlkPLz4vHdIZYm1gJFRFJP7nMrGA1OI4Nigwl0TUwFt/7YDNAff57Eo9Zttu9Ln5QoXguAw==", + "requires": { + "postcss": "^6.0.1" + }, + "dependencies": { + "postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "@jetbrains/ring-ui": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@jetbrains/ring-ui/-/ring-ui-2.1.16.tgz", + "integrity": "sha512-tx7fDglrKoo61iAIJf+XcWNn5e+MdnTLqvkUHfB/SFYrLZMX8fZK4DG9S/HoVNSSWKQ3cFbrsszH8RaTz5zgXg==", + "requires": { + "@babel/core": "^7.7.7", + "@hypnosphi/react-virtualized": "^9.21.2", + "@jetbrains/angular-elastic": "^2.5.1", + "@jetbrains/babel-preset-jetbrains": "^2.1.4", + "@jetbrains/icons": "^3.6.0", + "@jetbrains/logos": "^1.1.5", + "@jetbrains/postcss-require-hover": "^0.1.2", + "angular-sanitize": "^1.7.9", + "babel-loader": "^8.0.6", + "babel-plugin-transform-define": "^2.0.0", + "browserslist": "^4.7.0", + "change-case": "^3.1.0", + "classnames": "^2.2.6", + "combokeys": "^3.0.1", + "compile-code-loader": "^0.2.0", + "conic-gradient": "^1.0.0", + "css-loader": "^3.4.0", + "deep-equal": "^2.0.1", + "dom4": "^1.8.5", + "element-resize-detector": "^1.2.0", + "es6-error": "^4.1.1", + "extricate-loader": "^3.0.0", + "file-loader": "^5.0.2", + "focus-trap": "^5.1.0", + "focus-visible": "^5.0.2", + "highlight.js": "^9.17.1", + "html-loader": "^0.5.5", + "imports-loader": "^0.8.0", + "interpolate-loader": "^2.0.1", + "just-debounce-it": "^1.1.0", + "memoize-one": "^5.1.1", + "moment": "^2.24.0", + "postcss": "^7.0.25", + "postcss-calc": "^7.0.1", + "postcss-flexbugs-fixes": "^4.1.0", + "postcss-font-family-system-ui": "^4.2.0", + "postcss-loader": "^3.0.0", + "postcss-modules-values-replace": "^3.0.1", + "postcss-preset-env": "^6.7.0", + "prop-types": "^15.7.2", + "react-markdown": "^4.2.2", + "react-measure": "^2.3.0", + "react-sortable-hoc": "^1.10.1", + "react-virtualized": "^9.21.1", + "react-waypoint": "^9.0.2", + "recompose": "^0.30.0", + "remark-breaks": "^1.0.3", + "sass": "^1.24.0", + "sass-loader": "^8.0.0", + "scrollbar-width": "^3.1.1", + "simply-uuid": "^1.0.1", + "sniffr": "^1.2.0", + "style-loader": "~1.0.2", + "svg-inline-loader": "^0.8.0", + "url-loader": "^3.0.0", + "url-search-params": "^1.1.0", + "util-deprecate": "^1.0.2", + "whatwg-fetch": "^3.0.0" + } + }, + "@jetbrains/stylelint-config": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@jetbrains/stylelint-config/-/stylelint-config-2.0.1.tgz", + "integrity": "sha512-cIdiPQQMXn2bYvoQ8631KS3By2v65wMg1L/S4MGPRPdj7MGR/1pGOWkQT34wiWu+YxojxIERnP/bcFeimuvhCg==", + "dev": true, + "requires": { + "stylelint-config-css-modules": "^2.1.0", + "stylelint-config-standard": "^19.0.0", + "stylelint-order": "^3.1.1" + } + }, + "@mrmlnc/readdir-enhanced": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", + "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", + "dev": true, + "requires": { + "call-me-maybe": "^1.0.1", + "glob-to-regexp": "^0.3.0" + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", + "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.3", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz", + "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz", + "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.3", + "fastq": "^1.6.0" + } + }, + "@sindresorhus/is": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz", + "integrity": "sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==", + "dev": true + }, + "@stylelint/postcss-css-in-js": { + "version": "0.37.1", + "resolved": "https://registry.npmjs.org/@stylelint/postcss-css-in-js/-/postcss-css-in-js-0.37.1.tgz", + "integrity": "sha512-UMf2Rni3JGKi3ZwYRGMYJ5ipOA5ENJSKMtYA/pE1ZLURwdh7B5+z2r73RmWvub+N0UuH1Lo+TGfCgYwPvqpXNw==", + "dev": true, + "requires": { + "@babel/core": ">=7.9.0" + } + }, + "@stylelint/postcss-markdown": { + "version": "0.36.1", + "resolved": "https://registry.npmjs.org/@stylelint/postcss-markdown/-/postcss-markdown-0.36.1.tgz", + "integrity": "sha512-iDxMBWk9nB2BPi1VFQ+Dc5+XpvODBHw2n3tYpaBZuEAFQlbtF9If0Qh5LTTwSi/XwdbJ2jt+0dis3i8omyggpw==", + "dev": true, + "requires": { + "remark": "^12.0.0", + "unist-util-find-all-after": "^3.0.1" + } + }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, + "@types/glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-VgNIkxK+j7Nz5P7jvUZlRvhuPSmsEfS03b0alKcq5V/STUKAa3Plemsn5mrQUO7am6OErJ4rhGEGJbACclrtRA==", + "requires": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/json-schema": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.5.tgz", + "integrity": "sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ==" + }, + "@types/minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==" + }, + "@types/minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY=", + "dev": true + }, + "@types/node": { + "version": "12.12.47", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.47.tgz", + "integrity": "sha512-yzBInQFhdY8kaZmqoL2+3U5dSTMrKaYcb561VU+lDzAYvqt+2lojvBEy+hmpSNuXnPTx7m9+04CzWYOUqWME2A==" + }, + "@types/normalize-package-data": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", + "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", + "dev": true + }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "@types/prop-types": { + "version": "15.7.3", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", + "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==" + }, + "@types/react": { + "version": "16.9.36", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.36.tgz", + "integrity": "sha512-mGgUb/Rk/vGx4NCvquRuSH0GHBQKb1OqpGS9cT9lFxlTLHZgkksgI60TuIxubmn7JuCb+sENHhQciqa0npm0AQ==", + "requires": { + "@types/prop-types": "*", + "csstype": "^2.2.0" + } + }, + "@types/react-dom": { + "version": "16.9.8", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.8.tgz", + "integrity": "sha512-ykkPQ+5nFknnlU6lDd947WbQ6TE3NNzbQAkInC2EKY1qeYdTKp7onFusmYZb+ityzx2YviqT6BXSu+LyWWJwcA==", + "requires": { + "@types/react": "*" + } + }, + "@types/unist": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.3.tgz", + "integrity": "sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ==", + "dev": true + }, + "@webassemblyjs/ast": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", + "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==", + "requires": { + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz", + "integrity": "sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==" + }, + "@webassemblyjs/helper-api-error": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz", + "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==" + }, + "@webassemblyjs/helper-buffer": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz", + "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==" + }, + "@webassemblyjs/helper-code-frame": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz", + "integrity": "sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==", + "requires": { + "@webassemblyjs/wast-printer": "1.9.0" + } + }, + "@webassemblyjs/helper-fsm": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz", + "integrity": "sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==" + }, + "@webassemblyjs/helper-module-context": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz", + "integrity": "sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==", + "requires": { + "@webassemblyjs/ast": "1.9.0" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz", + "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==" + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz", + "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==", + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz", + "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==", + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz", + "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==", + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz", + "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==" + }, + "@webassemblyjs/wasm-edit": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz", + "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==", + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/helper-wasm-section": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-opt": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "@webassemblyjs/wast-printer": "1.9.0" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz", + "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==", + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz", + "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==", + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-buffer": "1.9.0", + "@webassemblyjs/wasm-gen": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz", + "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==", + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-wasm-bytecode": "1.9.0", + "@webassemblyjs/ieee754": "1.9.0", + "@webassemblyjs/leb128": "1.9.0", + "@webassemblyjs/utf8": "1.9.0" + } + }, + "@webassemblyjs/wast-parser": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz", + "integrity": "sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==", + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/floating-point-hex-parser": "1.9.0", + "@webassemblyjs/helper-api-error": "1.9.0", + "@webassemblyjs/helper-code-frame": "1.9.0", + "@webassemblyjs/helper-fsm": "1.9.0", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz", + "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==", + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/wast-parser": "1.9.0", + "@xtuc/long": "4.2.2" + } + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==" + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==" + }, + "JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "acorn": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz", + "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==" + }, + "acorn-jsx": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", + "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", + "dev": true + }, + "aggregate-error": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz", + "integrity": "sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "ajv": { + "version": "6.12.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.2.tgz", + "integrity": "sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ==", + "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" + } + }, + "ajv-errors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", + "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==" + }, + "ajv-keywords": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz", + "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==" + }, + "angular": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/angular/-/angular-1.8.0.tgz", + "integrity": "sha512-VdaMx+Qk0Skla7B5gw77a8hzlcOakwF8mjlW13DpIWIDlfqwAbSSLfd8N/qZnzEmQF4jC4iofInd3gE7vL8ZZg==" + }, + "angular-sanitize": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/angular-sanitize/-/angular-sanitize-1.8.0.tgz", + "integrity": "sha512-j5GiOPCvfcDWK5svEOVoPb11X3UDVy/mdHPRWuy14Iyw86xaq+Bb+x/em2sAOa5MQQeY5ciLXbF3RRp8iCKcNg==" + }, + "ansi": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/ansi/-/ansi-0.3.1.tgz", + "integrity": "sha1-DELU+xcWDVqa8eSEus4cZpIsGyE=", + "dev": true + }, + "ansi-align": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", + "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", + "dev": true, + "requires": { + "string-width": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "ansi-colors": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", + "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==" + }, + "ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "requires": { + "type-fest": "^0.11.0" + }, + "dependencies": { + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true + } + } + }, + "ansi-html": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", + "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=" + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "dev": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" + }, + "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==" + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" + }, + "array-differ": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-3.0.0.tgz", + "integrity": "sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==", + "dev": true + }, + "array-filter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz", + "integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=" + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true + }, + "array-flatten": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", + "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==" + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=" + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" + }, + "arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "dev": true + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + }, + "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" + } + }, + "asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" + } + } + }, + "assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", + "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", + "requires": { + "object-assign": "^4.1.1", + "util": "0.10.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", + "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" + }, + "util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", + "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", + "requires": { + "inherits": "2.0.1" + } + } + } + }, + "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=" + }, + "ast-types": { + "version": "0.9.6", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.9.6.tgz", + "integrity": "sha1-ECyenpAF0+fjgpvwxPok7oYu6bk=" + }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "requires": { + "lodash": "^4.17.14" + } + }, + "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==" + }, + "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==" + }, + "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==" + }, + "autoprefixer": { + "version": "9.8.0", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.0.tgz", + "integrity": "sha512-D96ZiIHXbDmU02dBaemyAg53ez+6F5yZmapmgKcjm35yEe1uVDYI8hGW3VYoGRaG290ZFf91YxHrR518vC0u/A==", + "requires": { + "browserslist": "^4.12.0", + "caniuse-lite": "^1.0.30001061", + "chalk": "^2.4.2", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss": "^7.0.30", + "postcss-value-parser": "^4.1.0" + } + }, + "available-typed-arrays": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz", + "integrity": "sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ==", + "requires": { + "array-filter": "^1.0.0" + } + }, + "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.10.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz", + "integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==", + "dev": true + }, + "axios": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.1.tgz", + "integrity": "sha512-0BfJq4NSfQXd+SkFdrvFbG7addhYSBA2mQwISr46pD6E5iqkWg02RAs8vyTT/j0RTnoYmeXauBuSv1qKwR179g==", + "dev": true, + "requires": { + "follow-redirects": "1.5.10", + "is-buffer": "^2.0.2" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "dev": true, + "requires": { + "debug": "=3.1.0" + } + }, + "is-buffer": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", + "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=" + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + } + } + }, + "babel-eslint": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", + "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.7.0", + "@babel/traverse": "^7.7.0", + "@babel/types": "^7.7.0", + "eslint-visitor-keys": "^1.0.0", + "resolve": "^1.12.0" + } + }, + "babel-loader": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.1.0.tgz", + "integrity": "sha512-7q7nC1tYOrqvUrN3LQK4GwSk/TQorZSOlO9C+RZDZpODgyN4ZlCqE5q9cDsyWOliN+aU9B4JX01xK9eJXowJLw==", + "requires": { + "find-cache-dir": "^2.1.0", + "loader-utils": "^1.4.0", + "mkdirp": "^0.5.3", + "pify": "^4.0.1", + "schema-utils": "^2.6.5" + } + }, + "babel-plugin-angularjs-annotate": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/babel-plugin-angularjs-annotate/-/babel-plugin-angularjs-annotate-0.9.0.tgz", + "integrity": "sha512-erYvZAJgnrgeyEZqIJOAiK6vUK44HsVb0+Tid4zTBcsvdQuas0Z5Teh0w/hcINKW3G0xweqA5LGfg2ZWlp3nMA==", + "requires": { + "babel-code-frame": "^6.26.0", + "babel-types": "^6.26.0", + "simple-is": "~0.2.0" + } + }, + "babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "requires": { + "object.assign": "^4.1.0" + } + }, + "babel-plugin-transform-define": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-define/-/babel-plugin-transform-define-2.0.0.tgz", + "integrity": "sha512-0dv5RNRUlUKxGYIIErl01lpvi8b7W2R04Qcl1mCj70ahwZcgiklfXnFlh4FGnRh6aayCfSZKdhiMryVzcq5Dmg==", + "requires": { + "lodash": "^4.17.11", + "traverse": "0.6.6" + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + } + } + }, + "babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "requires": { + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" + }, + "dependencies": { + "to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=" + } + } + }, + "bail": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz", + "integrity": "sha512-xFbRxM1tahm08yHBP16MMjVUAvDaBMD38zsM9EMAUN61omwLmKlOpB/Zku5QkjZ8TZ4vn53pj+t518cH0S03RQ==" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "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=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "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==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "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==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=" + }, + "batch-processor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/batch-processor/-/batch-processor-1.0.0.tgz", + "integrity": "sha1-dclcMrdI4IUNEMKxaPa9vpiRrOg=" + }, + "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" + } + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" + }, + "bin-version": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/bin-version/-/bin-version-2.0.0.tgz", + "integrity": "sha1-LMldg7Uive8umZeOdq61SRyBFP8=", + "dev": true, + "requires": { + "execa": "^0.1.1", + "find-versions": "^2.0.0" + }, + "dependencies": { + "execa": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.1.1.tgz", + "integrity": "sha1-sJwqkwm8DvBQFHlHLbMYD41MPt0=", + "dev": true, + "requires": { + "cross-spawn-async": "^2.1.1", + "object-assign": "^4.0.1", + "strip-eof": "^1.0.0" + } + } + } + }, + "bin-version-check": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bin-version-check/-/bin-version-check-3.0.0.tgz", + "integrity": "sha1-4k6/prY8sDh8X8F0+G5cyBLKfMk=", + "dev": true, + "requires": { + "bin-version": "^2.0.0", + "semver": "^5.1.0", + "semver-truncate": "^1.0.0" + } + }, + "binary-extensions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", + "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==" + }, + "binaryextensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-2.3.0.tgz", + "integrity": "sha512-nAihlQsYGyc5Bwq6+EsubvANYGExeJKHDO3RjnvwU042fawQTQfM3Kxn7IHUXQOz4bzfwsGYYHGSvXyW4zOGLg==", + "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==", + "optional": true, + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, + "bn.js": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.2.tgz", + "integrity": "sha512-40rZaf3bUNKTVYu9sIeeEGOg7g14Yvnj9kH7b50EiwX0Q7A6umbvfI5tvHaOERH0XigqKkfLkFQxzb4e6CIXnA==" + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "dependencies": { + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "bonjour": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", + "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", + "requires": { + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" + }, + "dependencies": { + "deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "requires": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + } + } + } + }, + "boolean": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.0.1.tgz", + "integrity": "sha512-HRZPIjPcbwAVQvOTxR4YE3o8Xs98NqbbL1iEZDCz7CL8ql0Lt5iOyJFxfnAB0oFs8Oh02F/lLlg30Mexv46LjA==", + "dev": true + }, + "boxen": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", + "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", + "dev": true, + "requires": { + "ansi-align": "^2.0.0", + "camelcase": "^4.0.0", + "chalk": "^2.0.1", + "cli-boxes": "^1.0.0", + "string-width": "^2.0.0", + "term-size": "^1.2.0", + "widest-line": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.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==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "requires": { + "fill-range": "^7.0.1" + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "requires": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" + } + } + }, + "browserify-sign": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.0.tgz", + "integrity": "sha512-hEZC1KEeYuoHRqhGhTy6gWrpJA3ZDjFWv0DE61643ZnOXAKJb3u7yWcrU0mMc9SwAqK1n7myPGndkp0dFG7NFA==", + "requires": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.2", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } + } + }, + "browserify-zlib": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", + "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", + "requires": { + "pako": "~1.0.5" + } + }, + "browserslist": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.12.0.tgz", + "integrity": "sha512-UH2GkcEDSI0k/lRkuDSzFl9ZZ87skSy9w2XAn1MsZnL+4c4rqbBd3e82UWHbYDpztABrPBhZsTEeuxVfHppqDg==", + "requires": { + "caniuse-lite": "^1.0.30001043", + "electron-to-chromium": "^1.3.413", + "node-releases": "^1.1.53", + "pkg-up": "^2.0.0" + } + }, + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + } + } + }, + "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==" + }, + "buffer-indexof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz", + "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==" + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" + }, + "builtin-status-codes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", + "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=" + }, + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=" + }, + "cacache": { + "version": "12.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", + "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", + "requires": { + "bluebird": "^3.5.5", + "chownr": "^1.1.1", + "figgy-pudding": "^3.5.1", + "glob": "^7.1.4", + "graceful-fs": "^4.1.15", + "infer-owner": "^1.0.3", + "lru-cache": "^5.1.1", + "mississippi": "^3.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.3", + "ssri": "^6.0.1", + "unique-filename": "^1.1.1", + "y18n": "^4.0.0" + } + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "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" + } + }, + "cacheable-request": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-2.1.4.tgz", + "integrity": "sha1-DYCIAbY0KtM8kd+dC0TcCbkeXD0=", + "dev": true, + "requires": { + "clone-response": "1.0.2", + "get-stream": "3.0.0", + "http-cache-semantics": "3.8.1", + "keyv": "3.0.0", + "lowercase-keys": "1.0.0", + "normalize-url": "2.0.1", + "responselike": "1.0.2" + }, + "dependencies": { + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + }, + "lowercase-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz", + "integrity": "sha1-TjNms55/VFfjXxMkvfb4jQv8cwY=", + "dev": true + } + } + }, + "call-me-maybe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", + "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=", + "dev": true + }, + "caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", + "requires": { + "callsites": "^2.0.0" + } + }, + "caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", + "requires": { + "caller-callsite": "^2.0.0" + } + }, + "callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=" + }, + "camel-case": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz", + "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", + "requires": { + "no-case": "^2.2.0", + "upper-case": "^1.1.1" + } + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + }, + "camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + } + }, + "caniuse-lite": { + "version": "1.0.30001081", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001081.tgz", + "integrity": "sha512-iZdh3lu09jsUtLE6Bp8NAbJskco4Y3UDtkR3GTCJGsbMowBU5IWDFF79sV2ws7lSqTzWyKazxam2thasHymENQ==" + }, + "capture-stack-trace": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz", + "integrity": "sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "ccount": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.0.5.tgz", + "integrity": "sha512-MOli1W+nfbPLlKEhInaxhRdp7KVLFxLN5ykwzHgLsLI3H3gs5jjFAK4Eoj3OzzcxCtumDaI8onoVDeQyWaNTkw==", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "change-case": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-3.1.0.tgz", + "integrity": "sha512-2AZp7uJZbYEzRPsFoa+ijKdvp9zsrnnt6+yFokfwEpeJm0xuJDVoxiRCAaTzyJND8GJkofo2IcKWaUZ/OECVzw==", + "requires": { + "camel-case": "^3.0.0", + "constant-case": "^2.0.0", + "dot-case": "^2.1.0", + "header-case": "^1.0.0", + "is-lower-case": "^1.1.0", + "is-upper-case": "^1.1.0", + "lower-case": "^1.1.1", + "lower-case-first": "^1.0.0", + "no-case": "^2.3.2", + "param-case": "^2.1.0", + "pascal-case": "^2.0.0", + "path-case": "^2.1.0", + "sentence-case": "^2.1.0", + "snake-case": "^2.1.0", + "swap-case": "^1.1.0", + "title-case": "^2.1.0", + "upper-case": "^1.1.1", + "upper-case-first": "^1.1.0" + } + }, + "change-emitter": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/change-emitter/-/change-emitter-0.1.6.tgz", + "integrity": "sha1-6LL+PX8at9aaMhma/5HqaTFAlRU=" + }, + "character-entities": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", + "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==" + }, + "character-entities-html4": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-1.1.4.tgz", + "integrity": "sha512-HRcDxZuZqMx3/a+qrzxdBKBPUpxWEq9xw2OPZ3a/174ihfrQKVsFhqtthBInFy1zZ9GgZyFXOatNujm8M+El3g==", + "dev": true + }, + "character-entities-legacy": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", + "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==" + }, + "character-reference-invalid": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", + "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==" + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, + "chokidar": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.0.tgz", + "integrity": "sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ==", + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.4.0" + } + }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, + "chrome-trace-event": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", + "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", + "requires": { + "tslib": "^1.9.0" + } + }, + "ci-info": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", + "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==", + "dev": true + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.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==", + "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=", + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "classnames": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz", + "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==" + }, + "clean-css": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz", + "integrity": "sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==", + "requires": { + "source-map": "~0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, + "cli-boxes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", + "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=", + "dev": true + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-list": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/cli-list/-/cli-list-0.2.0.tgz", + "integrity": "sha1-fmc+4N05phGkhkduU/PGs5QctYI=", + "dev": true + }, + "cli-table": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.1.tgz", + "integrity": "sha1-9TsFJmqLGguTSz0IIebi3FkUriM=", + "dev": true, + "requires": { + "colors": "1.0.3" + } + }, + "cli-width": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", + "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", + "dev": true + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.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-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "requires": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + } + }, + "clone-regexp": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clone-regexp/-/clone-regexp-2.2.0.tgz", + "integrity": "sha512-beMpP7BOtTipFuW8hrJvREQ2DrRu3BE7by0ZpibtfBA+qfHYvMGTc2Yb1JMYPKg/JUw0CHYvpg796aNTSW9z7Q==", + "dev": true, + "requires": { + "is-regexp": "^2.0.0" + } + }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, + "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" + } + }, + "clsx": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz", + "integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==" + }, + "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 + }, + "collapse-white-space": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/collapse-white-space/-/collapse-white-space-1.0.6.tgz", + "integrity": "sha512-jEovNnrhMuqyCcjfEJA56v0Xq8SkIoPKDyaHahwo3POf4qcSXqMYuwNcOTzp74vTsR9Tn08z4MxWqAhcekogkQ==" + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "colors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=", + "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" + } + }, + "combokeys": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/combokeys/-/combokeys-3.0.1.tgz", + "integrity": "sha512-5nAfaLZ3oO3kA+/xdoL7t197UJTz2WWidyH3BBeU6hqHtvyFERICd0y3DQFrQkJFTKBrtUDck/xCLLoFpnjaCw==" + }, + "commander": { + "version": "2.17.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz", + "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==" + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=" + }, + "compile-code-loader": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/compile-code-loader/-/compile-code-loader-0.2.0.tgz", + "integrity": "sha1-29nGZM0aS2pyCe92qo9eAMcNoh8=", + "requires": { + "loader-utils": "1.1.x" + }, + "dependencies": { + "big.js": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", + "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==" + }, + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=" + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=" + }, + "loader-utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", + "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", + "requires": { + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0" + } + } + } + }, + "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==" + }, + "compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "requires": { + "mime-db": ">= 1.43.0 < 2" + } + }, + "compression": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz", + "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==", + "requires": { + "accepts": "~1.3.5", + "bytes": "3.0.0", + "compressible": "~2.0.16", + "debug": "2.6.9", + "on-headers": "~1.0.2", + "safe-buffer": "5.1.2", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "conf": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/conf/-/conf-1.4.0.tgz", + "integrity": "sha512-bzlVWS2THbMetHqXKB8ypsXN4DQ/1qopGwNJi1eYbpwesJcd86FBjFciCQX/YwAhp9bM7NVnPFqZ5LpV7gP0Dg==", + "dev": true, + "requires": { + "dot-prop": "^4.1.0", + "env-paths": "^1.0.0", + "make-dir": "^1.0.0", + "pkg-up": "^2.0.0", + "write-file-atomic": "^2.3.0" + }, + "dependencies": { + "make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "write-file-atomic": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", + "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + } + } + }, + "config-chain": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz", + "integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==", + "dev": true, + "requires": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "configstore": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.2.tgz", + "integrity": "sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw==", + "dev": true, + "requires": { + "dot-prop": "^4.1.0", + "graceful-fs": "^4.1.2", + "make-dir": "^1.0.0", + "unique-string": "^1.0.0", + "write-file-atomic": "^2.0.0", + "xdg-basedir": "^3.0.0" + }, + "dependencies": { + "make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "write-file-atomic": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", + "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + } + } + }, + "conic-gradient": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/conic-gradient/-/conic-gradient-1.0.0.tgz", + "integrity": "sha512-TEmM3Ondx8nid2AN0Rsw6eQG7PgTUkL6gs90UqX1cNqO/bpt/H/Rw6DwbzoylQ9SSxqLG1SsteAr9/yBsAzdtw==", + "requires": { + "prefixfree": "^1.0.0" + } + }, + "connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==" + }, + "console-browserify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", + "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==" + }, + "consolidated-events": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/consolidated-events/-/consolidated-events-2.0.2.tgz", + "integrity": "sha512-2/uRVMdRypf5z/TW/ncD/66l75P5hH2vM/GR8Jf8HLc2xnfJtmina6F6du8+v4Z2vTrMo7jC+W1tmEEuuELgkQ==" + }, + "constant-case": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-2.0.0.tgz", + "integrity": "sha1-QXV2TTidP6nI7NKRhu1gBSQ7akY=", + "requires": { + "snake-case": "^2.1.0", + "upper-case": "^1.1.1" + } + }, + "constants-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", + "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=" + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "requires": { + "safe-buffer": "5.1.2" + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "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==", + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "copy-concurrently": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", + "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", + "requires": { + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" + }, + "core-js": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", + "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==" + }, + "core-js-compat": { + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.5.tgz", + "integrity": "sha512-7ItTKOhOZbznhXAQ2g/slGg1PJV5zDO/WdkTwi7UEOJmkvsE32PWvx6mKtDjiMpjnR2CNf6BAD6sSxIlv7ptng==", + "requires": { + "browserslist": "^4.8.5", + "semver": "7.0.0" + }, + "dependencies": { + "semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==" + } + } + }, + "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=" + }, + "cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "requires": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + } + }, + "create-ecdh": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", + "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" + } + } + }, + "create-error-class": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", + "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", + "dev": true, + "requires": { + "capture-stack-trace": "^1.0.0" + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "cross-spawn-async": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/cross-spawn-async/-/cross-spawn-async-2.2.5.tgz", + "integrity": "sha1-hF/wwINKPe2dFg2sptOQkGuyiMw=", + "dev": true, + "requires": { + "lru-cache": "^4.0.0", + "which": "^1.2.8" + }, + "dependencies": { + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + } + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "crypto-random-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", + "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=", + "dev": true + }, + "css-blank-pseudo": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-0.1.4.tgz", + "integrity": "sha512-LHz35Hr83dnFeipc7oqFDmsjHdljj3TQtxGGiNWSOsTLIAubSm4TEz8qCaKFpk7idaQ1GfWscF4E6mgpBysA1w==", + "requires": { + "postcss": "^7.0.5" + } + }, + "css-has-pseudo": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-0.10.0.tgz", + "integrity": "sha512-Z8hnfsZu4o/kt+AuFzeGpLVhFOGO9mluyHBaA2bA8aCGTwah5sT3WV/fTHH8UNZUytOIImuGPrl/prlb4oX4qQ==", + "requires": { + "postcss": "^7.0.6", + "postcss-selector-parser": "^5.0.0-rc.4" + }, + "dependencies": { + "cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==" + }, + "postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "requires": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "css-loader": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.5.3.tgz", + "integrity": "sha512-UEr9NH5Lmi7+dguAm+/JSPovNjYbm2k3TK58EiwQHzOHH5Jfq1Y+XoP2bQO6TMn7PptMd0opxxedAWcaSTRKHw==", + "requires": { + "camelcase": "^5.3.1", + "cssesc": "^3.0.0", + "icss-utils": "^4.1.1", + "loader-utils": "^1.2.3", + "normalize-path": "^3.0.0", + "postcss": "^7.0.27", + "postcss-modules-extract-imports": "^2.0.0", + "postcss-modules-local-by-default": "^3.0.2", + "postcss-modules-scope": "^2.2.0", + "postcss-modules-values": "^3.0.0", + "postcss-value-parser": "^4.0.3", + "schema-utils": "^2.6.6", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "css-prefers-color-scheme": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-3.1.1.tgz", + "integrity": "sha512-MTu6+tMs9S3EUqzmqLXEcgNRbNkkD/TGFvowpeoWJn5Vfq7FMgsmRQs9X5NXAURiOBmOxm/lLjsDNXDE6k9bhg==", + "requires": { + "postcss": "^7.0.5" + } + }, + "cssdb": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-4.4.0.tgz", + "integrity": "sha512-LsTAR1JPEM9TpGhl/0p3nQecC2LJ0kD8X5YARu1hk/9I1gril5vDtMZyNxcEpxxDj34YNck/ucjuoUd66K03oQ==" + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==" + }, + "csstype": { + "version": "2.6.10", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.10.tgz", + "integrity": "sha512-D34BqZU4cIlMCY93rZHbrq9pjTAQJ3U8S8rfBqjwHxkGPThWFjzZDQpgMJY0QViLxth6ZKYiwFBo14RdN44U/w==" + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "requires": { + "array-find-index": "^1.0.1" + } + }, + "cyclist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", + "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=" + }, + "dargs": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-6.1.0.tgz", + "integrity": "sha512-5dVBvpBLBnPwSsYXqfybFyehMmC/EenKEcf23AhCTgTf48JFBbmJKqoZBsERDnjL0FyiVTYWdFsRfTLHxLyKdQ==", + "dev": true + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "dateformat": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", + "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", + "dev": true + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "requires": { + "ms": "^2.1.1" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "decamelize-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.0.tgz", + "integrity": "sha1-0XGoeTMlKAfrPLYdwcFEXQeN8tk=", + "dev": true, + "requires": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "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=" + }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, + "deep-equal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.0.3.tgz", + "integrity": "sha512-Spqdl4H+ky45I9ByyJtXteOm9CaIrPmnIPmOhrkKGNYWeDgCvJ8jNYVCTjChxW4FqGuZnLHADc8EKRMX6+CgvA==", + "requires": { + "es-abstract": "^1.17.5", + "es-get-iterator": "^1.1.0", + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.2", + "is-regex": "^1.0.5", + "isarray": "^2.0.5", + "object-is": "^1.1.2", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "regexp.prototype.flags": "^1.3.0", + "side-channel": "^1.0.2", + "which-boxed-primitive": "^1.0.1", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.2" + } + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "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-gateway": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz", + "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==", + "requires": { + "execa": "^1.0.0", + "ip-regex": "^2.1.0" + } + }, + "default-uid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/default-uid/-/default-uid-1.0.0.tgz", + "integrity": "sha1-/O+p359axAyJFtkS3R/hFGqjxZ4=", + "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==", + "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==", + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "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==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "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==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "del": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", + "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", + "requires": { + "@types/glob": "^7.1.1", + "globby": "^6.1.0", + "is-path-cwd": "^2.0.0", + "is-path-in-cwd": "^2.0.0", + "p-map": "^2.0.0", + "pify": "^4.0.1", + "rimraf": "^2.6.3" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "dev": true + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=" + }, + "detect-node": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz", + "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==" + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" + } + } + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "dns-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", + "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=" + }, + "dns-packet": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz", + "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", + "requires": { + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" + } + }, + "dns-txt": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", + "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", + "requires": { + "buffer-indexof": "^1.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "dom-helpers": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.1.4.tgz", + "integrity": "sha512-TjMyeVUvNEnOnhzs6uAn9Ya47GmMo3qq7m+Lr/3ON0Rs5kHvb8I+SQYjLUSYn7qhEm0QjW0yrBkvz9yOrwwz1A==", + "requires": { + "@babel/runtime": "^7.8.7", + "csstype": "^2.6.7" + } + }, + "dom-serializer": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", + "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==", + "requires": { + "domelementtype": "^2.0.1", + "entities": "^2.0.0" + } + }, + "dom4": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/dom4/-/dom4-1.8.5.tgz", + "integrity": "sha512-ehHzOGGkVQOwU9HyZ99gHwkx4ybrRl/P1vJM7EH1nS9XsgHwO+J0KwCnVQrn8iQvpstGwFrtrX7aSNQ43QuK4A==" + }, + "domain-browser": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", + "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==" + }, + "domelementtype": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz", + "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==" + }, + "domhandler": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.0.0.tgz", + "integrity": "sha512-eKLdI5v9m67kbXQbJSNn1zjh0SDzvzWVWtX+qEI3eMjZw8daH9k8rlj1FZY9memPwjiskQFbe7vHVVJIAqoEhw==", + "requires": { + "domelementtype": "^2.0.1" + } + }, + "domutils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.1.0.tgz", + "integrity": "sha512-CD9M0Dm1iaHfQ1R/TI+z3/JWp/pgub0j4jIQKH89ARR4ATAV2nbaOQS5XxU9maJP5jHaPdDDQSEHuE2UmpUTKg==", + "requires": { + "dom-serializer": "^0.2.1", + "domelementtype": "^2.0.1", + "domhandler": "^3.0.0" + } + }, + "dot-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-2.1.1.tgz", + "integrity": "sha1-NNzzf1Co6TwrO8qLt/uRVcfaO+4=", + "requires": { + "no-case": "^2.2.0" + } + }, + "dot-prop": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", + "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", + "dev": true, + "requires": { + "is-obj": "^1.0.0" + } + }, + "downgrade-root": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/downgrade-root/-/downgrade-root-1.2.2.tgz", + "integrity": "sha1-UxMZcVsOgf/MIusoR4uidkPhLGw=", + "dev": true, + "requires": { + "default-uid": "^1.0.0", + "is-root": "^1.0.0" + } + }, + "download-stats": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/download-stats/-/download-stats-0.3.4.tgz", + "integrity": "sha512-ic2BigbyUWx7/CBbsfGjf71zUNZB4edBGC3oRliSzsoNmvyVx3Ycfp1w3vp2Y78Ee0eIIkjIEO5KzW0zThDGaA==", + "dev": true, + "requires": { + "JSONStream": "^1.2.1", + "lazy-cache": "^2.0.1", + "moment": "^2.15.1" + } + }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", + "dev": true + }, + "duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "requires": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "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" + } + }, + "editions": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/editions/-/editions-2.3.1.tgz", + "integrity": "sha512-ptGvkwTvGdGfC0hfhKg0MT+TRLRKGtUiWGBInxOm5pz7ssADezahjCUaYuZ8Dr+C05FW0AECIIPt4WBxVINEhA==", + "dev": true, + "requires": { + "errlop": "^2.0.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "ejs": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.7.4.tgz", + "integrity": "sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA==", + "dev": true + }, + "electron-to-chromium": { + "version": "1.3.468", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.468.tgz", + "integrity": "sha512-+KAppdklzPd5v8nLOvtDiD/S67mCT9gFRAvngYe8zuFy9azHhT9vWWH6WEPPCcyjQ1JMYVgqbN29yZ0paqxsEw==" + }, + "element-resize-detector": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/element-resize-detector/-/element-resize-detector-1.2.1.tgz", + "integrity": "sha512-BdFsPepnQr9fznNPF9nF4vQ457U/ZJXQDSNF1zBe7yaga8v9AdZf3/NElYxFdUh7SitSGt040QygiTo6dtatIw==", + "requires": { + "batch-processor": "1.0.0" + } + }, + "elliptic": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz", + "integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==", + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" + } + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==" + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "encoding": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", + "requires": { + "iconv-lite": "~0.4.13" + } + }, + "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==", + "requires": { + "once": "^1.4.0" + } + }, + "enhanced-resolve": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz", + "integrity": "sha1-BCHjOf1xQZs9oT0Smzl5BAIwR24=", + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.4.0", + "object-assign": "^4.0.1", + "tapable": "^0.2.7" + } + }, + "entities": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz", + "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==" + }, + "env-paths": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-1.0.0.tgz", + "integrity": "sha1-QWgTO0K7BcOKNbGuQ5fIKYqzaeA=", + "dev": true + }, + "errlop": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/errlop/-/errlop-2.2.0.tgz", + "integrity": "sha512-e64Qj9+4aZzjzzFpZC7p5kmm/ccCrbLhAJplhsDXQFs87XTsXwOpH4s1Io2s90Tau/8r2j9f4l/thhDevRjzxw==", + "dev": true + }, + "errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "requires": { + "prr": "~1.0.1" + } + }, + "error": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/error/-/error-7.2.1.tgz", + "integrity": "sha512-fo9HBvWnx3NGUKMvMwB/CBCMMrfEJgbDTVDEkPygA3Bdd3lM1OyCd+rbQ8BwnpF6GdVeOLDNmyL4N5Bg80ZvdA==", + "dev": true, + "requires": { + "string-template": "~0.2.1" + } + }, + "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==", + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "error-stack-parser": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-1.3.6.tgz", + "integrity": "sha1-4Oc7k+QXE40c18C3RrGkoUhUwpI=", + "requires": { + "stackframe": "^0.3.1" + } + }, + "es-abstract": { + "version": "1.17.5", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", + "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.1.5", + "is-regex": "^1.0.5", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimleft": "^2.1.1", + "string.prototype.trimright": "^2.1.1" + } + }, + "es-get-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.0.tgz", + "integrity": "sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==", + "requires": { + "es-abstract": "^1.17.4", + "has-symbols": "^1.0.1", + "is-arguments": "^1.0.4", + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-string": "^1.0.5", + "isarray": "^2.0.5" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==" + }, + "es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" + }, + "es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "requires": { + "es6-promise": "^4.0.3" + } + }, + "es6-templates": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/es6-templates/-/es6-templates-0.2.3.tgz", + "integrity": "sha1-XLmsn7He1usSOTQrgdeSu7QHjuQ=", + "requires": { + "recast": "~0.11.12", + "through": "~2.3.6" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "eslint": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz", + "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.10.0", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^1.4.3", + "eslint-visitor-keys": "^1.1.0", + "espree": "^6.1.2", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "inquirer": "^7.0.0", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.14", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.3", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^6.1.2", + "strip-ansi": "^5.2.0", + "strip-json-comments": "^3.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "eslint-scope": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.0.tgz", + "integrity": "sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + }, + "import-fresh": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", + "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", + "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.2.0.tgz", + "integrity": "sha512-WFb4ihckKil6hu3Dp798xdzSfddwKKU3+nGniKF6HfeW6OLd2OUDEPP7TcHtB5+QXOKg2s6B2DaMPE1Nn/kxKQ==", + "dev": true + }, + "espree": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz", + "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==", + "dev": true, + "requires": { + "acorn": "^7.1.1", + "acorn-jsx": "^5.2.0", + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "acorn": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz", + "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==", + "dev": true + } + } + }, + "esprima": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=" + }, + "esquery": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", + "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.1.0.tgz", + "integrity": "sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "eventemitter3": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz", + "integrity": "sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==" + }, + "events": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.1.0.tgz", + "integrity": "sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg==" + }, + "eventsource": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz", + "integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==", + "requires": { + "original": "^1.0.0" + } + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "execall": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/execall/-/execall-2.0.0.tgz", + "integrity": "sha512-0FU2hZ5Hh6iQnarpRtQurM/aAvp3RIbfvgLHrcqJYzhXyV2KFruhuChf9NC6waAhiUR7FFtlugkI4p7f2Fqlow==", + "dev": true, + "requires": { + "clone-regexp": "^2.1.0" + } + }, + "exit-hook": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", + "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=", + "dev": true + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "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": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "requires": { + "assign-symbols": "^1.0.0", + "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==", + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "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=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "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==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "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==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "extricate-loader": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/extricate-loader/-/extricate-loader-3.0.0.tgz", + "integrity": "sha512-Ts6BIh25xhFpeGaG0La345bYQdRXWv3ZvUwmJB6/QsXRywWrZmM1hGz8eZfQaBwy/HsmGOZevzGLesVtsrrmyg==", + "requires": { + "loader-utils": "^1.1.0" + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "fast-glob": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.2.tgz", + "integrity": "sha512-UDV82o4uQyljznxwMxyVRJgZZt3O5wENYojjzbaGEGZgeOxkLFf+V4cnUD+krzb2F72E18RhamkMZ7AdeggF7A==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.0", + "merge2": "^1.3.0", + "micromatch": "^4.0.2", + "picomatch": "^2.2.1" + } + }, + "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==" + }, + "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 + }, + "fastparse": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz", + "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==" + }, + "fastq": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.8.0.tgz", + "integrity": "sha512-SMIZoZdLh/fgofivvIkmknUXyPnvxRE3DhtZ5Me3Mrsk5gyPL42F0xr51TdRXskBxHfMp+07bcYzfsYEsSQA9Q==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "faye-websocket": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", + "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", + "requires": { + "websocket-driver": ">=0.5.1" + } + }, + "fbjs": { + "version": "0.8.17", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz", + "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=", + "requires": { + "core-js": "^1.0.0", + "isomorphic-fetch": "^2.1.1", + "loose-envify": "^1.0.0", + "object-assign": "^4.1.0", + "promise": "^7.1.1", + "setimmediate": "^1.0.5", + "ua-parser-js": "^0.7.18" + }, + "dependencies": { + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" + } + } + }, + "figgy-pudding": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", + "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==" + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "dev": true, + "requires": { + "flat-cache": "^2.0.1" + } + }, + "file-loader": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-5.1.0.tgz", + "integrity": "sha512-u/VkLGskw3Ue59nyOwUwXI/6nuBCo7KBkniB/l7ICwr/7cPNGsL1WCXUp3GB0qgOOKU1TiP49bv4DZF/LJqprg==", + "requires": { + "loader-utils": "^1.4.0", + "schema-utils": "^2.5.0" + } + }, + "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==", + "optional": true + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "filter-obj": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-2.0.1.tgz", + "integrity": "sha512-yDEp513p7+iLdFHWBVdZFnRiOYwg8ZqmpaAiZCMjzqsbo7tCS4Qm4ulXOht337NGzkukKa9u3W4wqQ9tQPm3Ug==", + "dev": true + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "requires": { + "locate-path": "^2.0.0" + } + }, + "find-versions": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-2.0.0.tgz", + "integrity": "sha1-KtkNSQ9oKMGqQCks9wmsMxghDDw=", + "dev": true, + "requires": { + "array-uniq": "^1.0.0", + "semver-regex": "^1.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==", + "requires": { + "detect-file": "^1.0.0", + "is-glob": "^4.0.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + }, + "dependencies": { + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "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" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "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=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "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" + } + }, + "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=", + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } + }, + "first-chunk-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-2.0.0.tgz", + "integrity": "sha1-G97NuOCDwGZLkZRVgVd6Q6nzHXA=", + "dev": true, + "requires": { + "readable-stream": "^2.0.2" + } + }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "dev": true, + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + }, + "dependencies": { + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "flatted": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", + "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", + "dev": true + }, + "flatten": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.3.tgz", + "integrity": "sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg==" + }, + "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==", + "requires": { + "inherits": "^2.0.3", + "readable-stream": "^2.3.6" + } + }, + "focus-trap": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-5.1.0.tgz", + "integrity": "sha512-CkB/nrO55069QAUjWFBpX6oc+9V90Qhgpe6fBWApzruMq5gnlh90Oo7iSSDK7pKiV5ugG6OY2AXM5mxcmL3lwQ==", + "requires": { + "tabbable": "^4.0.0", + "xtend": "^4.0.1" + } + }, + "focus-visible": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/focus-visible/-/focus-visible-5.1.0.tgz", + "integrity": "sha512-nPer0rjtzdZ7csVIu233P2cUm/ks/4aVSI+5KUkYrYpgA7ujgC3p6J7FtFU+AIMWwnwYQOB/yeiOITxFeYIXiw==" + }, + "follow-redirects": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.11.0.tgz", + "integrity": "sha512-KZm0V+ll8PfBrKwMzdo5D13b1bur9Iq9Zd/RMmAoQQcl2PxxFml8cxXPaaPYVbV0RjNjq1CU7zIzAOqtUPudmA==", + "requires": { + "debug": "^3.0.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" + }, + "foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" + }, + "foreachasync": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/foreachasync/-/foreachasync-3.0.0.tgz", + "integrity": "sha1-VQKYfchxS+M5IJfzLgBxyd7gfPY=", + "dev": true + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.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" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "requires": { + "map-cache": "^0.2.2" + } + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "from2": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", + "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", + "requires": { + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" + } + }, + "fs-write-stream-atomic": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", + "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", + "requires": { + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "optional": true + }, + "fullname": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/fullname/-/fullname-4.0.1.tgz", + "integrity": "sha512-jVT8q9Ah9JwqfIGKwKzTdbRRthdPpIjEe9kgvxM104Tv+q6SgOAQqJMVP90R0DBRAqejGMHDRWJtl3Ats6BjfQ==", + "dev": true, + "requires": { + "execa": "^1.0.0", + "filter-obj": "^2.0.0", + "mem": "^5.1.0", + "p-any": "^2.1.0", + "passwd-user": "^3.0.0", + "rc": "^1.2.8" + }, + "dependencies": { + "mem": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/mem/-/mem-5.1.1.tgz", + "integrity": "sha512-qvwipnozMohxLXG1pOqoLiZKNkC4r4qqRucSoDwXowsNGDSULiqFTRUF05vcZWnwJSG22qTsynQhxbaMtnX9gw==", + "dev": true, + "requires": { + "map-age-cleaner": "^0.1.3", + "mimic-fn": "^2.1.0", + "p-is-promise": "^2.1.0" + } + } + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "gauge": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-1.2.7.tgz", + "integrity": "sha1-6c7FSD09TuDvRLYKfZnkk14TbZM=", + "dev": true, + "requires": { + "ansi": "^0.3.0", + "has-unicode": "^2.0.0", + "lodash.pad": "^4.1.0", + "lodash.padend": "^4.1.0", + "lodash.padstart": "^4.1.0" + } + }, + "gensync": { + "version": "1.0.0-beta.1", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", + "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==" + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + }, + "get-node-dimensions": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/get-node-dimensions/-/get-node-dimensions-1.2.1.tgz", + "integrity": "sha512-2MSPMu7S1iOTL+BOa6K1S62hB2zUAYNF/lV0gSVlOaacd087lc6nR1H1r0e3B1CerTo+RceOmi1iJW+vp21xcQ==" + }, + "get-stdin": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", + "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==", + "dev": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" + }, + "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" + } + }, + "gh-got": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/gh-got/-/gh-got-5.0.0.tgz", + "integrity": "sha1-7pW+NxBv2HSKlvjR20uuqJ4b+oo=", + "dev": true, + "requires": { + "got": "^6.2.0", + "is-plain-obj": "^1.1.0" + }, + "dependencies": { + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + }, + "got": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", + "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", + "dev": true, + "requires": { + "create-error-class": "^3.0.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-redirect": "^1.0.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "lowercase-keys": "^1.0.0", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "unzip-response": "^2.0.1", + "url-parse-lax": "^1.0.0" + } + }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", + "dev": true + }, + "url-parse-lax": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", + "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", + "dev": true, + "requires": { + "prepend-http": "^1.0.1" + } + } + } + }, + "github-username": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/github-username/-/github-username-3.0.0.tgz", + "integrity": "sha1-CnciGbMTB0NCnyRW0L3T21Xc57E=", + "dev": true, + "requires": { + "gh-got": "^5.0.0" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "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": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "requires": { + "is-glob": "^4.0.1" + } + }, + "glob-to-regexp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz", + "integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=", + "dev": true + }, + "global-agent": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-2.1.12.tgz", + "integrity": "sha512-caAljRMS/qcDo69X9BfkgrihGUgGx44Fb4QQToNQjsiWh+YlQ66uqYVAdA8Olqit+5Ng0nkz09je3ZzANMZcjg==", + "dev": true, + "requires": { + "boolean": "^3.0.1", + "core-js": "^3.6.5", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" + }, + "dependencies": { + "core-js": { + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", + "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==", + "dev": true + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true + } + } + }, + "global-dirs": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", + "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", + "dev": true, + "requires": { + "ini": "^1.3.4" + } + }, + "global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "requires": { + "global-prefix": "^3.0.0" + }, + "dependencies": { + "global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "requires": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + } + } + } + }, + "global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "requires": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + } + }, + "global-tunnel-ng": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz", + "integrity": "sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg==", + "dev": true, + "requires": { + "encodeurl": "^1.0.2", + "lodash": "^4.17.10", + "npm-conf": "^1.1.3", + "tunnel": "^0.0.6" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" + }, + "globalthis": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.1.tgz", + "integrity": "sha512-mJPRTc/P39NH/iNG4mXa9aIhNymaQikTrnspeCa2ZuJ+mH2QN/rXwtX3XwKrHqWgUQFbNZKtHM105aHzJalElw==", + "dev": true, + "requires": { + "define-properties": "^1.1.3" + } + }, + "globby": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", + "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", + "requires": { + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + } + } + }, + "globjoin": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz", + "integrity": "sha1-L0SUrIkZ43Z8XLtpHp9GMyQoXUM=", + "dev": true + }, + "gonzales-pe": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/gonzales-pe/-/gonzales-pe-4.3.0.tgz", + "integrity": "sha512-otgSPpUmdWJ43VXyiNgEYE4luzHCL2pz4wQ0OnDluC6Eg4Ko3Vexy/SrSynglw/eR+OhkzmqFCZa/OFa/RgAOQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "got": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/got/-/got-8.3.2.tgz", + "integrity": "sha512-qjUJ5U/hawxosMryILofZCkm3C84PLJS/0grRIpjAwu+Lkxxj5cxeCU25BG0/3mDSpXKTyZr8oh8wIgLaH0QCw==", + "dev": true, + "requires": { + "@sindresorhus/is": "^0.7.0", + "cacheable-request": "^2.1.1", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "into-stream": "^3.1.0", + "is-retry-allowed": "^1.1.0", + "isurl": "^1.0.0-alpha5", + "lowercase-keys": "^1.0.0", + "mimic-response": "^1.0.0", + "p-cancelable": "^0.4.0", + "p-timeout": "^2.0.1", + "pify": "^3.0.0", + "safe-buffer": "^5.1.1", + "timed-out": "^4.0.1", + "url-parse-lax": "^3.0.0", + "url-to-options": "^1.0.1" + }, + "dependencies": { + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + }, + "p-cancelable": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.4.1.tgz", + "integrity": "sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ==", + "dev": true + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + } + } + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" + }, + "grouped-queue": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/grouped-queue/-/grouped-queue-1.1.0.tgz", + "integrity": "sha512-rZOFKfCqLhsu5VqjBjEWiwrYqJR07KxIkH4mLZlNlGDfntbb4FbMyGFP14TlvRPrU9S3Hnn/sgxbC5ZeN0no3Q==", + "dev": true, + "requires": { + "lodash": "^4.17.15" + } + }, + "handle-thing": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", + "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==" + }, + "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.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "dev": true, + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "has-symbol-support-x": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", + "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==", + "dev": true + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" + }, + "has-to-string-tag-x": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", + "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", + "dev": true, + "requires": { + "has-symbol-support-x": "^1.4.1" + } + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "dev": true + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "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=", + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "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=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "requires": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + } + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" + }, + "header-case": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/header-case/-/header-case-1.0.1.tgz", + "integrity": "sha1-lTWXMZfBRLCWE81l0xfvGZY70C0=", + "requires": { + "no-case": "^2.2.0", + "upper-case": "^1.1.3" + } + }, + "highlight.js": { + "version": "9.18.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.18.1.tgz", + "integrity": "sha512-OrVKYz70LHsnCgmbXctv/bfuvntIKDz177h0Co37DQ5jamGZLVmoCVMtjMtNZY3X9DrCcKfklHPNeA0uPZhSJg==" + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "hoist-non-react-statics": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz", + "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==" + }, + "homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "requires": { + "parse-passwd": "^1.0.0" + } + }, + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true + }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", + "requires": { + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" + } + }, + "html-entities": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.3.1.tgz", + "integrity": "sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA==" + }, + "html-loader": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/html-loader/-/html-loader-0.5.5.tgz", + "integrity": "sha512-7hIW7YinOYUpo//kSYcPB6dCKoceKLmOwjEMmhIobHuWGDVl0Nwe4l68mdG/Ru0wcUxQjVMEoZpkalZ/SE7zog==", + "requires": { + "es6-templates": "^0.2.3", + "fastparse": "^1.1.1", + "html-minifier": "^3.5.8", + "loader-utils": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "html-minifier": { + "version": "3.5.21", + "resolved": "https://registry.npmjs.org/html-minifier/-/html-minifier-3.5.21.tgz", + "integrity": "sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA==", + "requires": { + "camel-case": "3.0.x", + "clean-css": "4.2.x", + "commander": "2.17.x", + "he": "1.2.x", + "param-case": "2.1.x", + "relateurl": "0.2.x", + "uglify-js": "3.4.x" + } + }, + "html-tags": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz", + "integrity": "sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==", + "dev": true + }, + "html-to-react": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/html-to-react/-/html-to-react-1.4.3.tgz", + "integrity": "sha512-txe09A3vxW8yEZGJXJ1is5gGDfBEVACmZDSgwDyH5EsfRdOubBwBCg63ZThZP0xBn0UE4FyvMXZXmohusCxDcg==", + "requires": { + "domhandler": "^3.0", + "htmlparser2": "^4.1.0", + "lodash.camelcase": "^4.3.0", + "ramda": "^0.27" + } + }, + "htmlparser2": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-4.1.0.tgz", + "integrity": "sha512-4zDq1a1zhE4gQso/c5LP1OtrhYTncXNSpvJYtWJBtXAETPlMfi3IFNjGuQbYLuVY4ZR0QMqRVvo4Pdy9KLyP8Q==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^3.0.0", + "domutils": "^2.0.0", + "entities": "^2.0.0" + } + }, + "http-cache-semantics": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz", + "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==", + "dev": true + }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=" + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + } + } + }, + "http-proxy": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", + "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", + "requires": { + "eventemitter3": "^4.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" + } + }, + "http-proxy-middleware": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz", + "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==", + "requires": { + "http-proxy": "^1.17.0", + "is-glob": "^4.0.0", + "lodash": "^4.17.11", + "micromatch": "^3.1.10" + }, + "dependencies": { + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "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" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "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=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "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" + } + }, + "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=", + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } + }, + "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" + } + }, + "https-browserify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", + "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=" + }, + "human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true + }, + "humanize-string": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/humanize-string/-/humanize-string-1.0.2.tgz", + "integrity": "sha512-PH5GBkXqFxw5+4eKaKRIkD23y6vRd/IXSl7IldyJxEXpDH9SEIXRORkBtkGni/ae2P7RVOw6Wxypd2tGXhha1w==", + "dev": true, + "requires": { + "decamelize": "^1.0.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "icss-utils": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.1.tgz", + "integrity": "sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==", + "requires": { + "postcss": "^7.0.14" + } + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, + "iferr": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", + "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=" + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "import-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz", + "integrity": "sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=", + "requires": { + "import-from": "^2.1.0" + } + }, + "import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", + "requires": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + } + }, + "import-from": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz", + "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=", + "requires": { + "resolve-from": "^3.0.0" + } + }, + "import-lazy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", + "dev": true + }, + "import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "requires": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + } + }, + "imports-loader": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/imports-loader/-/imports-loader-0.8.0.tgz", + "integrity": "sha512-kXWL7Scp8KQ4552ZcdVTeaQCZSLW+e6nJfp3cwUMB673T7Hr98Xjx5JK+ql7ADlJUvj1JS5O01RLbKoutN5QDQ==", + "requires": { + "loader-utils": "^1.0.2", + "source-map": "^0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, + "indexes-of": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", + "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=" + }, + "infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "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==" + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" + }, + "inquirer": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.1.0.tgz", + "integrity": "sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^3.0.0", + "cli-cursor": "^3.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.15", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.5.3", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "insight": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/insight/-/insight-0.10.3.tgz", + "integrity": "sha512-YOncxSN6Omh+1Oqxt+OJAvJVMDKw7l6IEG0wT2cTMGxjsTcroOGW4IR926QDzxg/uZHcFZ2cZbckDWdZhc2pZw==", + "dev": true, + "requires": { + "async": "^2.6.2", + "chalk": "^2.4.2", + "conf": "^1.4.0", + "inquirer": "^6.3.1", + "lodash.debounce": "^4.0.8", + "os-name": "^3.1.0", + "request": "^2.88.0", + "tough-cookie": "^3.0.1", + "uuid": "^3.3.2" + }, + "dependencies": { + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "inquirer": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz", + "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==", + "dev": true, + "requires": { + "ansi-escapes": "^3.2.0", + "chalk": "^2.4.2", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^2.0.0", + "lodash": "^4.17.12", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^6.4.0", + "string-width": "^2.1.0", + "strip-ansi": "^5.1.0", + "through": "^2.3.6" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + } + } + } + } + }, + "internal-ip": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz", + "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==", + "requires": { + "default-gateway": "^4.2.0", + "ipaddr.js": "^1.9.0" + } + }, + "interpolate-loader": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/interpolate-loader/-/interpolate-loader-2.0.1.tgz", + "integrity": "sha512-X5/cKHUnAS5gV/oK9Z6pEjg2xVH5EGgnC5QmaOPwK/o7qMOMyyafwFL1mtH3yAK+COCjyaH56MOs9G8uXG12yA==", + "requires": { + "escape-string-regexp": "^2.0.0", + "loader-utils": "^1.1.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==" + } + } + }, + "interpret": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz", + "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==" + }, + "into-stream": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz", + "integrity": "sha1-lvsKk2wSur1v8XUqF9BWFqvQlMY=", + "dev": true, + "requires": { + "from2": "^2.1.1", + "p-is-promise": "^1.1.0" + }, + "dependencies": { + "p-is-promise": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz", + "integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=", + "dev": true + } + } + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "requires": { + "loose-envify": "^1.0.0" + } + }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==" + }, + "ip": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" + }, + "ip-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", + "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=" + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "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==" + }, + "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=", + "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=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-alphabetical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", + "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==" + }, + "is-alphanumeric": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-alphanumeric/-/is-alphanumeric-1.0.0.tgz", + "integrity": "sha1-Spzvcdr0wAHB2B1j0UDPU/1oifQ=", + "dev": true + }, + "is-alphanumerical": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", + "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", + "requires": { + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0" + } + }, + "is-arguments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==" + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "is-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.0.tgz", + "integrity": "sha512-t5mGUXC/xRheCK431ylNiSkGGpBp8bHENBcENTkDT6ppwPzEVxNGZRvgvmOEfbWkFhA7D2GEuE2mmQTr78sl2g==" + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-boolean-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.0.1.tgz", + "integrity": "sha512-TqZuVwa/sppcrhUCAYkGBk7w0yxfQQnxq28fjkO53tnK9FQXmdwz2JS5+GjsWQ6RByES1K40nI+yDic5c9/aAQ==" + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "is-callable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", + "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==" + }, + "is-ci": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", + "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", + "dev": true, + "requires": { + "ci-info": "^1.5.0" + } + }, + "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=", + "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=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-date-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==" + }, + "is-decimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-1.0.4.tgz", + "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==" + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "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==" + } + } + }, + "is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=" + }, + "is-docker": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-1.1.0.tgz", + "integrity": "sha1-8EN01O7lMQ6ajhE78UlUEeRhdqE=", + "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=" + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" + }, + "is-finite": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "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==", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-hexadecimal": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", + "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==" + }, + "is-installed-globally": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", + "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", + "dev": true, + "requires": { + "global-dirs": "^0.1.0", + "is-path-inside": "^1.0.0" + }, + "dependencies": { + "is-path-inside": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", + "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", + "dev": true, + "requires": { + "path-is-inside": "^1.0.1" + } + } + } + }, + "is-lower-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-lower-case/-/is-lower-case-1.1.3.tgz", + "integrity": "sha1-fhR75HaNxGbbO/shzGCzHmrWk5M=", + "requires": { + "lower-case": "^1.1.0" + } + }, + "is-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.1.tgz", + "integrity": "sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==" + }, + "is-npm": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", + "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "is-number-object": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz", + "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==" + }, + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "dev": true + }, + "is-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", + "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=", + "dev": true + }, + "is-path-cwd": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", + "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==" + }, + "is-path-in-cwd": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", + "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", + "requires": { + "is-path-inside": "^2.1.0" + } + }, + "is-path-inside": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", + "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", + "requires": { + "path-is-inside": "^1.0.2" + } + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=" + }, + "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==", + "requires": { + "isobject": "^3.0.1" + } + }, + "is-redirect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", + "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=", + "dev": true + }, + "is-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", + "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", + "requires": { + "has-symbols": "^1.0.1" + } + }, + "is-regexp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-2.1.0.tgz", + "integrity": "sha512-OZ4IlER3zmRIoB9AqNhEggVxqIH4ofDns5nRrPS6yQxXE1TPCUpFznBfRQmQa8uC+pXqjMnukiJBxCisIxiLGA==", + "dev": true + }, + "is-retry-allowed": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", + "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==", + "dev": true + }, + "is-root": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-root/-/is-root-1.0.0.tgz", + "integrity": "sha1-B7bCM7w5TNnQK6FclmvWZg1jQtU=", + "dev": true + }, + "is-scoped": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-scoped/-/is-scoped-1.0.0.tgz", + "integrity": "sha1-RJypgpnnEwOCViieyytUDcQ3yzA=", + "dev": true, + "requires": { + "scoped-regex": "^1.0.0" + } + }, + "is-set": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz", + "integrity": "sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==" + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "is-string": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==" + }, + "is-supported-regexp-flag": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-supported-regexp-flag/-/is-supported-regexp-flag-1.0.1.tgz", + "integrity": "sha512-3vcJecUUrpgCqc/ca0aWeNu64UGgxcvO60K/Fkr1N6RSvfGCTU60UKN68JDmKokgba0rFFJs12EnzOQa14ubKQ==", + "dev": true + }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "requires": { + "has-symbols": "^1.0.1" + } + }, + "is-typed-array": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.3.tgz", + "integrity": "sha512-BSYUBOK/HJibQ30wWkWold5txYwMUXQct9YHAQJr8fSwvZoiglcqB0pd7vEN23+Tsi9IUEjztdOSzl4qLVYGTQ==", + "requires": { + "available-typed-arrays": "^1.0.0", + "es-abstract": "^1.17.4", + "foreach": "^2.0.5", + "has-symbols": "^1.0.1" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-upper-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-upper-case/-/is-upper-case-1.1.2.tgz", + "integrity": "sha1-jQsfp+eTOh5YSDYA7H2WYcuvdW8=", + "requires": { + "upper-case": "^1.1.0" + } + }, + "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-weakmap": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", + "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==" + }, + "is-weakset": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.1.tgz", + "integrity": "sha512-pi4vhbhVHGLxohUw7PhGsueT4vRGFoXhP7+RGN0jKIv9+8PWYCQTqtADngrxOm2g46hoH0+g8uZZBzMrvVGDmw==" + }, + "is-whitespace-character": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz", + "integrity": "sha512-SDweEzfIZM0SJV0EUga669UTKlmL0Pq8Lno0QDQsPnvECB3IM2aP0gdx5TrU0A01MAPfViaZiI2V1QMZLaKK5w==" + }, + "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==" + }, + "is-word-character": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-word-character/-/is-word-character-1.0.4.tgz", + "integrity": "sha512-5SMO8RVennx3nZrqtKwCGyyetPE9VDba5ugvKLaD4KopPG5kR4mQ7tNt/r7feL5yt5h3lpuBbIUmCOG2eSzXHA==" + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=" + }, + "isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" + }, + "isbinaryfile": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.6.tgz", + "integrity": "sha512-ORrEy+SNVqUhrCaal4hA4fBzhggQQ+BaLntyPOdoEiwlKZW9BZiJXjg3RMiruE4tPEI3pyVPpySHQF/dKWperg==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" + }, + "isomorphic-fetch": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", + "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", + "requires": { + "node-fetch": "^1.0.1", + "whatwg-fetch": ">=0.10.0" + } + }, + "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 + }, + "istextorbinary": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-2.6.0.tgz", + "integrity": "sha512-+XRlFseT8B3L9KyjxxLjfXSLMuErKDsd8DBNrsaxoViABMEZlOSCstwmw0qpoFX3+U6yWU1yhLudAe6/lETGGA==", + "dev": true, + "requires": { + "binaryextensions": "^2.1.2", + "editions": "^2.2.0", + "textextensions": "^2.5.0" + } + }, + "isurl": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", + "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", + "dev": true, + "requires": { + "has-to-string-tag-x": "^1.2.0", + "is-object": "^1.0.1" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "js-yaml": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "dependencies": { + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + } + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" + }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + }, + "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==" + }, + "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 + }, + "json3": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz", + "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==" + }, + "json5": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", + "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "requires": { + "minimist": "^1.2.5" + } + }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=", + "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-it": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/just-debounce-it/-/just-debounce-it-1.1.0.tgz", + "integrity": "sha512-87Nnc0qZKgBZuhFZjYVjSraic0x7zwjhaTMrCKlj0QYKH6lh0KbFzVnfu6LHan03NO7J8ygjeBeD0epejn5Zcg==" + }, + "keyv": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.0.0.tgz", + "integrity": "sha512-eguHnq22OE3uVoSYG0LVWNP+4ppamWr9+zWBe1bsNcovIMy6huUJFPgy4mGwCd/rnl3vOLGW1MTlu4c57CT1xA==", + "dev": true, + "requires": { + "json-buffer": "3.0.0" + } + }, + "killable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", + "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==" + }, + "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==" + }, + "known-css-properties": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.19.0.tgz", + "integrity": "sha512-eYboRV94Vco725nKMlpkn3nV2+96p9c3gKXRsYqAJSswSENvBhN7n5L+uDhY58xQa0UukWsDMTGELzmD8Q+wTA==", + "dev": true + }, + "latest-version": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", + "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", + "dev": true, + "requires": { + "package-json": "^4.0.0" + }, + "dependencies": { + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + }, + "got": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", + "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", + "dev": true, + "requires": { + "create-error-class": "^3.0.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-redirect": "^1.0.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "lowercase-keys": "^1.0.0", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "unzip-response": "^2.0.1", + "url-parse-lax": "^1.0.0" + } + }, + "package-json": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", + "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", + "dev": true, + "requires": { + "got": "^6.7.1", + "registry-auth-token": "^3.0.1", + "registry-url": "^3.0.3", + "semver": "^5.1.0" + } + }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", + "dev": true + }, + "url-parse-lax": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", + "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", + "dev": true, + "requires": { + "prepend-http": "^1.0.1" + } + } + } + }, + "lazy-cache": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-2.0.2.tgz", + "integrity": "sha1-uRkKT5EzVGlIQIWfio9whNiCImQ=", + "dev": true, + "requires": { + "set-getter": "^0.1.0" + } + }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "requires": { + "invert-kv": "^2.0.0" + } + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==" + }, + "levenary": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/levenary/-/levenary-1.1.1.tgz", + "integrity": "sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ==", + "requires": { + "leven": "^3.1.0" + } + }, + "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" + } + }, + "lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "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" + }, + "dependencies": { + "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" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "loader-runner": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", + "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==" + }, + "loader-utils": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", + "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^1.0.1" + }, + "dependencies": { + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "requires": { + "minimist": "^1.2.0" + } + } + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "locutus": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/locutus/-/locutus-2.0.11.tgz", + "integrity": "sha512-C0q1L38lK5q1t+wE0KY21/9szrBHxye6o2z5EJzU+5B79tubNOC+nLAEzTTn1vPUGoUuehKh8kYKqiVUTWRyaQ==", + "dev": true, + "requires": { + "es6-promise": "^4.2.5" + } + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + }, + "lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=" + }, + "lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=" + }, + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", + "dev": true + }, + "lodash.pad": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/lodash.pad/-/lodash.pad-4.5.1.tgz", + "integrity": "sha1-QzCUmoM6fI2iLMIPaibE1Z3runA=", + "dev": true + }, + "lodash.padend": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz", + "integrity": "sha1-U8y6BH0G4VjTEfRdpiX05J5vFm4=", + "dev": true + }, + "lodash.padstart": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.padstart/-/lodash.padstart-4.6.1.tgz", + "integrity": "sha1-0uPuv/DZ05rVD1y9G1KnvOa7YRs=", + "dev": true + }, + "lodash.template": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", + "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==", + "requires": { + "lodash._reinterpolate": "^3.0.0", + "lodash.templatesettings": "^4.0.0" + } + }, + "lodash.templatesettings": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz", + "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==", + "requires": { + "lodash._reinterpolate": "^3.0.0" + } + }, + "log-symbols": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", + "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", + "dev": true, + "requires": { + "chalk": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "loglevel": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.8.tgz", + "integrity": "sha512-bsU7+gc9AJ2SqpzxwU3+1fedl8zAntbtC5XYlt3s2j1hJcn2PsXSmgN8TaLG/J1/2mod4+cE/3vNL70/c1RNCA==" + }, + "longest-streak": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-2.0.4.tgz", + "integrity": "sha512-vM6rUVCVUJJt33bnmHiZEvr7wPT78ztX7rojL+LW51bHtLh6HTjx84LA5W4+oa6aKEJA7jJu5LR6vQRBpA5DVg==", + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, + "lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=" + }, + "lower-case-first": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/lower-case-first/-/lower-case-first-1.0.2.tgz", + "integrity": "sha1-5dp8JvKacHO+AtUrrJmA5ZIq36E=", + "requires": { + "lower-case": "^1.1.2" + } + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "requires": { + "yallist": "^3.0.2" + } + }, + "macos-release": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.3.0.tgz", + "integrity": "sha512-OHhSbtcviqMPt7yfw5ef5aghS2jzFVKEFyCJndQt2YpSQ9qRVSEv2axSJI1paVThEu+FFGs584h/1YhxjVqajA==", + "dev": true + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "requires": { + "p-defer": "^1.0.0" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" + }, + "map-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.1.0.tgz", + "integrity": "sha512-glc9y00wgtwcDmp7GaE/0b0OnxpNJsVf3ael/An6Fe2Q51LLwN1er6sdomLRzz5h0+yMpiYLhWYF5R7HeqVd4g==", + "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=", + "requires": { + "object-visit": "^1.0.0" + } + }, + "markdown-escapes": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.4.tgz", + "integrity": "sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg==" + }, + "markdown-table": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-2.0.0.tgz", + "integrity": "sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==", + "dev": true, + "requires": { + "repeat-string": "^1.0.0" + } + }, + "matcher": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", + "dev": true, + "requires": { + "escape-string-regexp": "^4.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + } + } + }, + "mathml-tag-names": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz", + "integrity": "sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==", + "dev": true + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "mdast-add-list-metadata": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdast-add-list-metadata/-/mdast-add-list-metadata-1.0.1.tgz", + "integrity": "sha512-fB/VP4MJ0LaRsog7hGPxgOrSL3gE/2uEdZyDuSEnKCv/8IkYHiDkIQSbChiJoHyxZZXZ9bzckyRk+vNxFzh8rA==", + "requires": { + "unist-util-visit-parents": "1.1.2" + } + }, + "mdast-util-compact": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-compact/-/mdast-util-compact-2.0.1.tgz", + "integrity": "sha512-7GlnT24gEwDrdAwEHrU4Vv5lLWrEer4KOkAiKT9nYstsTad7Oc1TwqT2zIMKRdZF7cTuaf+GA1E4Kv7jJh8mPA==", + "dev": true, + "requires": { + "unist-util-visit": "^2.0.0" + }, + "dependencies": { + "unist-util-is": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.0.2.tgz", + "integrity": "sha512-Ofx8uf6haexJwI1gxWMGg6I/dLnF2yE+KibhD3/diOqY2TinLcqHXCV6OI5gFVn3xQqDH+u0M625pfKwIwgBKQ==", + "dev": true + }, + "unist-util-visit": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.2.tgz", + "integrity": "sha512-HoHNhGnKj6y+Sq+7ASo2zpVdfdRifhTgX2KTU3B/sO/TTlZchp7E3S4vjRzDJ7L60KmrCPsQkVK3lEF3cz36XQ==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0", + "unist-util-visit-parents": "^3.0.0" + } + }, + "unist-util-visit-parents": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.0.2.tgz", + "integrity": "sha512-yJEfuZtzFpQmg1OSCyS9M5NJRrln/9FbYosH3iW0MG402QbdbaB8ZESwUv9RO6nRfLAKvWcMxCwdLWOov36x/g==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0" + } + } + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "mem": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz", + "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==", + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^2.0.0", + "p-is-promise": "^2.0.0" + } + }, + "mem-fs": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mem-fs/-/mem-fs-1.2.0.tgz", + "integrity": "sha512-b8g0jWKdl8pM0LqAPdK9i8ERL7nYrzmJfRhxMiWH2uYdfYnb7uXnmwVb0ZGe7xyEl4lj+nLIU3yf4zPUT+XsVQ==", + "dev": true, + "requires": { + "through2": "^3.0.0", + "vinyl": "^2.0.1", + "vinyl-file": "^3.0.0" + }, + "dependencies": { + "through2": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", + "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "dev": true, + "requires": { + "readable-stream": "2 || 3" + } + } + } + }, + "mem-fs-editor": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/mem-fs-editor/-/mem-fs-editor-6.0.0.tgz", + "integrity": "sha512-e0WfJAMm8Gv1mP5fEq/Blzy6Lt1VbLg7gNnZmZak7nhrBTibs+c6nQ4SKs/ZyJYHS1mFgDJeopsLAv7Ow0FMFg==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "deep-extend": "^0.6.0", + "ejs": "^2.6.1", + "glob": "^7.1.4", + "globby": "^9.2.0", + "isbinaryfile": "^4.0.0", + "mkdirp": "^0.5.0", + "multimatch": "^4.0.0", + "rimraf": "^2.6.3", + "through2": "^3.0.1", + "vinyl": "^2.2.0" + }, + "dependencies": { + "@nodelib/fs.stat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", + "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", + "dev": true + }, + "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" + }, + "dependencies": { + "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" + } + } + } + }, + "dir-glob": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz", + "integrity": "sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw==", + "dev": true, + "requires": { + "path-type": "^3.0.0" + } + }, + "fast-glob": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz", + "integrity": "sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==", + "dev": true, + "requires": { + "@mrmlnc/readdir-enhanced": "^2.2.1", + "@nodelib/fs.stat": "^1.1.2", + "glob-parent": "^3.1.0", + "is-glob": "^4.0.0", + "merge2": "^1.2.3", + "micromatch": "^3.1.10" + } + }, + "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" + }, + "dependencies": { + "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" + } + } + } + }, + "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" + } + } + } + }, + "globby": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-9.2.0.tgz", + "integrity": "sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "array-union": "^1.0.2", + "dir-glob": "^2.2.2", + "fast-glob": "^2.2.6", + "glob": "^7.1.3", + "ignore": "^4.0.3", + "pify": "^4.0.1", + "slash": "^2.0.0" + } + }, + "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" + } + } + } + }, + "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" + } + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + } + } + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + }, + "through2": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", + "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "dev": true, + "requires": { + "readable-stream": "2 || 3" + } + }, + "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" + } + } + } + }, + "memoize-one": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.1.1.tgz", + "integrity": "sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA==" + }, + "memory-fs": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", + "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "meow": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/meow/-/meow-7.0.1.tgz", + "integrity": "sha512-tBKIQqVrAHqwit0vfuFPY3LlzJYkEOFyKa3bPgxzNl6q/RtN8KQ+ALYEASYuFayzSAsjlhXj/JZ10rH85Q6TUw==", + "dev": true, + "requires": { + "@types/minimist": "^1.2.0", + "arrify": "^2.0.1", + "camelcase": "^6.0.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "^4.0.2", + "normalize-package-data": "^2.5.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.13.1", + "yargs-parser": "^18.1.3" + }, + "dependencies": { + "camelcase": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.0.0.tgz", + "integrity": "sha512-8KMDF1Vz2gzOq54ONPJS65IvTUaB1cHJ2DMM7MbPmLZljDH1qpzzLsWdiN9pHh6qvkRVDTi/07+eNGch/oLU4w==", + "dev": true + }, + "type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "dependencies": { + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + } + } + } + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" + } + } + }, + "mime": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", + "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==" + }, + "mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" + }, + "mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "requires": { + "mime-db": "1.44.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true + }, + "min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "dependencies": { + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + } + } + }, + "mississippi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", + "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", + "requires": { + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^3.0.0", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" + } + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "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==", + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + }, + "moment": { + "version": "2.26.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.26.0.tgz", + "integrity": "sha512-oIixUO+OamkUkwjhAVE18rAMfRJNsNe/Stid/gwHSOfHrOtw9EhAY2AHvdKZ/k/MggcYELFCJz/Sn2pL8b8JMw==" + }, + "move-concurrently": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", + "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", + "requires": { + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "multicast-dns": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz", + "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", + "requires": { + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" + } + }, + "multicast-dns-service-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", + "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=" + }, + "multimatch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-4.0.0.tgz", + "integrity": "sha512-lDmx79y1z6i7RNx0ZGCPq1bzJ6ZoDDKbvh7jxr9SJcWLkShMzXrHbYVpTdnhNM5MXpDUxCQ4DgqVttVXlBgiBQ==", + "dev": true, + "requires": { + "@types/minimatch": "^3.0.3", + "array-differ": "^3.0.0", + "array-union": "^2.1.0", + "arrify": "^2.0.1", + "minimatch": "^3.0.4" + }, + "dependencies": { + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + } + } + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "nan": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", + "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==", + "optional": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "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" + } + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "neo-async": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", + "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==" + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + }, + "no-case": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", + "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", + "requires": { + "lower-case": "^1.1.1" + } + }, + "node-fetch": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", + "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", + "requires": { + "encoding": "^0.1.11", + "is-stream": "^1.0.1" + } + }, + "node-forge": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.0.tgz", + "integrity": "sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ==" + }, + "node-libs-browser": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", + "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", + "requires": { + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^3.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", + "path-browserify": "0.0.1", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", + "tty-browserify": "0.0.0", + "url": "^0.11.0", + "util": "^0.11.0", + "vm-browserify": "^1.0.1" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + } + } + }, + "node-releases": { + "version": "1.1.58", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.58.tgz", + "integrity": "sha512-NxBudgVKiRh/2aPWMgPR7bPTX0VPmGx5QBwCtdHitnqFE5/O8DeBXuIMH1nwNnw/aMo6AjOrpsHzfY3UbUJ7yg==" + }, + "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==" + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=" + }, + "normalize-selector": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/normalize-selector/-/normalize-selector-0.2.0.tgz", + "integrity": "sha1-0LFF62kRicY6eNIB3E/bEpPvDAM=", + "dev": true + }, + "normalize-url": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-2.0.1.tgz", + "integrity": "sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw==", + "dev": true, + "requires": { + "prepend-http": "^2.0.0", + "query-string": "^5.0.1", + "sort-keys": "^2.0.0" + } + }, + "npm-api": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/npm-api/-/npm-api-1.0.0.tgz", + "integrity": "sha512-gtJhIhGq07g9H5sIAB9TZzTySW8MYtcYqg+e+J+5q1GmDsDLLVfyvVBL1VklzjtRsElph11GUtLBS191RDOJxQ==", + "dev": true, + "requires": { + "JSONStream": "^1.3.5", + "clone-deep": "^4.0.1", + "download-stats": "^0.3.4", + "moment": "^2.24.0", + "paged-request": "^2.0.1", + "request": "^2.88.0" + } + }, + "npm-conf": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz", + "integrity": "sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==", + "dev": true, + "requires": { + "config-chain": "^1.1.11", + "pify": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + } + } + }, + "npm-keyword": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/npm-keyword/-/npm-keyword-5.0.0.tgz", + "integrity": "sha1-mbha7Cn8s4jS3TUfABO/Umh4fmc=", + "dev": true, + "requires": { + "got": "^7.1.0", + "registry-url": "^3.0.3" + }, + "dependencies": { + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + }, + "got": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/got/-/got-7.1.0.tgz", + "integrity": "sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw==", + "dev": true, + "requires": { + "decompress-response": "^3.2.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-plain-obj": "^1.1.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "isurl": "^1.0.0-alpha5", + "lowercase-keys": "^1.0.0", + "p-cancelable": "^0.3.0", + "p-timeout": "^1.1.1", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "url-parse-lax": "^1.0.0", + "url-to-options": "^1.0.1" + } + }, + "p-cancelable": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz", + "integrity": "sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw==", + "dev": true + }, + "p-timeout": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz", + "integrity": "sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y=", + "dev": true, + "requires": { + "p-finally": "^1.0.0" + } + }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", + "dev": true + }, + "url-parse-lax": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", + "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", + "dev": true, + "requires": { + "prepend-http": "^1.0.1" + } + } + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "requires": { + "path-key": "^2.0.0" + } + }, + "npmlog": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-2.0.4.tgz", + "integrity": "sha1-mLUlMPJRTKkNCexbIsiEZyI3VpI=", + "dev": true, + "requires": { + "ansi": "~0.3.1", + "are-we-there-yet": "~1.1.2", + "gauge": "~1.2.5" + } + }, + "num2fraction": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", + "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=" + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "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=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-inspect": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==" + }, + "object-is": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.2.tgz", + "integrity": "sha512-5lHCz+0uufF6wZ7CRFWJN3hp8Jqblpgve06U5CMQ3f//6iDjPr2PEo9MWCjEssDsa+UZEL4PkFpr+BMop6aKzQ==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.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==" + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "requires": { + "isobject": "^3.0.0" + } + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "requires": { + "isobject": "^3.0.1" + } + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", + "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "open": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-6.4.0.tgz", + "integrity": "sha512-IFenVPgF70fSm1keSd2iDBIDIBZkroLeuffXq+wKTzTJlBpesFWojV9lb8mzOfaAzM1sr7HQHuO0vtV0zYekGg==", + "dev": true, + "requires": { + "is-wsl": "^1.1.0" + } + }, + "opn": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", + "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==", + "requires": { + "is-wsl": "^1.1.0" + } + }, + "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" + } + }, + "original": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", + "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", + "requires": { + "url-parse": "^1.4.3" + } + }, + "os-browserify": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", + "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=" + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true + }, + "os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, + "os-name": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-name/-/os-name-3.1.0.tgz", + "integrity": "sha512-h8L+8aNjNcMpo/mAIBPn5PXCM16iyPGjHNWo6U1YO8sJTMHtEtyczI6QJnLoplswm6goopQkqc7OAnjhWcugVg==", + "dev": true, + "requires": { + "macos-release": "^2.2.0", + "windows-release": "^3.1.0" + } + }, + "os-shim": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/os-shim/-/os-shim-0.1.3.tgz", + "integrity": "sha1-a2LDeRz3kJ6jXtRuF2WLtBfLORc=", + "dev": true + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "p-any": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-any/-/p-any-2.1.0.tgz", + "integrity": "sha512-JAERcaMBLYKMq+voYw36+x5Dgh47+/o7yuv2oQYuSSUml4YeqJEFznBrY2UeEkoSHqBua6hz518n/PsowTYLLg==", + "dev": true, + "requires": { + "p-cancelable": "^2.0.0", + "p-some": "^4.0.0", + "type-fest": "^0.3.0" + }, + "dependencies": { + "type-fest": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz", + "integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ==", + "dev": true + } + } + }, + "p-cancelable": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.0.0.tgz", + "integrity": "sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg==", + "dev": true + }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=" + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, + "p-is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", + "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==" + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-map": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", + "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==" + }, + "p-retry": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz", + "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==", + "requires": { + "retry": "^0.12.0" + } + }, + "p-some": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-some/-/p-some-4.1.0.tgz", + "integrity": "sha512-MF/HIbq6GeBqTrTIl5OJubzkGU+qfFhAFi0gnTAK6rgEIJIknEiABHOTtQu4e6JiXjIwuMPMUFQzyHh5QjCl1g==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0", + "p-cancelable": "^2.0.0" + } + }, + "p-timeout": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-2.0.1.tgz", + "integrity": "sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA==", + "dev": true, + "requires": { + "p-finally": "^1.0.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" + }, + "package-json": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-5.0.0.tgz", + "integrity": "sha512-EeHQFFTlEmLrkIQoxbE9w0FuAWHoc1XpthDqnZ/i9keOt701cteyXwAxQFLpVqVjj3feh2TodkihjLaRUtIgLg==", + "dev": true, + "requires": { + "got": "^8.3.1", + "registry-auth-token": "^3.3.2", + "registry-url": "^3.1.0", + "semver": "^5.5.0" + } + }, + "pad-component": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pad-component/-/pad-component-0.0.1.tgz", + "integrity": "sha1-rR8izhvw/cDW3dkIrxfzUaQEuKw=", + "dev": true + }, + "paged-request": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/paged-request/-/paged-request-2.0.1.tgz", + "integrity": "sha512-C0bB/PFk9rQskD1YEiz7uuchzqKDQGgdsEHN1ahify0UUWzgmMK4NDG9fhlQg2waogmNFwEvEeHfMRvJySpdVw==", + "dev": true, + "requires": { + "axios": "^0.18.0" + } + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, + "parallel-transform": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", + "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", + "requires": { + "cyclist": "^1.0.1", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" + } + }, + "param-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz", + "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", + "requires": { + "no-case": "^2.2.0" + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + }, + "dependencies": { + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + } + } + }, + "parse-asn1": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz", + "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==", + "requires": { + "asn1.js": "^4.0.0", + "browserify-aes": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parse-entities": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-1.2.2.tgz", + "integrity": "sha512-NzfpbxW/NPrzZ/yYSoQxyqUZMZXIdCfE0OIN4ESsnptHJECoUk3FZktxNuzQf4tjt5UEopnxpYJbvYuxIFDdsg==", + "requires": { + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" + } + }, + "parse-help": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-help/-/parse-help-1.0.0.tgz", + "integrity": "sha512-dlOrbBba6Rrw/nrJ+V7/vkGZdiimWJQzMHZZrYsUq03JE8AV3fAv6kOYX7dP/w2h67lIdmRf8ES8mU44xAgE/Q==", + "dev": true, + "requires": { + "execall": "^1.0.0" + }, + "dependencies": { + "clone-regexp": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/clone-regexp/-/clone-regexp-1.0.1.tgz", + "integrity": "sha512-Fcij9IwRW27XedRIJnSOEupS7RVcXtObJXbcUOX93UCLqqOdRpkvzKywOOSizmEK/Is3S/RHX9dLdfo6R1Q1mw==", + "dev": true, + "requires": { + "is-regexp": "^1.0.0", + "is-supported-regexp-flag": "^1.0.0" + } + }, + "execall": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execall/-/execall-1.0.0.tgz", + "integrity": "sha1-c9CQTjlbPKsGWLCNCewlMH8pu3M=", + "dev": true, + "requires": { + "clone-regexp": "^1.0.0" + } + }, + "is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=", + "dev": true + } + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=" + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "pascal-case": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-2.0.1.tgz", + "integrity": "sha1-LVeNNFX2YNpl7KGO+VtODekSdh4=", + "requires": { + "camel-case": "^3.0.0", + "upper-case-first": "^1.1.0" + } + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" + }, + "passwd-user": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/passwd-user/-/passwd-user-3.0.0.tgz", + "integrity": "sha512-Iu90rROks+uDK00ppSewoZyqeCwjGR6W8PcY0Phl8YFWju/lRmIogQb98+vSb5RUeYkONL3IC4ZLBFg4FiE0Hg==", + "dev": true, + "requires": { + "execa": "^1.0.0" + } + }, + "path-browserify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", + "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==" + }, + "path-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/path-case/-/path-case-2.1.1.tgz", + "integrity": "sha1-lLgDfDctP+KQbkZbtF4l0ibo7qU=", + "requires": { + "no-case": "^2.2.0" + } + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=" + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, + "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=" + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=" + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "pbkdf2": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz", + "integrity": "sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==", + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==" + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "requires": { + "pinkie": "^2.0.0" + } + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "requires": { + "find-up": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + } + } + }, + "pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz", + "integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=", + "requires": { + "find-up": "^2.1.0" + } + }, + "portfinder": { + "version": "1.0.26", + "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.26.tgz", + "integrity": "sha512-Xi7mKxJHHMI3rIUrnm/jjUgwhbYMkp/XKEcZX3aG4BrumLpq3nmoQMX+ClYnDZnZ/New7IatC1no5RX0zo1vXQ==", + "requires": { + "async": "^2.6.2", + "debug": "^3.1.1", + "mkdirp": "^0.5.1" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "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=" + }, + "postcss": { + "version": "7.0.32", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.32.tgz", + "integrity": "sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw==", + "requires": { + "chalk": "^2.4.2", + "source-map": "^0.6.1", + "supports-color": "^6.1.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "postcss-attribute-case-insensitive": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-4.0.2.tgz", + "integrity": "sha512-clkFxk/9pcdb4Vkn0hAHq3YnxBQ2p0CGD1dy24jN+reBck+EWxMbxSUqN4Yj7t0w8csl87K6p0gxBe1utkJsYA==", + "requires": { + "postcss": "^7.0.2", + "postcss-selector-parser": "^6.0.2" + } + }, + "postcss-calc": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.2.tgz", + "integrity": "sha512-rofZFHUg6ZIrvRwPeFktv06GdbDYLcGqh9EwiMutZg+a0oePCCw1zHOEiji6LCpyRcjTREtPASuUqeAvYlEVvQ==", + "requires": { + "postcss": "^7.0.27", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.0.2" + } + }, + "postcss-color-functional-notation": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-2.0.1.tgz", + "integrity": "sha512-ZBARCypjEDofW4P6IdPVTLhDNXPRn8T2s1zHbZidW6rPaaZvcnCS2soYFIQJrMZSxiePJ2XIYTlcb2ztr/eT2g==", + "requires": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "dependencies": { + "postcss-values-parser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz", + "integrity": "sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg==", + "requires": { + "flatten": "^1.0.2", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-color-gray": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-color-gray/-/postcss-color-gray-5.0.0.tgz", + "integrity": "sha512-q6BuRnAGKM/ZRpfDascZlIZPjvwsRye7UDNalqVz3s7GDxMtqPY6+Q871liNxsonUw8oC61OG+PSaysYpl1bnw==", + "requires": { + "@csstools/convert-colors": "^1.4.0", + "postcss": "^7.0.5", + "postcss-values-parser": "^2.0.0" + }, + "dependencies": { + "postcss-values-parser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz", + "integrity": "sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg==", + "requires": { + "flatten": "^1.0.2", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-color-hex-alpha": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-5.0.3.tgz", + "integrity": "sha512-PF4GDel8q3kkreVXKLAGNpHKilXsZ6xuu+mOQMHWHLPNyjiUBOr75sp5ZKJfmv1MCus5/DWUGcK9hm6qHEnXYw==", + "requires": { + "postcss": "^7.0.14", + "postcss-values-parser": "^2.0.1" + }, + "dependencies": { + "postcss-values-parser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz", + "integrity": "sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg==", + "requires": { + "flatten": "^1.0.2", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-color-mod-function": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/postcss-color-mod-function/-/postcss-color-mod-function-3.0.3.tgz", + "integrity": "sha512-YP4VG+xufxaVtzV6ZmhEtc+/aTXH3d0JLpnYfxqTvwZPbJhWqp8bSY3nfNzNRFLgB4XSaBA82OE4VjOOKpCdVQ==", + "requires": { + "@csstools/convert-colors": "^1.4.0", + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "dependencies": { + "postcss-values-parser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz", + "integrity": "sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg==", + "requires": { + "flatten": "^1.0.2", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-color-rebeccapurple": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-4.0.1.tgz", + "integrity": "sha512-aAe3OhkS6qJXBbqzvZth2Au4V3KieR5sRQ4ptb2b2O8wgvB3SJBsdG+jsn2BZbbwekDG8nTfcCNKcSfe/lEy8g==", + "requires": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "dependencies": { + "postcss-values-parser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz", + "integrity": "sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg==", + "requires": { + "flatten": "^1.0.2", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-custom-media": { + "version": "7.0.8", + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-7.0.8.tgz", + "integrity": "sha512-c9s5iX0Ge15o00HKbuRuTqNndsJUbaXdiNsksnVH8H4gdc+zbLzr/UasOwNG6CTDpLFekVY4672eWdiiWu2GUg==", + "requires": { + "postcss": "^7.0.14" + } + }, + "postcss-custom-properties": { + "version": "8.0.11", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-8.0.11.tgz", + "integrity": "sha512-nm+o0eLdYqdnJ5abAJeXp4CEU1c1k+eB2yMCvhgzsds/e0umabFrN6HoTy/8Q4K5ilxERdl/JD1LO5ANoYBeMA==", + "requires": { + "postcss": "^7.0.17", + "postcss-values-parser": "^2.0.1" + }, + "dependencies": { + "postcss-values-parser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz", + "integrity": "sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg==", + "requires": { + "flatten": "^1.0.2", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-custom-selectors": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-5.1.2.tgz", + "integrity": "sha512-DSGDhqinCqXqlS4R7KGxL1OSycd1lydugJ1ky4iRXPHdBRiozyMHrdu0H3o7qNOCiZwySZTUI5MV0T8QhCLu+w==", + "requires": { + "postcss": "^7.0.2", + "postcss-selector-parser": "^5.0.0-rc.3" + }, + "dependencies": { + "cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==" + }, + "postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "requires": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-dir-pseudo-class": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-5.0.0.tgz", + "integrity": "sha512-3pm4oq8HYWMZePJY+5ANriPs3P07q+LW6FAdTlkFH2XqDdP4HeeJYMOzn0HYLhRSjBO3fhiqSwwU9xEULSrPgw==", + "requires": { + "postcss": "^7.0.2", + "postcss-selector-parser": "^5.0.0-rc.3" + }, + "dependencies": { + "cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==" + }, + "postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "requires": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-double-position-gradients": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-1.0.0.tgz", + "integrity": "sha512-G+nV8EnQq25fOI8CH/B6krEohGWnF5+3A6H/+JEpOncu5dCnkS1QQ6+ct3Jkaepw1NGVqqOZH6lqrm244mCftA==", + "requires": { + "postcss": "^7.0.5", + "postcss-values-parser": "^2.0.0" + }, + "dependencies": { + "postcss-values-parser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz", + "integrity": "sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg==", + "requires": { + "flatten": "^1.0.2", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-env-function": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-2.0.2.tgz", + "integrity": "sha512-rwac4BuZlITeUbiBq60h/xbLzXY43qOsIErngWa4l7Mt+RaSkT7QBjXVGTcBHupykkblHMDrBFh30zchYPaOUw==", + "requires": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "dependencies": { + "postcss-values-parser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz", + "integrity": "sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg==", + "requires": { + "flatten": "^1.0.2", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-flexbugs-fixes": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-4.2.1.tgz", + "integrity": "sha512-9SiofaZ9CWpQWxOwRh1b/r85KD5y7GgvsNt1056k6OYLvWUun0czCvogfJgylC22uJTwW1KzY3Gz65NZRlvoiQ==", + "requires": { + "postcss": "^7.0.26" + } + }, + "postcss-focus-visible": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-4.0.0.tgz", + "integrity": "sha512-Z5CkWBw0+idJHSV6+Bgf2peDOFf/x4o+vX/pwcNYrWpXFrSfTkQ3JQ1ojrq9yS+upnAlNRHeg8uEwFTgorjI8g==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-focus-within": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-3.0.0.tgz", + "integrity": "sha512-W0APui8jQeBKbCGZudW37EeMCjDeVxKgiYfIIEo8Bdh5SpB9sxds/Iq8SEuzS0Q4YFOlG7EPFulbbxujpkrV2w==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-font-family-system-ui": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-font-family-system-ui/-/postcss-font-family-system-ui-4.2.0.tgz", + "integrity": "sha512-qTizXw0iwu/9K1+UPmu2BsC65VX2NiZI2hMreL/SkqJd+EwmCktE+juz/xoy7XH2WGfb9x0ES2yA4LuwUyV/Yg==", + "requires": { + "browserslist": "^4.8.2", + "caniuse-lite": "^1.0.30001016", + "postcss": "^7.0.0" + } + }, + "postcss-font-variant": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-4.0.0.tgz", + "integrity": "sha512-M8BFYKOvCrI2aITzDad7kWuXXTm0YhGdP9Q8HanmN4EF1Hmcgs1KK5rSHylt/lUJe8yLxiSwWAHdScoEiIxztg==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-gap-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-2.0.0.tgz", + "integrity": "sha512-QZSqDaMgXCHuHTEzMsS2KfVDOq7ZFiknSpkrPJY6jmxbugUPTuSzs/vuE5I3zv0WAS+3vhrlqhijiprnuQfzmg==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-html": { + "version": "0.36.0", + "resolved": "https://registry.npmjs.org/postcss-html/-/postcss-html-0.36.0.tgz", + "integrity": "sha512-HeiOxGcuwID0AFsNAL0ox3mW6MHH5cstWN1Z3Y+n6H+g12ih7LHdYxWwEA/QmrebctLjo79xz9ouK3MroHwOJw==", + "dev": true, + "requires": { + "htmlparser2": "^3.10.0" + }, + "dependencies": { + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==", + "dev": true + }, + "domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "dev": true, + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", + "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "dev": true, + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true + }, + "htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "dev": true, + "requires": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "postcss-image-set-function": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-3.0.1.tgz", + "integrity": "sha512-oPTcFFip5LZy8Y/whto91L9xdRHCWEMs3e1MdJxhgt4jy2WYXfhkng59fH5qLXSCPN8k4n94p1Czrfe5IOkKUw==", + "requires": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "dependencies": { + "postcss-values-parser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz", + "integrity": "sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg==", + "requires": { + "flatten": "^1.0.2", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-import": { + "version": "12.0.1", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-12.0.1.tgz", + "integrity": "sha512-3Gti33dmCjyKBgimqGxL3vcV8w9+bsHwO5UrBawp796+jdardbcFl4RP5w/76BwNL7aGzpKstIfF9I+kdE8pTw==", + "requires": { + "postcss": "^7.0.1", + "postcss-value-parser": "^3.2.3", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "dependencies": { + "postcss-value-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz", + "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==" + } + } + }, + "postcss-initial": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-3.0.2.tgz", + "integrity": "sha512-ugA2wKonC0xeNHgirR4D3VWHs2JcU08WAi1KFLVcnb7IN89phID6Qtg2RIctWbnvp1TM2BOmDtX8GGLCKdR8YA==", + "requires": { + "lodash.template": "^4.5.0", + "postcss": "^7.0.2" + } + }, + "postcss-lab-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-2.0.1.tgz", + "integrity": "sha512-whLy1IeZKY+3fYdqQFuDBf8Auw+qFuVnChWjmxm/UhHWqNHZx+B99EwxTvGYmUBqe3Fjxs4L1BoZTJmPu6usVg==", + "requires": { + "@csstools/convert-colors": "^1.4.0", + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "dependencies": { + "postcss-values-parser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz", + "integrity": "sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg==", + "requires": { + "flatten": "^1.0.2", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-less": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/postcss-less/-/postcss-less-3.1.4.tgz", + "integrity": "sha512-7TvleQWNM2QLcHqvudt3VYjULVB49uiW6XzEUFmvwHzvsOEF5MwBrIXZDJQvJNFGjJQTzSzZnDoCJ8h/ljyGXA==", + "dev": true, + "requires": { + "postcss": "^7.0.14" + } + }, + "postcss-load-config": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.0.tgz", + "integrity": "sha512-4pV3JJVPLd5+RueiVVB+gFOAa7GWc25XQcMp86Zexzke69mKf6Nx9LRcQywdz7yZI9n1udOxmLuAwTBypypF8Q==", + "requires": { + "cosmiconfig": "^5.0.0", + "import-cwd": "^2.0.0" + } + }, + "postcss-loader": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-3.0.0.tgz", + "integrity": "sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==", + "requires": { + "loader-utils": "^1.1.0", + "postcss": "^7.0.0", + "postcss-load-config": "^2.0.0", + "schema-utils": "^1.0.0" + }, + "dependencies": { + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + } + } + }, + "postcss-logical": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-3.0.0.tgz", + "integrity": "sha512-1SUKdJc2vuMOmeItqGuNaC+N8MzBWFWEkAnRnLpFYj1tGGa7NqyVBujfRtgNa2gXR+6RkGUiB2O5Vmh7E2RmiA==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-media-minmax": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-4.0.0.tgz", + "integrity": "sha512-fo9moya6qyxsjbFAYl97qKO9gyre3qvbMnkOZeZwlsW6XYFsvs2DMGDlchVLfAd8LHPZDxivu/+qW2SMQeTHBw==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-media-query-parser": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", + "integrity": "sha1-J7Ocb02U+Bsac7j3Y1HGCeXO8kQ=", + "dev": true + }, + "postcss-modules-extract-imports": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz", + "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==", + "requires": { + "postcss": "^7.0.5" + } + }, + "postcss-modules-local-by-default": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.2.tgz", + "integrity": "sha512-jM/V8eqM4oJ/22j0gx4jrp63GSvDH6v86OqyTHHUvk4/k1vceipZsaymiZ5PvocqZOl5SFHiFJqjs3la0wnfIQ==", + "requires": { + "icss-utils": "^4.1.1", + "postcss": "^7.0.16", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.0.0" + } + }, + "postcss-modules-scope": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz", + "integrity": "sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ==", + "requires": { + "postcss": "^7.0.6", + "postcss-selector-parser": "^6.0.0" + } + }, + "postcss-modules-values": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz", + "integrity": "sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg==", + "requires": { + "icss-utils": "^4.0.0", + "postcss": "^7.0.6" + } + }, + "postcss-modules-values-replace": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values-replace/-/postcss-modules-values-replace-3.1.0.tgz", + "integrity": "sha512-9UPidpGUjJPZEangWQF09JNKm2A3LeWytV2KuSY9OgUXZm6sQw5W6lIIG4XoGji43CD0gZf17nR5br5WMUCMyQ==", + "requires": { + "enhanced-resolve": "^3.1.0", + "es6-promisify": "^5.0.0", + "postcss": "^6.0.1", + "postcss-values-parser": "^1.3.1" + }, + "dependencies": { + "postcss": { + "version": "6.0.23", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz", + "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", + "requires": { + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "postcss-nesting": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-7.0.1.tgz", + "integrity": "sha512-FrorPb0H3nuVq0Sff7W2rnc3SmIcruVC6YwpcS+k687VxyxO33iE1amna7wHuRVzM8vfiYofXSBHNAZ3QhLvYg==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-overflow-shorthand": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-2.0.0.tgz", + "integrity": "sha512-aK0fHc9CBNx8jbzMYhshZcEv8LtYnBIRYQD5i7w/K/wS9c2+0NSR6B3OVMu5y0hBHYLcMGjfU+dmWYNKH0I85g==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-page-break": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-2.0.0.tgz", + "integrity": "sha512-tkpTSrLpfLfD9HvgOlJuigLuk39wVTbbd8RKcy8/ugV2bNBUW3xU+AIqyxhDrQr1VUj1RmyJrBn1YWrqUm9zAQ==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-place": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-4.0.1.tgz", + "integrity": "sha512-Zb6byCSLkgRKLODj/5mQugyuj9bvAAw9LqJJjgwz5cYryGeXfFZfSXoP1UfveccFmeq0b/2xxwcTEVScnqGxBg==", + "requires": { + "postcss": "^7.0.2", + "postcss-values-parser": "^2.0.0" + }, + "dependencies": { + "postcss-values-parser": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-2.0.1.tgz", + "integrity": "sha512-2tLuBsA6P4rYTNKCXYG/71C7j1pU6pK503suYOmn4xYrQIzW+opD+7FAFNuGSdZC/3Qfy334QbeMu7MEb8gOxg==", + "requires": { + "flatten": "^1.0.2", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-preset-env": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-6.7.0.tgz", + "integrity": "sha512-eU4/K5xzSFwUFJ8hTdTQzo2RBLbDVt83QZrAvI07TULOkmyQlnYlpwep+2yIK+K+0KlZO4BvFcleOCCcUtwchg==", + "requires": { + "autoprefixer": "^9.6.1", + "browserslist": "^4.6.4", + "caniuse-lite": "^1.0.30000981", + "css-blank-pseudo": "^0.1.4", + "css-has-pseudo": "^0.10.0", + "css-prefers-color-scheme": "^3.1.1", + "cssdb": "^4.4.0", + "postcss": "^7.0.17", + "postcss-attribute-case-insensitive": "^4.0.1", + "postcss-color-functional-notation": "^2.0.1", + "postcss-color-gray": "^5.0.0", + "postcss-color-hex-alpha": "^5.0.3", + "postcss-color-mod-function": "^3.0.3", + "postcss-color-rebeccapurple": "^4.0.1", + "postcss-custom-media": "^7.0.8", + "postcss-custom-properties": "^8.0.11", + "postcss-custom-selectors": "^5.1.2", + "postcss-dir-pseudo-class": "^5.0.0", + "postcss-double-position-gradients": "^1.0.0", + "postcss-env-function": "^2.0.2", + "postcss-focus-visible": "^4.0.0", + "postcss-focus-within": "^3.0.0", + "postcss-font-variant": "^4.0.0", + "postcss-gap-properties": "^2.0.0", + "postcss-image-set-function": "^3.0.1", + "postcss-initial": "^3.0.0", + "postcss-lab-function": "^2.0.1", + "postcss-logical": "^3.0.0", + "postcss-media-minmax": "^4.0.0", + "postcss-nesting": "^7.0.0", + "postcss-overflow-shorthand": "^2.0.0", + "postcss-page-break": "^2.0.0", + "postcss-place": "^4.0.1", + "postcss-pseudo-class-any-link": "^6.0.0", + "postcss-replace-overflow-wrap": "^3.0.0", + "postcss-selector-matches": "^4.0.0", + "postcss-selector-not": "^4.0.0" + } + }, + "postcss-pseudo-class-any-link": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-6.0.0.tgz", + "integrity": "sha512-lgXW9sYJdLqtmw23otOzrtbDXofUdfYzNm4PIpNE322/swES3VU9XlXHeJS46zT2onFO7V1QFdD4Q9LiZj8mew==", + "requires": { + "postcss": "^7.0.2", + "postcss-selector-parser": "^5.0.0-rc.3" + }, + "dependencies": { + "cssesc": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-2.0.0.tgz", + "integrity": "sha512-MsCAG1z9lPdoO/IUMLSBWBSVxVtJ1395VGIQ+Fc2gNdkQ1hNDnQdw3YhA71WJCBW1vdwA0cAnk/DnW6bqoEUYg==" + }, + "postcss-selector-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-5.0.0.tgz", + "integrity": "sha512-w+zLE5Jhg6Liz8+rQOWEAwtwkyqpfnmsinXjXg6cY7YIONZZtgvE0v2O0uhQBs0peNomOJwWRKt6JBfTdTd3OQ==", + "requires": { + "cssesc": "^2.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + } + } + }, + "postcss-replace-overflow-wrap": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-3.0.0.tgz", + "integrity": "sha512-2T5hcEHArDT6X9+9dVSPQdo7QHzG4XKclFT8rU5TzJPDN7RIRTbO9c4drUISOVemLj03aezStHCR2AIcr8XLpw==", + "requires": { + "postcss": "^7.0.2" + } + }, + "postcss-reporter": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-reporter/-/postcss-reporter-6.0.1.tgz", + "integrity": "sha512-LpmQjfRWyabc+fRygxZjpRxfhRf9u/fdlKf4VHG4TSPbV2XNsuISzYW1KL+1aQzx53CAppa1bKG4APIB/DOXXw==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "lodash": "^4.17.11", + "log-symbols": "^2.2.0", + "postcss": "^7.0.7" + }, + "dependencies": { + "log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "dev": true, + "requires": { + "chalk": "^2.0.1" + } + } + } + }, + "postcss-resolve-nested-selector": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz", + "integrity": "sha1-Kcy8fDfe36wwTp//C/FZaz9qDk4=", + "dev": true + }, + "postcss-safe-parser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-4.0.2.tgz", + "integrity": "sha512-Uw6ekxSWNLCPesSv/cmqf2bY/77z11O7jZGPax3ycZMFU/oi2DMH9i89AdHc1tRwFg/arFoEwX0IS3LCUxJh1g==", + "dev": true, + "requires": { + "postcss": "^7.0.26" + } + }, + "postcss-sass": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/postcss-sass/-/postcss-sass-0.4.4.tgz", + "integrity": "sha512-BYxnVYx4mQooOhr+zer0qWbSPYnarAy8ZT7hAQtbxtgVf8gy+LSLT/hHGe35h14/pZDTw1DsxdbrwxBN++H+fg==", + "dev": true, + "requires": { + "gonzales-pe": "^4.3.0", + "postcss": "^7.0.21" + } + }, + "postcss-scss": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-2.1.1.tgz", + "integrity": "sha512-jQmGnj0hSGLd9RscFw9LyuSVAa5Bl1/KBPqG1NQw9w8ND55nY4ZEsdlVuYJvLPpV+y0nwTV5v/4rHPzZRihQbA==", + "dev": true, + "requires": { + "postcss": "^7.0.6" + } + }, + "postcss-selector-matches": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-matches/-/postcss-selector-matches-4.0.0.tgz", + "integrity": "sha512-LgsHwQR/EsRYSqlwdGzeaPKVT0Ml7LAT6E75T8W8xLJY62CE4S/l03BWIt3jT8Taq22kXP08s2SfTSzaraoPww==", + "requires": { + "balanced-match": "^1.0.0", + "postcss": "^7.0.2" + } + }, + "postcss-selector-not": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-4.0.0.tgz", + "integrity": "sha512-W+bkBZRhqJaYN8XAnbbZPLWMvZD1wKTu0UxtFKdhtGjWYmxhkUneoeOhRJKdAE5V7ZTlnbHfCR+6bNwK9e1dTQ==", + "requires": { + "balanced-match": "^1.0.0", + "postcss": "^7.0.2" + } + }, + "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==", + "requires": { + "cssesc": "^3.0.0", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "postcss-sorting": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/postcss-sorting/-/postcss-sorting-5.0.1.tgz", + "integrity": "sha512-Y9fUFkIhfrm6i0Ta3n+89j56EFqaNRdUKqXyRp6kvTcSXnmgEjaVowCXH+JBe9+YKWqd4nc28r2sgwnzJalccA==", + "dev": true, + "requires": { + "lodash": "^4.17.14", + "postcss": "^7.0.17" + } + }, + "postcss-syntax": { + "version": "0.36.2", + "resolved": "https://registry.npmjs.org/postcss-syntax/-/postcss-syntax-0.36.2.tgz", + "integrity": "sha512-nBRg/i7E3SOHWxF3PpF5WnJM/jQ1YpY9000OaVXlAQj6Zp/kIqJxEDWIZ67tAd7NLuk7zqN4yqe9nc0oNAOs1w==", + "dev": true + }, + "postcss-value-parser": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz", + "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==" + }, + "postcss-values-parser": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/postcss-values-parser/-/postcss-values-parser-1.5.0.tgz", + "integrity": "sha512-3M3p+2gMp0AH3da530TlX8kiO1nxdTnc3C6vr8dMxRLIlh8UYkz0/wcwptSXjhtx2Fr0TySI7a+BHDQ8NL7LaQ==", + "requires": { + "flatten": "^1.0.2", + "indexes-of": "^1.0.1", + "uniq": "^1.0.1" + } + }, + "prefixfree": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prefixfree/-/prefixfree-1.0.0.tgz", + "integrity": "sha1-grDtu6wQfyo+LcVp1sPfQDXNeRA=" + }, + "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 + }, + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", + "dev": true + }, + "pretty-bytes": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.3.0.tgz", + "integrity": "sha512-hjGrh+P926p4R4WbaB6OckyRtO0F0/lQBiT+0gnxjV+5kjPBrfVBFCsCLbMqVQeydvIoouYTCmmEURiH3R1Bdg==", + "dev": true + }, + "private": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==" + }, + "process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" + }, + "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==" + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "requires": { + "asap": "~2.0.3" + } + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=" + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=", + "dev": true + }, + "proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "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 + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + }, + "dependencies": { + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" + } + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "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==", + "requires": { + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" + }, + "dependencies": { + "pump": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", + "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + } + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + }, + "query-string": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", + "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", + "dev": true, + "requires": { + "decode-uri-component": "^0.2.0", + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + } + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + }, + "querystring-es3": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", + "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=" + }, + "querystringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz", + "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==" + }, + "quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true + }, + "ramda": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.0.tgz", + "integrity": "sha512-pVzZdDpWwWqEVVLshWUHjNwuVP7SfcmPraYuqocJp1yo2U1R7P+5QAfDhdItkuoGqIBnBYrtPp7rEPqDn9HlZA==" + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "dependencies": { + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + } + } + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + } + } + }, + "react": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react/-/react-16.13.1.tgz", + "integrity": "sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2" + } + }, + "react-dom": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.13.1.tgz", + "integrity": "sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "scheduler": "^0.19.1" + } + }, + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "react-markdown": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-4.3.1.tgz", + "integrity": "sha512-HQlWFTbDxTtNY6bjgp3C3uv1h2xcjCSi1zAEzfBW9OwJJvENSYiLXWNXN5hHLsoqai7RnZiiHzcnWdXk2Splzw==", + "requires": { + "html-to-react": "^1.3.4", + "mdast-add-list-metadata": "1.0.1", + "prop-types": "^15.7.2", + "react-is": "^16.8.6", + "remark-parse": "^5.0.0", + "unified": "^6.1.5", + "unist-util-visit": "^1.3.0", + "xtend": "^4.0.1" + } + }, + "react-measure": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/react-measure/-/react-measure-2.3.0.tgz", + "integrity": "sha512-dwAvmiOeblj5Dvpnk8Jm7Q8B4THF/f1l1HtKVi0XDecsG6LXwGvzV5R1H32kq3TW6RW64OAf5aoQxpIgLa4z8A==", + "requires": { + "@babel/runtime": "^7.2.0", + "get-node-dimensions": "^1.2.1", + "prop-types": "^15.6.2", + "resize-observer-polyfill": "^1.5.0" + } + }, + "react-sortable-hoc": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/react-sortable-hoc/-/react-sortable-hoc-1.11.0.tgz", + "integrity": "sha512-v1CDCvdfoR3zLGNp6qsBa4J1BWMEVH25+UKxF/RvQRh+mrB+emqtVHMgZ+WreUiKJoEaiwYoScaueIKhMVBHUg==", + "requires": { + "@babel/runtime": "^7.2.0", + "invariant": "^2.2.4", + "prop-types": "^15.5.7" + } + }, + "react-virtualized": { + "version": "9.21.2", + "resolved": "https://registry.npmjs.org/react-virtualized/-/react-virtualized-9.21.2.tgz", + "integrity": "sha512-oX7I7KYiUM7lVXQzmhtF4Xg/4UA5duSA+/ZcAvdWlTLFCoFYq1SbauJT5gZK9cZS/wdYR6TPGpX/dqzvTqQeBA==", + "requires": { + "babel-runtime": "^6.26.0", + "clsx": "^1.0.1", + "dom-helpers": "^5.0.0", + "loose-envify": "^1.3.0", + "prop-types": "^15.6.0", + "react-lifecycles-compat": "^3.0.4" + } + }, + "react-waypoint": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/react-waypoint/-/react-waypoint-9.0.2.tgz", + "integrity": "sha512-6tIr9NozeDH789Ox2tOkyDcmprYOx1+eII40dERLrZclFe6RhWAQ/bbd6B7cGild6onXNwPzg16y0/wHWQ/q+g==", + "requires": { + "consolidated-events": "^1.1.0 || ^2.0.0", + "prop-types": "^15.0.0", + "react-is": "^16.6.3" + } + }, + "read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=", + "requires": { + "pify": "^2.3.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + } + } + }, + "read-chunk": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/read-chunk/-/read-chunk-3.2.0.tgz", + "integrity": "sha512-CEjy9LCzhmD7nUpJ1oVOE6s/hBkejlcJEgLQHVnQznOSilOPb+kpKktlLfFDK3/WP43+F80xkUTM2VOkYoSYvQ==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "with-open-file": "^0.1.6" + } + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "parse-json": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz", + "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1", + "lines-and-columns": "^1.1.6" + } + }, + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + } + } + }, + "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==", + "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" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + } + } + }, + "readdirp": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", + "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", + "requires": { + "picomatch": "^2.2.1" + } + }, + "recast": { + "version": "0.11.23", + "resolved": "https://registry.npmjs.org/recast/-/recast-0.11.23.tgz", + "integrity": "sha1-RR/TAEqx5N+bTktmN2sqIZEkYtM=", + "requires": { + "ast-types": "0.9.6", + "esprima": "~3.1.0", + "private": "~0.1.5", + "source-map": "~0.5.0" + } + }, + "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" + } + }, + "recompose": { + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/recompose/-/recompose-0.30.0.tgz", + "integrity": "sha512-ZTrzzUDa9AqUIhRk4KmVFihH0rapdCSMFXjhHbNrjAWxBuUD/guYlyysMnuHjlZC/KRiOKRtB4jf96yYSkKE8w==", + "requires": { + "@babel/runtime": "^7.0.0", + "change-emitter": "^0.1.2", + "fbjs": "^0.8.1", + "hoist-non-react-statics": "^2.3.1", + "react-lifecycles-compat": "^3.0.2", + "symbol-observable": "^1.0.4" + } + }, + "redbox-react": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/redbox-react/-/redbox-react-1.6.0.tgz", + "integrity": "sha512-mLjM5eYR41yOp5YKHpd3syFeGq6B4Wj5vZr64nbLvTZW5ZLff4LYk7VE4ITpVxkZpCY6OZuqh0HiP3A3uEaCpg==", + "requires": { + "error-stack-parser": "^1.3.6", + "object-assign": "^4.0.1", + "prop-types": "^15.5.4", + "sourcemapped-stacktrace": "^1.1.6" + } + }, + "redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "requires": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + } + }, + "regenerate": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.1.tgz", + "integrity": "sha512-j2+C8+NtXQgEKWk49MMP5P/u2GhnahTtVkRIHr5R5lVRlbKvmQ+oS+A5aLKWp2ma5VkT8sh6v+v4hbH0YHR66A==" + }, + "regenerate-unicode-properties": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz", + "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==", + "requires": { + "regenerate": "^1.4.0" + } + }, + "regenerator-runtime": { + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", + "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==" + }, + "regenerator-transform": { + "version": "0.14.4", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.4.tgz", + "integrity": "sha512-EaJaKPBI9GvKpvUz2mz4fhx7WPgvwRLY9v3hlNHWmAuJHI13T4nwKnNvm5RWJzEdnI5g5UwtOww+S8IdoUC2bw==", + "requires": { + "@babel/runtime": "^7.8.4", + "private": "^0.1.8" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "regexp.prototype.flags": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", + "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + } + }, + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "dev": true + }, + "regexpu-core": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.0.tgz", + "integrity": "sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ==", + "requires": { + "regenerate": "^1.4.0", + "regenerate-unicode-properties": "^8.2.0", + "regjsgen": "^0.5.1", + "regjsparser": "^0.6.4", + "unicode-match-property-ecmascript": "^1.0.4", + "unicode-match-property-value-ecmascript": "^1.2.0" + } + }, + "registry-auth-token": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz", + "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==", + "dev": true, + "requires": { + "rc": "^1.1.6", + "safe-buffer": "^5.0.1" + } + }, + "registry-url": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", + "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", + "dev": true, + "requires": { + "rc": "^1.0.1" + } + }, + "regjsgen": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", + "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==" + }, + "regjsparser": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.4.tgz", + "integrity": "sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw==", + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=" + } + } + }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=" + }, + "remark": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/remark/-/remark-12.0.0.tgz", + "integrity": "sha512-oX4lMIS0csgk8AEbzY0h2jdR0ngiCHOpwwpxjmRa5TqAkeknY+tkhjRJGZqnCmvyuWh55/0SW5WY3R3nn3PH9A==", + "dev": true, + "requires": { + "remark-parse": "^8.0.0", + "remark-stringify": "^8.0.0", + "unified": "^9.0.0" + }, + "dependencies": { + "is-buffer": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", + "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==", + "dev": true + }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true + }, + "parse-entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", + "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", + "dev": true, + "requires": { + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" + } + }, + "remark-parse": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-8.0.2.tgz", + "integrity": "sha512-eMI6kMRjsAGpMXXBAywJwiwAse+KNpmt+BK55Oofy4KvBZEqUDj6mWbGLJZrujoPIPPxDXzn3T9baRlpsm2jnQ==", + "dev": true, + "requires": { + "ccount": "^1.0.0", + "collapse-white-space": "^1.0.2", + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-whitespace-character": "^1.0.0", + "is-word-character": "^1.0.0", + "markdown-escapes": "^1.0.0", + "parse-entities": "^2.0.0", + "repeat-string": "^1.5.4", + "state-toggle": "^1.0.0", + "trim": "0.0.1", + "trim-trailing-lines": "^1.0.0", + "unherit": "^1.0.4", + "unist-util-remove-position": "^2.0.0", + "vfile-location": "^3.0.0", + "xtend": "^4.0.1" + } + }, + "unified": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/unified/-/unified-9.0.0.tgz", + "integrity": "sha512-ssFo33gljU3PdlWLjNp15Inqb77d6JnJSfyplGJPT/a+fNRNyCBeveBAYJdO5khKdF6WVHa/yYCC7Xl6BDwZUQ==", + "dev": true, + "requires": { + "bail": "^1.0.0", + "extend": "^3.0.0", + "is-buffer": "^2.0.0", + "is-plain-obj": "^2.0.0", + "trough": "^1.0.0", + "vfile": "^4.0.0" + } + }, + "unist-util-is": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.0.2.tgz", + "integrity": "sha512-Ofx8uf6haexJwI1gxWMGg6I/dLnF2yE+KibhD3/diOqY2TinLcqHXCV6OI5gFVn3xQqDH+u0M625pfKwIwgBKQ==", + "dev": true + }, + "unist-util-remove-position": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-2.0.1.tgz", + "integrity": "sha512-fDZsLYIe2uT+oGFnuZmy73K6ZxOPG/Qcm+w7jbEjaFcJgbQ6cqjs/eSPzXhsmGpAsWPkqZM9pYjww5QTn3LHMA==", + "dev": true, + "requires": { + "unist-util-visit": "^2.0.0" + } + }, + "unist-util-stringify-position": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-2.0.3.tgz", + "integrity": "sha512-3faScn5I+hy9VleOq/qNbAd6pAx7iH5jYBMS9I1HgQVijz/4mv5Bvw5iw1sC/90CODiKo81G/ps8AJrISn687g==", + "dev": true, + "requires": { + "@types/unist": "^2.0.2" + } + }, + "unist-util-visit": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-2.0.2.tgz", + "integrity": "sha512-HoHNhGnKj6y+Sq+7ASo2zpVdfdRifhTgX2KTU3B/sO/TTlZchp7E3S4vjRzDJ7L60KmrCPsQkVK3lEF3cz36XQ==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0", + "unist-util-visit-parents": "^3.0.0" + } + }, + "unist-util-visit-parents": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-3.0.2.tgz", + "integrity": "sha512-yJEfuZtzFpQmg1OSCyS9M5NJRrln/9FbYosH3iW0MG402QbdbaB8ZESwUv9RO6nRfLAKvWcMxCwdLWOov36x/g==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "unist-util-is": "^4.0.0" + } + }, + "vfile": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-4.1.1.tgz", + "integrity": "sha512-lRjkpyDGjVlBA7cDQhQ+gNcvB1BGaTHYuSOcY3S7OhDmBtnzX95FhtZZDecSTDm6aajFymyve6S5DN4ZHGezdQ==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "is-buffer": "^2.0.0", + "replace-ext": "1.0.0", + "unist-util-stringify-position": "^2.0.0", + "vfile-message": "^2.0.0" + } + }, + "vfile-location": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-3.0.1.tgz", + "integrity": "sha512-yYBO06eeN/Ki6Kh1QAkgzYpWT1d3Qln+ZCtSbJqFExPl1S3y2qqotJQXoh6qEvl/jDlgpUJolBn3PItVnnZRqQ==", + "dev": true + }, + "vfile-message": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-2.0.4.tgz", + "integrity": "sha512-DjssxRGkMvifUOJre00juHoP9DPWuzjxKuMDrhNbk2TdaYYBNMStsNhEOt3idrtI12VQYM/1+iM0KOzXi4pxwQ==", + "dev": true, + "requires": { + "@types/unist": "^2.0.0", + "unist-util-stringify-position": "^2.0.0" + } + } + } + }, + "remark-breaks": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/remark-breaks/-/remark-breaks-1.0.5.tgz", + "integrity": "sha512-lr8+TlJI273NjEqL27eUthPYPTCgXEj4NaLbnazS3bQaQL2FySlsbtgo52gE36fE1gWeQgkn1VdmWsoT+uA7FA==" + }, + "remark-parse": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-5.0.0.tgz", + "integrity": "sha512-b3iXszZLH1TLoyUzrATcTQUZrwNl1rE70rVdSruJFlDaJ9z5aMkhrG43Pp68OgfHndL/ADz6V69Zow8cTQu+JA==", + "requires": { + "collapse-white-space": "^1.0.2", + "is-alphabetical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-whitespace-character": "^1.0.0", + "is-word-character": "^1.0.0", + "markdown-escapes": "^1.0.0", + "parse-entities": "^1.1.0", + "repeat-string": "^1.5.4", + "state-toggle": "^1.0.0", + "trim": "0.0.1", + "trim-trailing-lines": "^1.0.0", + "unherit": "^1.0.4", + "unist-util-remove-position": "^1.0.0", + "vfile-location": "^2.0.0", + "xtend": "^4.0.1" + } + }, + "remark-stringify": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-8.1.0.tgz", + "integrity": "sha512-FSPZv1ds76oAZjurhhuV5qXSUSoz6QRPuwYK38S41sLHwg4oB7ejnmZshj7qwjgYLf93kdz6BOX9j5aidNE7rA==", + "dev": true, + "requires": { + "ccount": "^1.0.0", + "is-alphanumeric": "^1.0.0", + "is-decimal": "^1.0.0", + "is-whitespace-character": "^1.0.0", + "longest-streak": "^2.0.1", + "markdown-escapes": "^1.0.0", + "markdown-table": "^2.0.0", + "mdast-util-compact": "^2.0.0", + "parse-entities": "^2.0.0", + "repeat-string": "^1.5.4", + "state-toggle": "^1.0.0", + "stringify-entities": "^3.0.0", + "unherit": "^1.0.4", + "xtend": "^4.0.1" + }, + "dependencies": { + "parse-entities": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz", + "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", + "dev": true, + "requires": { + "character-entities": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "character-reference-invalid": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.0", + "is-hexadecimal": "^1.0.0" + } + } + } + }, + "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=" + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==" + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "requires": { + "is-finite": "^1.0.0" + } + }, + "replace-ext": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz", + "integrity": "sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=" + }, + "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" + }, + "dependencies": { + "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 + }, + "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" + } + } + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + }, + "resize-observer-polyfill": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", + "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==" + }, + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "requires": { + "resolve-from": "^3.0.0" + } + }, + "resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "requires": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + }, + "dependencies": { + "global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "requires": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + } + } + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=" + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" + }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "dev": true, + "requires": { + "lowercase-keys": "^1.0.0" + } + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" + }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=" + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "roarr": { + "version": "2.15.3", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.3.tgz", + "integrity": "sha512-AEjYvmAhlyxOeB9OqPUzQCo3kuAkNfuDk/HqWbZdFsqDFpapkTjiw+p4svNEoRLvuqNTxqfL+s+gtD4eDgZ+CA==", + "dev": true, + "requires": { + "boolean": "^3.0.0", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + }, + "dependencies": { + "sprintf-js": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", + "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==", + "dev": true + } + } + }, + "root-check": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/root-check/-/root-check-1.0.0.tgz", + "integrity": "sha1-xSp5S/Dbn61WdTbkGJjwyeCoZpc=", + "dev": true, + "requires": { + "downgrade-root": "^1.0.0", + "sudo-block": "^1.1.0" + } + }, + "run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true + }, + "run-parallel": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", + "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", + "dev": true + }, + "run-queue": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", + "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", + "requires": { + "aproba": "^1.1.1" + } + }, + "rx": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/rx/-/rx-4.1.0.tgz", + "integrity": "sha1-pfE/957zt0D+MKqAP7CfmIBdR4I=", + "dev": true + }, + "rxjs": { + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.5.tgz", + "integrity": "sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "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==" + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "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==" + }, + "sass": { + "version": "1.26.8", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.26.8.tgz", + "integrity": "sha512-yvtzyrKLGiXQu7H12ekXqsfoGT/aTKeMDyVzCB675k1HYuaj0py63i8Uf4SI9CHXj6apDhpfwbUr3gGOjdpu2Q==", + "requires": { + "chokidar": ">=2.0.0 <4.0.0" + } + }, + "sass-loader": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-8.0.2.tgz", + "integrity": "sha512-7o4dbSK8/Ol2KflEmSco4jTjQoV988bM82P9CZdmo9hR3RLnvNc0ufMNdMrB0caq38JQ/FgF4/7RcbcfKzxoFQ==", + "requires": { + "clone-deep": "^4.0.1", + "loader-utils": "^1.2.3", + "neo-async": "^2.6.1", + "schema-utils": "^2.6.1", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "scheduler": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", + "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "schema-utils": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", + "integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", + "requires": { + "@types/json-schema": "^7.0.4", + "ajv": "^6.12.2", + "ajv-keywords": "^3.4.1" + } + }, + "scoped-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/scoped-regex/-/scoped-regex-1.0.0.tgz", + "integrity": "sha1-o0a7Gs1CB65wvXwMfKnlZra63bg=", + "dev": true + }, + "scrollbar-width": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/scrollbar-width/-/scrollbar-width-3.1.1.tgz", + "integrity": "sha1-xi5j76WTTaw3tD2jT3VQysqERKI=" + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=" + }, + "selfsigned": { + "version": "1.10.7", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.7.tgz", + "integrity": "sha512-8M3wBCzeWIJnQfl43IKwOmC4H/RAp50S8DF60znzjW5GVqTcSe2vWclt7hmYVPkKPlHWOu5EaWOMZ2Y6W8ZXTA==", + "requires": { + "node-forge": "0.9.0" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", + "dev": true + }, + "semver-diff": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", + "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", + "dev": true, + "requires": { + "semver": "^5.0.3" + } + }, + "semver-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-1.0.0.tgz", + "integrity": "sha1-kqSWkGX5xwxpR1PVUkj8aPj2Usk=", + "dev": true + }, + "semver-truncate": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/semver-truncate/-/semver-truncate-1.1.2.tgz", + "integrity": "sha1-V/Qd5pcHpicJp+AQS6IRcQnqR+g=", + "dev": true, + "requires": { + "semver": "^5.3.0" + } + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "sentence-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-2.1.1.tgz", + "integrity": "sha1-H24t2jnBaL+S0T+G1KkYkz9mftQ=", + "requires": { + "no-case": "^2.2.0", + "upper-case-first": "^1.1.2" + } + }, + "serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "dev": true, + "requires": { + "type-fest": "^0.13.1" + }, + "dependencies": { + "type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true + } + } + }, + "serialize-javascript": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-3.1.0.tgz", + "integrity": "sha512-JIJT1DGiWmIKhzRsG91aS6Ze4sFUrYbltlkg2onR5OrnNM02Kl/hnY/T4FN2omvyeBbQmMJv+K4cPOpGzOTFBg==", + "requires": { + "randombytes": "^2.1.0" + } + }, + "serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", + "requires": { + "accepts": "~1.3.4", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.0", + "statuses": ">= 1.4.0 < 2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "setprototypeof": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "set-getter": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/set-getter/-/set-getter-0.1.0.tgz", + "integrity": "sha1-12nBgsnVpR9AkUXy+6guXoboA3Y=", + "dev": true, + "requires": { + "to-object-path": "^0.3.0" + } + }, + "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==", + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "requires": { + "kind-of": "^6.0.2" + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + }, + "shelljs": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz", + "integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==", + "dev": true, + "requires": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + } + }, + "side-channel": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.2.tgz", + "integrity": "sha512-7rL9YlPHg7Ancea1S96Pa8/QWb4BtXL/TZvS6B8XFetGBeuhAsfmUspK6DokBeZ64+Kj9TCNRD/30pVz1BvQNA==", + "requires": { + "es-abstract": "^1.17.0-next.1", + "object-inspect": "^1.7.0" + } + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==" + }, + "simple-html-tokenizer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/simple-html-tokenizer/-/simple-html-tokenizer-0.1.1.tgz", + "integrity": "sha1-BcLuxXn//+FFoDCsJs/qYbmA+r4=" + }, + "simple-is": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/simple-is/-/simple-is-0.2.0.tgz", + "integrity": "sha1-Krt1qt453rXMgVzhDmGRFkhQuvA=" + }, + "simply-uuid": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simply-uuid/-/simply-uuid-1.0.1.tgz", + "integrity": "sha1-U5JB2BUolpzvI4kvr0WIAF+pmrg=" + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + } + }, + "snake-case": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-2.1.0.tgz", + "integrity": "sha1-Qb2xtz8w7GagTU4srRt2OH1NbZ8=", + "requires": { + "no-case": "^2.2.0" + } + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "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": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, + "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==", + "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=", + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "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==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "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==", + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "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=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "sniffr": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/sniffr/-/sniffr-1.2.0.tgz", + "integrity": "sha512-k7C0ZcHBU330LcSkKyc2cOOB0uHosME8b2t9qFJqdqB1cKwGmZWd7BVwBz5mWOMJ5dggK1dy2qv+DSwteKLBzQ==" + }, + "sockjs": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.20.tgz", + "integrity": "sha512-SpmVOVpdq0DJc0qArhF3E5xsxvaiqGNb73XfgBpK1y3UD5gs8DSo8aCTsuT5pX8rssdc2NDIzANwP9eCAiSdTA==", + "requires": { + "faye-websocket": "^0.10.0", + "uuid": "^3.4.0", + "websocket-driver": "0.6.5" + } + }, + "sockjs-client": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.4.0.tgz", + "integrity": "sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g==", + "requires": { + "debug": "^3.2.5", + "eventsource": "^1.0.7", + "faye-websocket": "~0.11.1", + "inherits": "^2.0.3", + "json3": "^3.3.2", + "url-parse": "^1.4.3" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "requires": { + "ms": "^2.1.1" + } + }, + "faye-websocket": { + "version": "0.11.3", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz", + "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==", + "requires": { + "websocket-driver": ">=0.5.1" + } + } + } + }, + "sort-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz", + "integrity": "sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg=", + "dev": true, + "requires": { + "is-plain-obj": "^1.0.0" + } + }, + "sort-on": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/sort-on/-/sort-on-3.0.0.tgz", + "integrity": "sha512-e2RHeY1iM6dT9od3RoqeJSyz3O7naNFsGy34+EFEcwghjAncuOXC2/Xwq87S4FbypqLVp6PcizYEsGEGsGIDXA==", + "dev": true, + "requires": { + "arrify": "^1.0.0", + "dot-prop": "^4.1.1" + }, + "dependencies": { + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + } + } + }, + "source-list-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", + "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==" + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "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==", + "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-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=" + }, + "sourcemapped-stacktrace": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/sourcemapped-stacktrace/-/sourcemapped-stacktrace-1.1.11.tgz", + "integrity": "sha512-O0pcWjJqzQFVsisPlPXuNawJHHg9N9UgpJ/aDmvi9+vnS3x1C0NhwkVFzzZ1VN0Xo+bekyweoqYvBw5ZBKiNnQ==", + "requires": { + "source-map": "0.5.6" + }, + "dependencies": { + "source-map": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz", + "integrity": "sha1-dc449SvwczxafwwRjYEzSiu19BI=" + } + } + }, + "spawn-sync": { + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/spawn-sync/-/spawn-sync-1.0.15.tgz", + "integrity": "sha1-sAeZVX63+wyDdsKdROih6mfldHY=", + "dev": true, + "requires": { + "concat-stream": "^1.4.7", + "os-shim": "^0.1.2" + } + }, + "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.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "dev": true + }, + "spdy": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", + "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", + "requires": { + "debug": "^4.1.0", + "handle-thing": "^2.0.0", + "http-deceiver": "^1.2.7", + "select-hose": "^2.0.0", + "spdy-transport": "^3.0.0" + } + }, + "spdy-transport": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", + "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", + "requires": { + "debug": "^4.1.0", + "detect-node": "^2.0.4", + "hpack.js": "^2.1.6", + "obuf": "^1.1.2", + "readable-stream": "^3.0.6", + "wbuf": "^1.7.3" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "specificity": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/specificity/-/specificity-0.4.1.tgz", + "integrity": "sha512-1klA3Gi5PD1Wv9Q0wUoOQN1IWAuPu0D1U03ThXTr0cJ20+/iq2tHSDnK7Kk/0LXJ1ztUB2/1Os0wKmfyNgUQfg==", + "dev": true + }, + "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==", + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + }, + "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" + } + }, + "ssri": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", + "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "requires": { + "figgy-pudding": "^3.5.1" + } + }, + "stackframe": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-0.3.1.tgz", + "integrity": "sha1-M6qE8Rd6VUjIk1Uzy/6zQgl19aQ=" + }, + "state-toggle": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/state-toggle/-/state-toggle-1.0.3.tgz", + "integrity": "sha512-d/5Z4/2iiCnHw6Xzghyhb+GcmF89bxwgXG60wjIiZaxnymbyOmI8Hk4VqHXiVVp6u2ysaskFfXg3ekCj4WNftQ==" + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "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=", + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "stream-browserify": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", + "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", + "requires": { + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" + } + }, + "stream-each": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", + "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", + "requires": { + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" + } + }, + "stream-http": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", + "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", + "requires": { + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" + } + }, + "stream-shift": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", + "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" + }, + "strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", + "dev": true + }, + "string-length": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-2.0.0.tgz", + "integrity": "sha1-1A27aGo6zpYMHP/KVivyxF+DY+0=", + "dev": true, + "requires": { + "astral-regex": "^1.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "string-template": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz", + "integrity": "sha1-QpMuWYo1LQH8IuwzZ9nYTuxsmt0=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "string.prototype.trimend": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", + "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "string.prototype.trimleft": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", + "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5", + "string.prototype.trimstart": "^1.0.0" + } + }, + "string.prototype.trimright": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", + "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5", + "string.prototype.trimend": "^1.0.0" + } + }, + "string.prototype.trimstart": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", + "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "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==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "stringify-entities": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-3.0.1.tgz", + "integrity": "sha512-Lsk3ISA2++eJYqBMPKcr/8eby1I6L0gP0NlxF8Zja6c05yr/yCYyb2c9PwXjd08Ib3If1vn1rbs1H5ZtVuOfvQ==", + "dev": true, + "requires": { + "character-entities-html4": "^1.0.0", + "character-entities-legacy": "^1.0.0", + "is-alphanumerical": "^1.0.0", + "is-decimal": "^1.0.2", + "is-hexadecimal": "^1.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=", + "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" + } + }, + "strip-bom-buf": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-buf/-/strip-bom-buf-1.0.0.tgz", + "integrity": "sha1-HLRar1dTD0yvhsf3UXnSyaUd1XI=", + "dev": true, + "requires": { + "is-utf8": "^0.2.1" + } + }, + "strip-bom-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-2.0.0.tgz", + "integrity": "sha1-+H217yYT9paKpUWr/h7HKLaoKco=", + "dev": true, + "requires": { + "first-chunk-stream": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, + "strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "requires": { + "min-indent": "^1.0.0" + } + }, + "strip-json-comments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.0.tgz", + "integrity": "sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w==", + "dev": true + }, + "style-loader": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-1.0.2.tgz", + "integrity": "sha512-xehHGWeCPrr+R/bU82To0j7L7ENzH30RHYmMhmAumbuIpQ/bHmv3SAj1aTRfBSszkXoqNtpKnJyWXEDydS+KeA==", + "requires": { + "loader-utils": "^1.2.3", + "schema-utils": "^2.0.1" + } + }, + "style-search": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz", + "integrity": "sha1-eVjHk+R+MuB9K1yv5cC/jhLneQI=", + "dev": true + }, + "stylelint": { + "version": "13.6.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-13.6.0.tgz", + "integrity": "sha512-55gG2pNjVr183JJM/tlr3KAua6vTVX7Ho/lgKKuCIWszTZ1gmrXjX4Wok53SI8wRYFPbwKAcJGULQ77OJxTcNw==", + "dev": true, + "requires": { + "@stylelint/postcss-css-in-js": "^0.37.1", + "@stylelint/postcss-markdown": "^0.36.1", + "autoprefixer": "^9.8.0", + "balanced-match": "^1.0.0", + "chalk": "^4.0.0", + "cosmiconfig": "^6.0.0", + "debug": "^4.1.1", + "execall": "^2.0.0", + "file-entry-cache": "^5.0.1", + "get-stdin": "^8.0.0", + "global-modules": "^2.0.0", + "globby": "^11.0.1", + "globjoin": "^0.1.4", + "html-tags": "^3.1.0", + "ignore": "^5.1.8", + "import-lazy": "^4.0.0", + "imurmurhash": "^0.1.4", + "known-css-properties": "^0.19.0", + "leven": "^3.1.0", + "lodash": "^4.17.15", + "log-symbols": "^4.0.0", + "mathml-tag-names": "^2.1.3", + "meow": "^7.0.1", + "micromatch": "^4.0.2", + "normalize-selector": "^0.2.0", + "postcss": "^7.0.32", + "postcss-html": "^0.36.0", + "postcss-less": "^3.1.4", + "postcss-media-query-parser": "^0.2.3", + "postcss-reporter": "^6.0.1", + "postcss-resolve-nested-selector": "^0.1.1", + "postcss-safe-parser": "^4.0.2", + "postcss-sass": "^0.4.4", + "postcss-scss": "^2.1.1", + "postcss-selector-parser": "^6.0.2", + "postcss-syntax": "^0.36.2", + "postcss-value-parser": "^4.1.0", + "resolve-from": "^5.0.0", + "slash": "^3.0.0", + "specificity": "^0.4.1", + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "style-search": "^0.1.0", + "sugarss": "^2.0.0", + "svg-tags": "^1.0.0", + "table": "^5.4.6", + "v8-compile-cache": "^2.1.1", + "write-file-atomic": "^3.0.3" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "dev": true, + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "globby": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz", + "integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true + }, + "import-fresh": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", + "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + } + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "parse-json": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz", + "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1", + "lines-and-columns": "^1.1.6" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "v8-compile-cache": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz", + "integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==", + "dev": true + } + } + }, + "stylelint-config-css-modules": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/stylelint-config-css-modules/-/stylelint-config-css-modules-2.2.0.tgz", + "integrity": "sha512-+zjcDbot+zbuxy1UA31k4G2lUG+nHUwnLyii3uT2F09B8kT2YrT9LZYNfMtAWlDidrxr7sFd5HX9EqPHGU3WKA==", + "dev": true + }, + "stylelint-config-recommended": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-3.0.0.tgz", + "integrity": "sha512-F6yTRuc06xr1h5Qw/ykb2LuFynJ2IxkKfCMf+1xqPffkxh0S09Zc902XCffcsw/XMFq/OzQ1w54fLIDtmRNHnQ==", + "dev": true + }, + "stylelint-config-standard": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-19.0.0.tgz", + "integrity": "sha512-VvcODsL1PryzpYteWZo2YaA5vU/pWfjqBpOvmeA8iB2MteZ/ZhI1O4hnrWMidsS4vmEJpKtjdhLdfGJmmZm6Cg==", + "dev": true, + "requires": { + "stylelint-config-recommended": "^3.0.0" + } + }, + "stylelint-order": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/stylelint-order/-/stylelint-order-3.1.1.tgz", + "integrity": "sha512-4gP/r8j/6JGZ/LL41b2sYtQqfwZl4VSqTp7WeIwI67v/OXNQ08dnn64BGXNwAUSgb2+YIvIOxQaMzqMyQMzoyQ==", + "dev": true, + "requires": { + "lodash": "^4.17.15", + "postcss": "^7.0.17", + "postcss-sorting": "^5.0.1" + } + }, + "sudo-block": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/sudo-block/-/sudo-block-1.2.0.tgz", + "integrity": "sha1-zFOb+BkWJNT1B9g+60W0zqJ/NGM=", + "dev": true, + "requires": { + "chalk": "^1.0.0", + "is-docker": "^1.0.0", + "is-root": "^1.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "sugarss": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/sugarss/-/sugarss-2.0.0.tgz", + "integrity": "sha512-WfxjozUk0UVA4jm+U1d736AUpzSrNsQcIbyOkoE364GrtWmIrFdk5lksEupgWMD4VaT/0kVx1dobpiDumSgmJQ==", + "dev": true, + "requires": { + "postcss": "^7.0.2" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "svg-inline-loader": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/svg-inline-loader/-/svg-inline-loader-0.8.2.tgz", + "integrity": "sha512-kbrcEh5n5JkypaSC152eGfGcnT4lkR0eSfvefaUJkLqgGjRQJyKDvvEE/CCv5aTSdfXuc+N98w16iAojhShI3g==", + "requires": { + "loader-utils": "^1.1.0", + "object-assign": "^4.0.1", + "simple-html-tokenizer": "^0.1.1" + } + }, + "svg-tags": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", + "integrity": "sha1-WPcc7jvVGbWdSyqEO2x95krAR2Q=", + "dev": true + }, + "swap-case": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/swap-case/-/swap-case-1.1.2.tgz", + "integrity": "sha1-w5IDpFhzhfrTyFCgvRvK+ggZdOM=", + "requires": { + "lower-case": "^1.1.1", + "upper-case": "^1.1.1" + } + }, + "symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" + }, + "tabbable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-4.0.0.tgz", + "integrity": "sha512-H1XoH1URcBOa/rZZWxLxHCtOdVUEev+9vo5YdYhC9tCY4wnybX+VQrCYuy9ubkg69fCBxCONJOSLGfw0DWMffQ==" + }, + "table": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + } + }, + "tabtab": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/tabtab/-/tabtab-1.3.2.tgz", + "integrity": "sha1-u5wspjJPZZ/edjTCyvPAluEYfKc=", + "dev": true, + "requires": { + "debug": "^2.2.0", + "inquirer": "^1.0.2", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "npmlog": "^2.0.3", + "object-assign": "^4.1.0" + }, + "dependencies": { + "ansi-escapes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", + "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "cli-cursor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", + "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", + "dev": true, + "requires": { + "restore-cursor": "^1.0.1" + } + }, + "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" + } + }, + "external-editor": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-1.1.1.tgz", + "integrity": "sha1-Etew24UPf/fnCBuvQAVwAGDEYAs=", + "dev": true, + "requires": { + "extend": "^3.0.0", + "spawn-sync": "^1.0.15", + "tmp": "^0.0.29" + } + }, + "figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" + } + }, + "inquirer": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-1.2.3.tgz", + "integrity": "sha1-TexvMvN+97sLLtPx0aXD9UUHSRg=", + "dev": true, + "requires": { + "ansi-escapes": "^1.1.0", + "chalk": "^1.0.0", + "cli-cursor": "^1.0.1", + "cli-width": "^2.0.0", + "external-editor": "^1.1.0", + "figures": "^1.3.5", + "lodash": "^4.3.0", + "mute-stream": "0.0.6", + "pinkie-promise": "^2.0.0", + "run-async": "^2.2.0", + "rx": "^4.1.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.0", + "through": "^2.3.6" + } + }, + "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" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "mute-stream": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.6.tgz", + "integrity": "sha1-SJYrGeFp/R38JAs/HnMXYnu8R9s=", + "dev": true + }, + "onetime": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", + "dev": true + }, + "restore-cursor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", + "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", + "dev": true, + "requires": { + "exit-hook": "^1.0.0", + "onetime": "^1.0.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" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "tmp": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.29.tgz", + "integrity": "sha1-8lEl/w3Z2jzLDC3Tce4SiLuRKMA=", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.1" + } + } + } + }, + "taketalk": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/taketalk/-/taketalk-1.0.0.tgz", + "integrity": "sha1-tNTw3u0gauffd1sSnqLKbeUvJt0=", + "dev": true, + "requires": { + "get-stdin": "^4.0.1", + "minimist": "^1.1.0" + }, + "dependencies": { + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true + } + } + }, + "tapable": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.9.tgz", + "integrity": "sha512-2wsvQ+4GwBvLPLWsNfLCDYGsW6xb7aeC6utq2Qh0PFwgEy7K7dsma9Jsmb2zSQj7GvYAyUGSntLtsv++GmgL1A==" + }, + "term-size": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", + "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", + "dev": true, + "requires": { + "execa": "^0.7.0" + }, + "dependencies": { + "cross-spawn": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", + "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", + "dev": true, + "requires": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "dev": true, + "requires": { + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", + "dev": true + }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + } + } + }, + "terser": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-4.7.0.tgz", + "integrity": "sha512-Lfb0RiZcjRDXCC3OSHJpEkxJ9Qeqs6mp2v4jf2MHfy8vGERmVDuvjXdd/EnP5Deme5F2yBRBymKmKHCBg2echw==", + "requires": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + }, + "dependencies": { + "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==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "terser-webpack-plugin": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.4.tgz", + "integrity": "sha512-U4mACBHIegmfoEe5fdongHESNJWqsGU+W0S/9+BmYGVQDw1+c2Ow05TpMhxjPK1sRb7cuYq1BPl1e5YHJMTCqA==", + "requires": { + "cacache": "^12.0.2", + "find-cache-dir": "^2.1.0", + "is-wsl": "^1.1.0", + "schema-utils": "^1.0.0", + "serialize-javascript": "^3.1.0", + "source-map": "^0.6.1", + "terser": "^4.1.2", + "webpack-sources": "^1.4.0", + "worker-farm": "^1.7.0" + }, + "dependencies": { + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "textextensions": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-2.6.0.tgz", + "integrity": "sha512-49WtAWS+tcsy93dRt6P0P3AMD2m5PvXRhuEA0kaXos5ZLlujtYmpmFsB+QvWUSxE1ZsstmYXfQ7L40+EcQgpAQ==", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" + }, + "timed-out": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", + "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", + "dev": true + }, + "timers-browserify": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz", + "integrity": "sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==", + "requires": { + "setimmediate": "^1.0.4" + } + }, + "title-case": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/title-case/-/title-case-2.1.1.tgz", + "integrity": "sha1-PhJyFtpY0rxb7PE3q5Ha46fNj6o=", + "requires": { + "no-case": "^2.2.0", + "upper-case": "^1.0.3" + } + }, + "titleize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/titleize/-/titleize-1.0.1.tgz", + "integrity": "sha512-rUwGDruKq1gX+FFHbTl5qjI7teVO7eOe+C8IcQ7QT+1BK3eEUXJqbZcBOeaRP4FwSC/C1A5jDoIVta0nIQ9yew==", + "dev": true + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "to-arraybuffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", + "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=" + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" + }, + "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=", + "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=", + "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==", + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "requires": { + "is-number": "^7.0.0" + } + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "tough-cookie": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", + "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", + "dev": true, + "requires": { + "ip-regex": "^2.1.0", + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "traverse": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz", + "integrity": "sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc=" + }, + "trim": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/trim/-/trim-0.0.1.tgz", + "integrity": "sha1-WFhUf2spB1fulczMZm+1AITEYN0=" + }, + "trim-newlines": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.0.tgz", + "integrity": "sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA==", + "dev": true + }, + "trim-trailing-lines": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/trim-trailing-lines/-/trim-trailing-lines-1.1.3.tgz", + "integrity": "sha512-4ku0mmjXifQcTVfYDfR5lpgV7zVqPg6zV9rdZmwOPqq0+Zq19xDqEgagqVbc4pOOShbncuAOIs59R3+3gcF3ZA==" + }, + "trough": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz", + "integrity": "sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA==" + }, + "ts-loader": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-7.0.5.tgz", + "integrity": "sha512-zXypEIT6k3oTc+OZNx/cqElrsbBtYqDknf48OZos0NQ3RTt045fBIU8RRSu+suObBzYB355aIPGOe/3kj9h7Ig==", + "requires": { + "chalk": "^2.3.0", + "enhanced-resolve": "^4.0.0", + "loader-utils": "^1.0.2", + "micromatch": "^4.0.0", + "semver": "^6.0.0" + }, + "dependencies": { + "enhanced-resolve": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz", + "integrity": "sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA==", + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" + } + }, + "memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==" + } + } + }, + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" + }, + "tty-browserify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", + "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=" + }, + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "dev": true + }, + "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 + }, + "twig": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/twig/-/twig-1.15.1.tgz", + "integrity": "sha512-SPGkUM0p7hjgo+y5Dlm/XCSuZe0G3kfcgRPrxkMFln5e8bvQbxDOsia8QEo8xqXfjLR1Emp9FGkVYHya2b8qdA==", + "dev": true, + "requires": { + "@babel/runtime": "^7.8.4", + "locutus": "^2.0.11", + "minimatch": "3.0.x", + "walk": "2.3.x" + } + }, + "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" + } + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "typescript": { + "version": "3.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.5.tgz", + "integrity": "sha512-hSAifV3k+i6lEoCJ2k6R2Z/rp/H3+8sdmcn5NrS3/3kE7+RyZXm9aqvxWqjEXHAd8b0pShatpcdMTvEdvAJltQ==" + }, + "ua-parser-js": { + "version": "0.7.21", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.21.tgz", + "integrity": "sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ==" + }, + "uglify-js": { + "version": "3.4.10", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.10.tgz", + "integrity": "sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw==", + "requires": { + "commander": "~2.19.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "commander": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "unherit": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/unherit/-/unherit-1.1.3.tgz", + "integrity": "sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ==", + "requires": { + "inherits": "^2.0.0", + "xtend": "^4.0.0" + } + }, + "unicode-canonical-property-names-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", + "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==" + }, + "unicode-match-property-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", + "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", + "requires": { + "unicode-canonical-property-names-ecmascript": "^1.0.4", + "unicode-property-aliases-ecmascript": "^1.0.4" + } + }, + "unicode-match-property-value-ecmascript": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz", + "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==" + }, + "unicode-property-aliases-ecmascript": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz", + "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==" + }, + "unified": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/unified/-/unified-6.2.0.tgz", + "integrity": "sha512-1k+KPhlVtqmG99RaTbAv/usu85fcSRu3wY8X+vnsEhIxNP5VbVIDiXnLqyKIG+UMdyTg0ZX9EI6k2AfjJkHPtA==", + "requires": { + "bail": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^1.1.0", + "trough": "^1.0.0", + "vfile": "^2.0.0", + "x-is-string": "^0.1.0" + } + }, + "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==", + "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=" + }, + "unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "requires": { + "unique-slug": "^2.0.0" + } + }, + "unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "requires": { + "imurmurhash": "^0.1.4" + } + }, + "unique-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", + "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", + "dev": true, + "requires": { + "crypto-random-string": "^1.0.0" + } + }, + "unist-util-find-all-after": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/unist-util-find-all-after/-/unist-util-find-all-after-3.0.1.tgz", + "integrity": "sha512-0GICgc++sRJesLwEYDjFVJPJttBpVQaTNgc6Jw0Jhzvfs+jtKePEMu+uD+PqkRUrAvGQqwhpDwLGWo1PK8PDEw==", + "dev": true, + "requires": { + "unist-util-is": "^4.0.0" + }, + "dependencies": { + "unist-util-is": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-4.0.2.tgz", + "integrity": "sha512-Ofx8uf6haexJwI1gxWMGg6I/dLnF2yE+KibhD3/diOqY2TinLcqHXCV6OI5gFVn3xQqDH+u0M625pfKwIwgBKQ==", + "dev": true + } + } + }, + "unist-util-is": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-3.0.0.tgz", + "integrity": "sha512-sVZZX3+kspVNmLWBPAB6r+7D9ZgAFPNWm66f7YNb420RlQSbn+n8rG8dGZSkrER7ZIXGQYNm5pqC3v3HopH24A==" + }, + "unist-util-remove-position": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-1.1.4.tgz", + "integrity": "sha512-tLqd653ArxJIPnKII6LMZwH+mb5q+n/GtXQZo6S6csPRs5zB0u79Yw8ouR3wTw8wxvdJFhpP6Y7jorWdCgLO0A==", + "requires": { + "unist-util-visit": "^1.1.0" + } + }, + "unist-util-stringify-position": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-1.1.2.tgz", + "integrity": "sha512-pNCVrk64LZv1kElr0N1wPiHEUoXNVFERp+mlTg/s9R5Lwg87f9bM/3sQB99w+N9D/qnM9ar3+AKDBwo/gm/iQQ==" + }, + "unist-util-visit": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-1.4.1.tgz", + "integrity": "sha512-AvGNk7Bb//EmJZyhtRUnNMEpId/AZ5Ph/KUpTI09WHQuDZHKovQ1oEv3mfmKpWKtoMzyMC4GLBm1Zy5k12fjIw==", + "requires": { + "unist-util-visit-parents": "^2.0.0" + }, + "dependencies": { + "unist-util-visit-parents": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-2.1.2.tgz", + "integrity": "sha512-DyN5vD4NE3aSeB+PXYNKxzGsfocxp6asDc2XXE3b0ekO2BaRUpBicbbUygfSvYfUz1IkmjFR1YF7dPklraMZ2g==", + "requires": { + "unist-util-is": "^3.0.0" + } + } + } + }, + "unist-util-visit-parents": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-1.1.2.tgz", + "integrity": "sha512-yvo+MMLjEwdc3RhhPYSximset7rwjMrdt9E41Smmvg25UQIenzrN83cRnF1JMzoMi9zZOQeYXHSDf7p+IQkW3Q==" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "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=", + "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=", + "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=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + } + } + }, + "untildify": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-3.0.3.tgz", + "integrity": "sha512-iSk/J8efr8uPT/Z4eSUywnqyrQU7DSdMfdqK4iWEaUVVmcP5JcnpRqmVMwcwcnmI1ATFNgC5V90u09tBynNFKA==", + "dev": true + }, + "unzip-response": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", + "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=", + "dev": true + }, + "upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==" + }, + "update-notifier": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.5.0.tgz", + "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==", + "dev": true, + "requires": { + "boxen": "^1.2.1", + "chalk": "^2.0.1", + "configstore": "^3.0.0", + "import-lazy": "^2.1.0", + "is-ci": "^1.0.10", + "is-installed-globally": "^0.1.0", + "is-npm": "^1.0.0", + "latest-version": "^3.0.0", + "semver-diff": "^2.0.0", + "xdg-basedir": "^3.0.0" + }, + "dependencies": { + "import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", + "dev": true + } + } + }, + "upper-case": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz", + "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=" + }, + "upper-case-first": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-1.1.2.tgz", + "integrity": "sha1-XXm+3P8UQZUY/S7bCgUHybaFkRU=", + "requires": { + "upper-case": "^1.1.1" + } + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "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=" + }, + "url": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", + "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + }, + "dependencies": { + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + } + } + }, + "url-loader": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-3.0.0.tgz", + "integrity": "sha512-a84JJbIA5xTFTWyjjcPdnsu+41o/SNE8SpXMdUvXs6Q+LuhCD9E2+0VCiuDWqgo3GGXVlFHzArDmBpj9PgWn4A==", + "requires": { + "loader-utils": "^1.2.3", + "mime": "^2.4.4", + "schema-utils": "^2.5.0" + } + }, + "url-parse": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", + "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==", + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "dev": true, + "requires": { + "prepend-http": "^2.0.0" + } + }, + "url-search-params": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/url-search-params/-/url-search-params-1.1.0.tgz", + "integrity": "sha512-XiO5GLAxmlZgdLob/RmKZRc2INHrssMbpwD6O46JkB1aEJO4fkV3x3mR6+CDX01ijfEUwvfwCiUQZrKqfm1ILw==" + }, + "url-to-options": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", + "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=", + "dev": true + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" + }, + "user-home": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz", + "integrity": "sha1-nHC/2Babwdy/SGBODwS4tJzenp8=", + "dev": true, + "requires": { + "os-homedir": "^1.0.0" + } + }, + "util": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", + "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", + "requires": { + "inherits": "2.0.3" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + } + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + }, + "v8-compile-cache": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz", + "integrity": "sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w==" + }, + "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" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "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" + } + }, + "vfile": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-2.3.0.tgz", + "integrity": "sha512-ASt4mBUHcTpMKD/l5Q+WJXNtshlWxOogYyGYYrg4lt/vuRjC1EFQtlAofL5VmtVNIZJzWYFJjzGWZ0Gw8pzW1w==", + "requires": { + "is-buffer": "^1.1.4", + "replace-ext": "1.0.0", + "unist-util-stringify-position": "^1.0.0", + "vfile-message": "^1.0.0" + } + }, + "vfile-location": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-2.0.6.tgz", + "integrity": "sha512-sSFdyCP3G6Ka0CEmN83A2YCMKIieHx0EDaj5IDP4g1pa5ZJ4FJDvpO0WODLxo4LUX4oe52gmSCK7Jw4SBghqxA==" + }, + "vfile-message": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-1.1.1.tgz", + "integrity": "sha512-1WmsopSGhWt5laNir+633LszXvZ+Z/lxveBf6yhGsqnQIhlhzooZae7zV6YVM1Sdkw68dtAW3ow0pOdPANugvA==", + "requires": { + "unist-util-stringify-position": "^1.1.1" + } + }, + "vinyl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.0.tgz", + "integrity": "sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg==", + "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-file": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/vinyl-file/-/vinyl-file-3.0.0.tgz", + "integrity": "sha1-sQTZ5ECf+jJfqt1SBkLQo7SIs2U=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.3.0", + "strip-bom-buf": "^1.0.0", + "strip-bom-stream": "^2.0.0", + "vinyl": "^2.0.1" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "vm-browserify": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", + "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==" + }, + "walk": { + "version": "2.3.14", + "resolved": "https://registry.npmjs.org/walk/-/walk-2.3.14.tgz", + "integrity": "sha512-5skcWAUmySj6hkBdH6B6+3ddMjVQYH5Qy9QGbPmN8kVmLteXk+yVXg+yfk1nbX30EYakahLrr8iPcCxJQSCBeg==", + "dev": true, + "requires": { + "foreachasync": "^3.0.0" + } + }, + "watchpack": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.2.tgz", + "integrity": "sha512-ymVbbQP40MFTp+cNMvpyBpBtygHnPzPkHqoIwRRj/0B8KhqQwV8LaKjtbaxF2lK4vl8zN9wCxS46IFCU5K4W0g==", + "requires": { + "chokidar": "^3.4.0", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0", + "watchpack-chokidar2": "^2.0.0" + } + }, + "watchpack-chokidar2": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.0.tgz", + "integrity": "sha512-9TyfOyN/zLUbA288wZ8IsMZ+6cbzvsNyEzSBp6e/zkifi6xxbl8SmQ/CxQq32k8NNqrdVEVUVSEf56L4rQ/ZxA==", + "optional": true, + "requires": { + "chokidar": "^2.1.8" + }, + "dependencies": { + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "optional": 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=", + "optional": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "optional": true + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "optional": 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" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "optional": 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" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "optional": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "optional": 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=", + "optional": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "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=", + "optional": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "optional": 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=", + "optional": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "optional": 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" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "optional": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "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=", + "optional": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } + }, + "wbuf": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", + "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", + "requires": { + "minimalistic-assert": "^1.0.0" + } + }, + "webpack": { + "version": "4.43.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.43.0.tgz", + "integrity": "sha512-GW1LjnPipFW2Y78OOab8NJlCflB7EFskMih2AHdvjbpKMeDJqEgSx24cXXXiPS65+WSwVyxtDsJH6jGX2czy+g==", + "requires": { + "@webassemblyjs/ast": "1.9.0", + "@webassemblyjs/helper-module-context": "1.9.0", + "@webassemblyjs/wasm-edit": "1.9.0", + "@webassemblyjs/wasm-parser": "1.9.0", + "acorn": "^6.4.1", + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^4.1.0", + "eslint-scope": "^4.0.3", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.4.0", + "loader-utils": "^1.2.3", + "memory-fs": "^0.4.1", + "micromatch": "^3.1.10", + "mkdirp": "^0.5.3", + "neo-async": "^2.6.1", + "node-libs-browser": "^2.2.1", + "schema-utils": "^1.0.0", + "tapable": "^1.1.3", + "terser-webpack-plugin": "^1.4.3", + "watchpack": "^1.6.1", + "webpack-sources": "^1.4.1" + }, + "dependencies": { + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "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" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "enhanced-resolve": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz", + "integrity": "sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA==", + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.5.0", + "tapable": "^1.0.0" + }, + "dependencies": { + "memory-fs": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", + "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", + "requires": { + "errno": "^0.1.3", + "readable-stream": "^2.0.1" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "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=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "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" + } + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==" + }, + "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=", + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + } + } + }, + "webpack-cli": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.11.tgz", + "integrity": "sha512-dXlfuml7xvAFwYUPsrtQAA9e4DOe58gnzSxhgrO/ZM/gyXTBowrsYeubyN4mqGhYdpXMFNyQ6emjJS9M7OBd4g==", + "requires": { + "chalk": "2.4.2", + "cross-spawn": "6.0.5", + "enhanced-resolve": "4.1.0", + "findup-sync": "3.0.0", + "global-modules": "2.0.0", + "import-local": "2.0.0", + "interpret": "1.2.0", + "loader-utils": "1.2.3", + "supports-color": "6.1.0", + "v8-compile-cache": "2.0.3", + "yargs": "13.2.4" + }, + "dependencies": { + "emojis-list": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", + "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=" + }, + "enhanced-resolve": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz", + "integrity": "sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==", + "requires": { + "graceful-fs": "^4.1.2", + "memory-fs": "^0.4.0", + "tapable": "^1.0.0" + } + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", + "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^2.0.0", + "json5": "^1.0.1" + } + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "tapable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", + "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==" + } + } + }, + "webpack-dev-middleware": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz", + "integrity": "sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw==", + "requires": { + "memory-fs": "^0.4.1", + "mime": "^2.4.4", + "mkdirp": "^0.5.1", + "range-parser": "^1.2.1", + "webpack-log": "^2.0.0" + } + }, + "webpack-dev-server": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.0.tgz", + "integrity": "sha512-PUxZ+oSTxogFQgkTtFndEtJIPNmml7ExwufBZ9L2/Xyyd5PnOL5UreWe5ZT7IU25DSdykL9p1MLQzmLh2ljSeg==", + "requires": { + "ansi-html": "0.0.7", + "bonjour": "^3.5.0", + "chokidar": "^2.1.8", + "compression": "^1.7.4", + "connect-history-api-fallback": "^1.6.0", + "debug": "^4.1.1", + "del": "^4.1.1", + "express": "^4.17.1", + "html-entities": "^1.3.1", + "http-proxy-middleware": "0.19.1", + "import-local": "^2.0.0", + "internal-ip": "^4.3.0", + "ip": "^1.1.5", + "is-absolute-url": "^3.0.3", + "killable": "^1.0.1", + "loglevel": "^1.6.8", + "opn": "^5.5.0", + "p-retry": "^3.0.1", + "portfinder": "^1.0.26", + "schema-utils": "^1.0.0", + "selfsigned": "^1.10.7", + "semver": "^6.3.0", + "serve-index": "^1.9.1", + "sockjs": "0.3.20", + "sockjs-client": "1.4.0", + "spdy": "^4.0.2", + "strip-ansi": "^3.0.1", + "supports-color": "^6.1.0", + "url": "^0.11.0", + "webpack-dev-middleware": "^3.7.2", + "webpack-log": "^2.0.0", + "ws": "^6.2.1", + "yargs": "^13.3.2" + }, + "dependencies": { + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "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=", + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==" + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "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" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "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" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "fsevents": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", + "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", + "optional": true, + "requires": { + "bindings": "^1.5.0", + "nan": "^2.12.1" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "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=", + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "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=", + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "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=", + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "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" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "schema-utils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", + "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", + "requires": { + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + }, + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "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=", + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" + } + } + } + }, + "webpack-log": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz", + "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==", + "requires": { + "ansi-colors": "^3.0.0", + "uuid": "^3.3.2" + } + }, + "webpack-sources": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", + "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", + "requires": { + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + } + } + }, + "websocket-driver": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.6.5.tgz", + "integrity": "sha1-XLJVbOuF9Dc8bYI4qmkchFThOjY=", + "requires": { + "websocket-extensions": ">=0.1.1" + } + }, + "websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==" + }, + "whatwg-fetch": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz", + "integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==" + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + }, + "which-boxed-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.1.tgz", + "integrity": "sha512-7BT4TwISdDGBgaemWU0N0OU7FeAEJ9Oo2P1PHRm/FCWoEi2VLWC9b6xvxAA3C/NMpxg3HXVgi0sMmGbNUbNepQ==", + "requires": { + "is-bigint": "^1.0.0", + "is-boolean-object": "^1.0.0", + "is-number-object": "^1.0.3", + "is-string": "^1.0.4", + "is-symbol": "^1.0.2" + } + }, + "which-collection": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", + "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", + "requires": { + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-weakmap": "^2.0.1", + "is-weakset": "^2.0.1" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" + }, + "which-typed-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.2.tgz", + "integrity": "sha512-KT6okrd1tE6JdZAy3o2VhMoYPh3+J6EMZLyrxBQsZflI1QCZIxMrIYLkosd8Twf+YfknVIHmYQPgJt238p8dnQ==", + "requires": { + "available-typed-arrays": "^1.0.2", + "es-abstract": "^1.17.5", + "foreach": "^2.0.5", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.1", + "is-typed-array": "^1.1.3" + } + }, + "widest-line": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz", + "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", + "dev": true, + "requires": { + "string-width": "^2.1.1" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "windows-release": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.3.1.tgz", + "integrity": "sha512-Pngk/RDCaI/DkuHPlGTdIkDiTAnAkyMjoQMZqRsxydNl1qGXNIoZrB7RK8g53F2tEgQBMqQJHQdYZuQEEAu54A==", + "dev": true, + "requires": { + "execa": "^1.0.0" + } + }, + "with-open-file": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/with-open-file/-/with-open-file-0.1.7.tgz", + "integrity": "sha512-ecJS2/oHtESJ1t3ZfMI3B7KIDKyfN0O16miWxdn30zdh66Yd3LsRFebXZXq6GU4xfxLf6nVxp9kIqElb5fqczA==", + "dev": true, + "requires": { + "p-finally": "^1.0.0", + "p-try": "^2.1.0", + "pify": "^4.0.1" + }, + "dependencies": { + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "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 + }, + "worker-farm": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", + "requires": { + "errno": "~0.1.7" + } + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + }, + "write-file-atomic": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "ws": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", + "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "requires": { + "async-limiter": "~1.0.0" + } + }, + "x-is-string": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/x-is-string/-/x-is-string-0.1.0.tgz", + "integrity": "sha1-R0tQhlrzpJqcRlfwWs0UVFj3fYI=" + }, + "xdg-basedir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", + "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=", + "dev": true + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, + "yaml": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz", + "integrity": "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==", + "dev": true + }, + "yargs": { + "version": "13.2.4", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.4.tgz", + "integrity": "sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg==", + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "os-locale": "^3.1.0", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" + } + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "yeoman-character": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/yeoman-character/-/yeoman-character-1.1.0.tgz", + "integrity": "sha1-kNS1vq+SdZCGF3AVsv36LgaE18c=", + "dev": true, + "requires": { + "supports-color": "^3.1.2" + }, + "dependencies": { + "has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", + "dev": true + }, + "supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", + "dev": true, + "requires": { + "has-flag": "^1.0.0" + } + } + } + }, + "yeoman-doctor": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yeoman-doctor/-/yeoman-doctor-4.0.0.tgz", + "integrity": "sha512-CP0fwGk5Y+jel+A0AQbyqnIFZRRpkKOeYUibiTSmlgV9PcgNFFVwn86VcUIpDLOqVjF+9v+O9FWQMo+IUcV2mA==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "bin-version-check": "^3.0.0", + "chalk": "^2.3.0", + "global-agent": "^2.0.0", + "global-tunnel-ng": "^2.5.3", + "latest-version": "^3.1.0", + "log-symbols": "^2.1.0", + "semver": "^5.0.3", + "twig": "^1.10.5", + "user-home": "^2.0.0" + }, + "dependencies": { + "log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "dev": true, + "requires": { + "chalk": "^2.0.1" + } + } + } + }, + "yeoman-environment": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/yeoman-environment/-/yeoman-environment-2.10.3.tgz", + "integrity": "sha512-pLIhhU9z/G+kjOXmJ2bPFm3nejfbH+f1fjYRSOteEXDBrv1EoJE/e+kuHixSXfCYfTkxjYsvRaDX+1QykLCnpQ==", + "dev": true, + "requires": { + "chalk": "^2.4.1", + "debug": "^3.1.0", + "diff": "^3.5.0", + "escape-string-regexp": "^1.0.2", + "execa": "^4.0.0", + "globby": "^8.0.1", + "grouped-queue": "^1.1.0", + "inquirer": "^7.1.0", + "is-scoped": "^1.0.0", + "lodash": "^4.17.10", + "log-symbols": "^2.2.0", + "mem-fs": "^1.1.0", + "mem-fs-editor": "^6.0.0", + "npm-api": "^1.0.0", + "semver": "^7.1.3", + "strip-ansi": "^4.0.0", + "text-table": "^0.2.0", + "untildify": "^3.0.3", + "yeoman-generator": "^4.8.2" + }, + "dependencies": { + "@nodelib/fs.stat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz", + "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", + "dev": true + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "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" + }, + "dependencies": { + "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" + } + } + } + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "dir-glob": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.0.0.tgz", + "integrity": "sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag==", + "dev": true, + "requires": { + "arrify": "^1.0.1", + "path-type": "^3.0.0" + } + }, + "execa": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.2.tgz", + "integrity": "sha512-QI2zLa6CjGWdiQsmSkZoGtDx2N+cQIGb3yNolGTdjSQzydzLgYYf8LRuagp7S7fPimjcrzUDSUFd/MgzELMi4Q==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + }, + "fast-glob": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz", + "integrity": "sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw==", + "dev": true, + "requires": { + "@mrmlnc/readdir-enhanced": "^2.2.1", + "@nodelib/fs.stat": "^1.1.2", + "glob-parent": "^3.1.0", + "is-glob": "^4.0.0", + "merge2": "^1.2.3", + "micromatch": "^3.1.10" + } + }, + "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" + }, + "dependencies": { + "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" + } + } + } + }, + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "dev": true, + "requires": { + "pump": "^3.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" + } + } + } + }, + "globby": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-8.0.2.tgz", + "integrity": "sha512-yTzMmKygLp8RUpG1Ymu2VXPSJQZjNAZPD4ywgYEaG7e4tBJeUQBO8OpXrf1RCNcEs5alsoJYPAMiIHP0cmeC7w==", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "dir-glob": "2.0.0", + "fast-glob": "^2.0.2", + "glob": "^7.1.2", + "ignore": "^3.3.5", + "pify": "^3.0.0", + "slash": "^1.0.0" + } + }, + "ignore": { + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", + "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==", + "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-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true + }, + "log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "dev": true, + "requires": { + "chalk": "^2.0.1" + } + }, + "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" + } + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "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" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "yeoman-generator": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/yeoman-generator/-/yeoman-generator-4.10.1.tgz", + "integrity": "sha512-QgbtHSaqBAkyJJM0heQUhT63ubCt34NBFMEBydOBUdAuy8RBvGSzeqVBSZOjdh1tSLrwWXlU3Ck6y14awinF6Q==", + "dev": true, + "requires": { + "async": "^2.6.2", + "chalk": "^2.4.2", + "cli-table": "^0.3.1", + "cross-spawn": "^6.0.5", + "dargs": "^6.1.0", + "dateformat": "^3.0.3", + "debug": "^4.1.1", + "diff": "^4.0.1", + "error": "^7.0.2", + "find-up": "^3.0.0", + "github-username": "^3.0.0", + "grouped-queue": "^1.1.0", + "istextorbinary": "^2.5.1", + "lodash": "^4.17.11", + "make-dir": "^3.0.0", + "mem-fs-editor": "^6.0.0", + "minimist": "^1.2.5", + "pretty-bytes": "^5.2.0", + "read-chunk": "^3.2.0", + "read-pkg-up": "^5.0.0", + "rimraf": "^2.6.3", + "run-async": "^2.0.0", + "semver": "^7.2.1", + "shelljs": "^0.8.3", + "text-table": "^0.2.0", + "through2": "^3.0.1", + "yeoman-environment": "^2.9.5" + }, + "dependencies": { + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "read-pkg-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-5.0.0.tgz", + "integrity": "sha512-XBQjqOBtTzyol2CpsQOw8LHV0XbDZVG7xMMjmXAJomlVY03WOBRmYgDJETlvcg0H63AJvPRwT7GFi5rvOzUOKg==", + "dev": true, + "requires": { + "find-up": "^3.0.0", + "read-pkg": "^5.0.0" + } + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true + }, + "through2": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", + "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", + "dev": true, + "requires": { + "readable-stream": "2 || 3" + } + } + } + }, + "yo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yo/-/yo-3.1.1.tgz", + "integrity": "sha512-GFg4QC1xi3gkbHGGUFme8/8XPg3kDISu/qJfx56X207yuv1FSevGY/eKuym7kh0bniCB4n3rseWW+QZXPH8LIw==", + "dev": true, + "requires": { + "async": "^2.6.1", + "chalk": "^2.4.1", + "cli-list": "^0.2.0", + "configstore": "^3.1.2", + "cross-spawn": "^6.0.5", + "figures": "^2.0.0", + "fullname": "^4.0.1", + "global-agent": "^2.0.0", + "global-tunnel-ng": "^2.7.1", + "got": "^8.3.2", + "humanize-string": "^1.0.2", + "inquirer": "^6.0.0", + "insight": "^0.10.3", + "lodash": "^4.17.15", + "meow": "^3.0.0", + "npm-keyword": "^5.0.0", + "open": "^6.3.0", + "package-json": "^5.0.0", + "parse-help": "^1.0.0", + "read-pkg-up": "^4.0.0", + "root-check": "^1.0.0", + "sort-on": "^3.0.0", + "string-length": "^2.0.0", + "tabtab": "^1.3.2", + "titleize": "^1.0.1", + "update-notifier": "^2.5.0", + "user-home": "^2.0.0", + "yeoman-character": "^1.0.0", + "yeoman-doctor": "^4.0.0", + "yeoman-environment": "^2.4.0", + "yosay": "^2.0.2" + }, + "dependencies": { + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + } + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "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" + } + }, + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "inquirer": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz", + "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==", + "dev": true, + "requires": { + "ansi-escapes": "^3.2.0", + "chalk": "^2.4.2", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^2.0.0", + "lodash": "^4.17.12", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^6.4.0", + "string-width": "^2.1.0", + "strip-ansi": "^5.1.0", + "through": "^2.3.6" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "dependencies": { + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + } + } + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "requires": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + }, + "dependencies": { + "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" + } + } + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "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-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" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "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": "4.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", + "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", + "dev": true, + "requires": { + "find-up": "^3.0.0", + "read-pkg": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + } + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + } + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + } + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "requires": { + "get-stdin": "^4.0.1" + } + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true + } + } + }, + "yosay": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/yosay/-/yosay-2.0.2.tgz", + "integrity": "sha512-avX6nz2esp7IMXGag4gu6OyQBsMh/SEn+ZybGu3yKPlOTE6z9qJrzG/0X5vCq/e0rPFy0CUYCze0G5hL310ibA==", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0", + "ansi-styles": "^3.0.0", + "chalk": "^1.0.0", + "cli-boxes": "^1.0.0", + "pad-component": "0.0.1", + "string-width": "^2.0.0", + "strip-ansi": "^3.0.0", + "taketalk": "^1.0.0", + "wrap-ansi": "^2.0.0" + }, + "dependencies": { + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + } + } + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "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" + }, + "dependencies": { + "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" + } + }, + "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" + } + } + } + } + } + } + } +} diff --git a/plugins/base/frontend/package.json b/plugins/base/frontend/package.json new file mode 100644 index 00000000..120d7f34 --- /dev/null +++ b/plugins/base/frontend/package.json @@ -0,0 +1,57 @@ +{ + "name": "search", + "version": "1.0.0", + "private": true, + "config": { + "components": "./src/main/components", + "dist": "./dist" + }, + "scripts": { + "build": "webpack --mode=production --devtool sourcemap", + "lint": "eslint . && npm run stylelint", + "stylelint": "stylelint --ignore-path .gitignore ./src/main/**/*.scss", + "start": "webpack-dev-server -d --history-api-fallback --inline --hot --colors --port 9010" + }, + "babel": { + "presets": [ + [ + "@jetbrains/jetbrains", + { + "useBuiltIns": "usage" + } + ] + ] + }, + "dependencies": { + "@babel/core": "^7.8.3", + "@jetbrains/babel-preset-jetbrains": "^2.1.4", + "@jetbrains/logos": "1.1.5", + "@jetbrains/ring-ui": "2.1.16", + "@types/node": "^12.12.36", + "@types/react": "^16.9.0", + "@types/react-dom": "^16.9.0", + "babel-loader": "^8.0.6", + "postcss-import": "^12.0.1", + "postcss-preset-env": "^6.7.0", + "react": "^16.12.0", + "react-dom": "^16.12.0", + "redbox-react": "^1.6.0", + "ts-loader": "^7.0.0", + "typescript": "^3.8.3", + "webpack": "^4.41.5", + "webpack-cli": "^3.3.10", + "webpack-dev-server": "^3.10.1" + }, + "devDependencies": { + "@jetbrains/stylelint-config": "^2.0.0", + "babel-eslint": "^10.0.3", + "eslint": "^6.8.0", + "sass": "^1.26.3", + "sass-loader": "^8.0.2", + "stylelint": "^13.3.2", + "yo": "^3.1.1" + }, + "engines": { + "node": ">=8.0.0" + } +} diff --git a/plugins/base/frontend/postcss.config.js b/plugins/base/frontend/postcss.config.js new file mode 100644 index 00000000..66c94ee0 --- /dev/null +++ b/plugins/base/frontend/postcss.config.js @@ -0,0 +1,16 @@ +module.exports = () => ({ + plugins: [ + require('postcss-import'), + require('postcss-preset-env')({ + features: { + stage: 3, // See https://cssdb.org/#staging-process + features: { + 'nesting-rules': true, + 'custom-properties': { + preserve: true + } + } + } + }) + ] +}); diff --git a/plugins/base/frontend/src/main/components/app/index.scss b/plugins/base/frontend/src/main/components/app/index.scss new file mode 100644 index 00000000..da5042b1 --- /dev/null +++ b/plugins/base/frontend/src/main/components/app/index.scss @@ -0,0 +1,27 @@ +@import "src/main/scss/index.scss"; + +html, +.app-root { + height: 100%; +} + +.search-root { + margin: 0; + padding: 0; + + background: var(--ring-content-background-color); + + font-family: var(--ring-font-family); + font-size: var(--ring-font-size); + line-height: var(--ring-line-height); +} + +.search-content { + padding-top: 24px; + margin: 0 41px; + position: absolute; + top: 0; + right: 0; + z-index: 8; + background-color: #f4f4f4 +} diff --git a/plugins/base/frontend/src/main/components/app/index.tsx b/plugins/base/frontend/src/main/components/app/index.tsx new file mode 100644 index 00000000..4081dec4 --- /dev/null +++ b/plugins/base/frontend/src/main/components/app/index.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import {WithFuzzySearchFilter} from '../search/search'; +import './index.scss'; + +const App: React.FC = () => { + return <div className="search-content"> + <WithFuzzySearchFilter/> + </div> +} + +export default App diff --git a/plugins/base/frontend/src/main/components/root.tsx b/plugins/base/frontend/src/main/components/root.tsx new file mode 100644 index 00000000..70ed9550 --- /dev/null +++ b/plugins/base/frontend/src/main/components/root.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import {render} from 'react-dom'; +import RedBox from 'redbox-react'; + +import App from "./app"; +import './app/index.scss'; + +const appEl = document.getElementById('searchBar'); +const rootEl = document.createElement('div'); + +let renderApp = () => { + render( + <App/>, + rootEl + ); +}; + +// @ts-ignore +if (module.hot) { + const renderAppHot = renderApp; + const renderError = (error: Error) => { + render( + <RedBox error={error}/>, + rootEl + ); + }; + + renderApp = () => { + try { + renderAppHot(); + } catch (error) { + renderError(error); + } + }; + + // @ts-ignore + module.hot.accept('./app', () => { + setTimeout(renderApp); + }); +} + +renderApp(); +appEl!.appendChild(rootEl); diff --git a/plugins/base/frontend/src/main/components/search/search.scss b/plugins/base/frontend/src/main/components/search/search.scss new file mode 100644 index 00000000..cc5a61ac --- /dev/null +++ b/plugins/base/frontend/src/main/components/search/search.scss @@ -0,0 +1,37 @@ +.search { + button { + border: none; + fill: #637282; + background: #F4F4F4; + + &:focus { + outline: none; + } + } +} + +.popup-wrapper { + min-width: calc(100% - 360px) !important; +} + +.indented { + text-indent: 10px; +} + +.disabled { + color: gray; +} + +.template-wrapper { + display: grid; + grid-template-columns: auto auto; +} + +.template-name { + justify-self: start; +} + +.template-description { + color: gray; + justify-self: end; +}
\ No newline at end of file diff --git a/plugins/base/frontend/src/main/components/search/search.tsx b/plugins/base/frontend/src/main/components/search/search.tsx new file mode 100644 index 00000000..c7b36654 --- /dev/null +++ b/plugins/base/frontend/src/main/components/search/search.tsx @@ -0,0 +1,75 @@ +import React, {useCallback, useState} from 'react'; +import {Select} from '@jetbrains/ring-ui'; +import {List} from '@jetbrains/ring-ui'; +import '@jetbrains/ring-ui/components/input-size/input-size.scss'; +import './search.scss'; +import {IWindow, Option, Props, Page} from "./types"; + +const WithFuzzySearchFilterComponent: React.FC<Props> = ({data}: Props) => { + const [selected, onSelected] = useState<Option>(data[0]); + const onChangeSelected = useCallback( + (option: Option) => { + window.location.replace(`${(window as IWindow).pathToRoot}${option.location}?query=${option.name}`) + onSelected(option); + }, + [data] + ); + + return ( + <div className="search-container"> + <div className="search"> + <Select + selectedLabel="Search" + label="Please type page name" + filter={{fuzzy: true}} + type={Select.Type.CUSTOM} + clear + selected={selected} + data={data} + popupClassName={"popup-wrapper"} + onSelect={onChangeSelected} + customAnchor={({wrapperProps, buttonProps, popup}) => ( + <span {...wrapperProps}> + <button type="button" {...buttonProps}> + <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"> + <path d="M19.64 18.36l-6.24-6.24a7.52 7.52 0 1 0-1.28 1.28l6.24 6.24zM7.5 13.4a5.9 5.9 0 1 1 5.9-5.9 5.91 5.91 0 0 1-5.9 5.9z"/> + </svg> + </button> + {popup} + </span> + )} + /> + </div> + </div> + ) +} + +const templateGenerator = (page:Page) => { + let classGenerator = (page:Page) => { + let classes = "" + if(page.level !== undefined) classes = classes + " indented" + if(page.disabled) classes = classes + " disabled" + return classes + } + return <div className="template-wrapper"> + <span className= {classGenerator(page)}>{page.name}</span> + <span className="template-description">{page.description}</span> + </div> +} + +export const WithFuzzySearchFilter = () => { + let data: Option[] = []; + const pages = (window as IWindow).pages; + if (pages) { + data = pages.map((page, i) => ({ + ...page, + label: page.searchKey, + key: i + 1, + type: page.kind, + template: templateGenerator(page), + rgItemType: List.ListProps.Type.CUSTOM + })); + } + + return <WithFuzzySearchFilterComponent data={data}/>; +}; diff --git a/plugins/base/frontend/src/main/components/search/types.ts b/plugins/base/frontend/src/main/components/search/types.ts new file mode 100644 index 00000000..881a16d8 --- /dev/null +++ b/plugins/base/frontend/src/main/components/search/types.ts @@ -0,0 +1,31 @@ +export type Page = { + name: string; + kind: string; + location: string; + searchKey: string; + level: number; + index: string; + description: string; + disabled: boolean; +} + +export type Option = Page & { + label: string; + key: number; + location: string; + name: string; +} + +export type IWindow = typeof window & { + pathToRoot: string + pages: Page[] +} + +export type Props = { + data: Option[] +}; + + +export type State = { + selected: any +} diff --git a/plugins/base/frontend/src/main/scss/index.scss b/plugins/base/frontend/src/main/scss/index.scss new file mode 100644 index 00000000..74af970d --- /dev/null +++ b/plugins/base/frontend/src/main/scss/index.scss @@ -0,0 +1 @@ +@import "~@jetbrains/ring-ui/components/global/variables.css"; diff --git a/plugins/base/frontend/src/main/types/@jetbrains/index.d.ts b/plugins/base/frontend/src/main/types/@jetbrains/index.d.ts new file mode 100644 index 00000000..1dc9983c --- /dev/null +++ b/plugins/base/frontend/src/main/types/@jetbrains/index.d.ts @@ -0,0 +1,3 @@ +declare module '@jetbrains/ring-ui' { + export const Select: any; +} diff --git a/plugins/base/frontend/stylelint.config.js b/plugins/base/frontend/stylelint.config.js new file mode 100644 index 00000000..02b3f4ac --- /dev/null +++ b/plugins/base/frontend/stylelint.config.js @@ -0,0 +1,4 @@ +module.exports = { + extends: '@jetbrains/stylelint-config', + rules: {} +}; diff --git a/plugins/base/frontend/tsconfig.json b/plugins/base/frontend/tsconfig.json new file mode 100644 index 00000000..4cc8fd37 --- /dev/null +++ b/plugins/base/frontend/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "sourceMap": true, + "target": "es5", + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "noImplicitAny": true, + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react", + "typeRoots": [ + "src/main/types" + ] + }, + "include": [ + "./node_modules/@types/node/globals.d.ts" + ] +} diff --git a/plugins/base/frontend/webpack.config.js b/plugins/base/frontend/webpack.config.js new file mode 100644 index 00000000..559f5792 --- /dev/null +++ b/plugins/base/frontend/webpack.config.js @@ -0,0 +1,64 @@ +const {join, resolve} = require('path'); + +const ringUiWebpackConfig = require('@jetbrains/ring-ui/webpack.config'); + +const pkgConfig = require('./package.json').config; + +const componentsPath = join(__dirname, pkgConfig.components); + +// Patch @jetbrains/ring-ui svg-sprite-loader config +ringUiWebpackConfig.loaders.svgInlineLoader.include.push( + require('@jetbrains/logos'), + require('@jetbrains/icons') +); + +const webpackConfig = () => ({ + entry: `${componentsPath}/root.tsx`, + resolve: { + mainFields: ['module', 'browser', 'main'], + extensions: ['.tsx', '.ts', '.js'], + alias: { + react: resolve('./node_modules/react'), + 'react-dom': resolve('./node_modules/react-dom'), + '@jetbrains/ring-ui': resolve('./node_modules/@jetbrains/ring-ui') + } + }, + output: { + path: resolve(__dirname, pkgConfig.dist), + filename: '[name].js', + publicPath: '', + devtoolModuleFilenameTemplate: '/[absolute-resource-path]' + }, + module: { + rules: [ + ...ringUiWebpackConfig.config.module.rules, + { + test: /\.s[ac]ss$/i, + use: [ + 'style-loader', + 'css-loader', + 'sass-loader', + ], + include: componentsPath, + exclude: ringUiWebpackConfig.componentsPath, + }, + { + test: /\.tsx?$/, + use: [ + { + loader: 'ts-loader', + options: { + transpileOnly: true + } + } + ] + } + ] + }, + plugins: [], + output: { + path: __dirname + '/dist/' + } +}); + +module.exports = webpackConfig; diff --git a/plugins/base/src/main/kotlin/DokkaBase.kt b/plugins/base/src/main/kotlin/DokkaBase.kt new file mode 100644 index 00000000..6586f2dc --- /dev/null +++ b/plugins/base/src/main/kotlin/DokkaBase.kt @@ -0,0 +1,225 @@ +@file:Suppress("unused") + +package org.jetbrains.dokka.base + +import org.jetbrains.dokka.CoreExtensions +import org.jetbrains.dokka.analysis.KotlinAnalysis +import org.jetbrains.dokka.base.allModulePage.MultimodulePageCreator +import org.jetbrains.dokka.base.renderers.* +import org.jetbrains.dokka.base.renderers.html.* +import org.jetbrains.dokka.base.signatures.KotlinSignatureProvider +import org.jetbrains.dokka.base.signatures.SignatureProvider +import org.jetbrains.dokka.base.resolvers.external.* +import org.jetbrains.dokka.base.resolvers.local.DefaultLocationProviderFactory +import org.jetbrains.dokka.base.resolvers.local.LocationProviderFactory +import org.jetbrains.dokka.base.transformers.documentables.* +import org.jetbrains.dokka.base.transformers.documentables.DefaultDocumentableMerger +import org.jetbrains.dokka.base.transformers.documentables.ModuleAndPackageDocumentationTransformer +import org.jetbrains.dokka.base.transformers.documentables.ReportUndocumentedTransformer +import org.jetbrains.dokka.base.transformers.pages.annotations.SinceKotlinTransformer +import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter +import org.jetbrains.dokka.base.transformers.pages.comments.DocTagToContentConverter +import org.jetbrains.dokka.base.transformers.pages.merger.FallbackPageMergerStrategy +import org.jetbrains.dokka.base.transformers.pages.merger.PageMerger +import org.jetbrains.dokka.base.transformers.pages.merger.PageMergerStrategy +import org.jetbrains.dokka.base.transformers.pages.merger.SameMethodNamePageMergerStrategy +import org.jetbrains.dokka.base.transformers.pages.samples.DefaultSamplesTransformer +import org.jetbrains.dokka.base.transformers.pages.sourcelinks.SourceLinksTransformer +import org.jetbrains.dokka.base.translators.descriptors.DefaultDescriptorToDocumentableTranslator +import org.jetbrains.dokka.base.translators.documentables.DefaultDocumentableToPageTranslator +import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder +import org.jetbrains.dokka.base.translators.psi.DefaultPsiToDocumentableTranslator +import org.jetbrains.dokka.plugability.DokkaPlugin +import org.jetbrains.dokka.transformers.pages.PageTransformer + +class DokkaBase : DokkaPlugin() { + val pageMergerStrategy by extensionPoint<PageMergerStrategy>() + val commentsToContentConverter by extensionPoint<CommentsToContentConverter>() + val signatureProvider by extensionPoint<SignatureProvider>() + val locationProviderFactory by extensionPoint<LocationProviderFactory>() + val externalLocationProviderFactory by extensionPoint<ExternalLocationProviderFactory>() + val outputWriter by extensionPoint<OutputWriter>() + val htmlPreprocessors by extensionPoint<PageTransformer>() + val kotlinAnalysis by extensionPoint<KotlinAnalysis>() + val tabSortingStrategy by extensionPoint<TabSortingStrategy>() + + + val descriptorToDocumentableTranslator by extending { + CoreExtensions.sourceToDocumentableTranslator providing { ctx -> + DefaultDescriptorToDocumentableTranslator(ctx.single(kotlinAnalysis)) + } + } + + val psiToDocumentableTranslator by extending { + CoreExtensions.sourceToDocumentableTranslator providing { ctx -> + DefaultPsiToDocumentableTranslator(ctx.single(kotlinAnalysis)) + } + } + + val documentableMerger by extending { + CoreExtensions.documentableMerger with DefaultDocumentableMerger + } + + val deprecatedDocumentableFilter by extending { + CoreExtensions.preMergeDocumentableTransformer providing ::DeprecatedDocumentableFilterTransformer + } + + val documentableVisbilityFilter by extending { + CoreExtensions.preMergeDocumentableTransformer providing ::DocumentableVisibilityFilterTransformer + } + + val emptyPackagesFilter by extending { + CoreExtensions.preMergeDocumentableTransformer providing ::EmptyPackagesFilterTransformer order { + after(deprecatedDocumentableFilter, documentableVisbilityFilter) + } + } + + val actualTypealiasAdder by extending { + CoreExtensions.documentableTransformer with ActualTypealiasAdder() + } + + val modulesAndPackagesDocumentation by extending { + CoreExtensions.preMergeDocumentableTransformer providing { ctx -> + ModuleAndPackageDocumentationTransformer(ctx, ctx.single(kotlinAnalysis)) + } + } + + val kotlinSignatureProvider by extending { + signatureProvider providing { ctx -> + KotlinSignatureProvider(ctx.single(commentsToContentConverter), ctx.logger) + } + } + + val sinceKotlinTransformer by extending { + CoreExtensions.documentableTransformer providing ::SinceKotlinTransformer + } + + val inheritorsExtractor by extending { + CoreExtensions.documentableTransformer with InheritorsExtractorTransformer() + } + + + val undocumentedCodeReporter by extending { + CoreExtensions.documentableTransformer with ReportUndocumentedTransformer() + } + + val extensionsExtractor by extending { + CoreExtensions.documentableTransformer with ExtensionExtractorTransformer() + } + + val documentableToPageTranslator by extending { + CoreExtensions.documentableToPageTranslator providing { ctx -> + DefaultDocumentableToPageTranslator( + ctx.single(commentsToContentConverter), + ctx.single(signatureProvider), + ctx.logger + ) + } + } + + val docTagToContentConverter by extending { + commentsToContentConverter with DocTagToContentConverter + } + + val pageMerger by extending { + CoreExtensions.pageTransformer providing { ctx -> PageMerger(ctx[pageMergerStrategy]) } + } + + val fallbackMerger by extending { + pageMergerStrategy providing { ctx -> FallbackPageMergerStrategy(ctx.logger) } + } + + val sameMethodNameMerger by extending { + pageMergerStrategy providing { ctx -> SameMethodNamePageMergerStrategy(ctx.logger) } order { + before(fallbackMerger) + } + } + + val defaultTabSortingStrategy by extending { + tabSortingStrategy with DefaultTabSortingStrategy() + } + + val htmlRenderer by extending { + CoreExtensions.renderer providing ::HtmlRenderer + } + + + val defaultKotlinAnalysis by extending { + kotlinAnalysis providing { ctx -> KotlinAnalysis(ctx) } + } + + val locationProvider by extending { + locationProviderFactory providing ::DefaultLocationProviderFactory + } + + val javadocLocationProvider by extending { + externalLocationProviderFactory with JavadocExternalLocationProviderFactory() + } + + val dokkaLocationProvider by extending { + externalLocationProviderFactory with DokkaExternalLocationProviderFactory() + } + + val fileWriter by extending { + outputWriter providing ::FileWriter + } + + val rootCreator by extending { + htmlPreprocessors with RootCreator + } + + val defaultSamplesTransformer by extending { + CoreExtensions.pageTransformer providing ::DefaultSamplesTransformer order { + before(pageMerger) + } + } + + val sourceLinksTransformer by extending { + htmlPreprocessors providing { + SourceLinksTransformer( + it, + PageContentBuilder( + it.single(commentsToContentConverter), + it.single(signatureProvider), + it.logger + ) + ) + } order { after(rootCreator) } + } + + val navigationPageInstaller by extending { + htmlPreprocessors with NavigationPageInstaller order { after(rootCreator) } + } + + val searchPageInstaller by extending { + htmlPreprocessors with SearchPageInstaller order { after(rootCreator) } + } + + val resourceInstaller by extending { + htmlPreprocessors with ResourceInstaller order { after(rootCreator) } + } + + val styleAndScriptsAppender by extending { + htmlPreprocessors with StyleAndScriptsAppender order { after(rootCreator) } + } + + val packageListCreator by extending { + htmlPreprocessors providing { + PackageListCreator( + it, + "html", + "html" + ) + } order { after(rootCreator) } + } + + val sourcesetDependencyAppender by extending { + htmlPreprocessors providing ::SourcesetDependencyAppender order { after(rootCreator) } + } + + val allModulePageCreators by extending { + CoreExtensions.allModulePageCreator providing { + MultimodulePageCreator(it) + } + } +} diff --git a/plugins/base/src/main/kotlin/allModulePage/MultimodulePageCreator.kt b/plugins/base/src/main/kotlin/allModulePage/MultimodulePageCreator.kt new file mode 100644 index 00000000..de2242f2 --- /dev/null +++ b/plugins/base/src/main/kotlin/allModulePage/MultimodulePageCreator.kt @@ -0,0 +1,81 @@ +package org.jetbrains.dokka.base.allModulePage + +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.DokkaException +import org.jetbrains.dokka.base.DokkaBase +import org.jetbrains.dokka.base.resolvers.local.MultimoduleLocationProvider.Companion.MULTIMODULE_PACKAGE_PLACEHOLDER +import org.jetbrains.dokka.base.transformers.pages.comments.DocTagToContentConverter +import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.doc.DocumentationNode +import org.jetbrains.dokka.model.doc.P +import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.base.parsers.MarkdownParser +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.querySingle +import org.jetbrains.dokka.transformers.pages.PageCreator +import org.jetbrains.dokka.utilities.DokkaLogger +import java.io.File + +class MultimodulePageCreator( + val context: DokkaContext +) : PageCreator { + private val logger: DokkaLogger = context.logger + + override fun invoke(): RootPageNode { + val parser = MarkdownParser(logger = logger) + val modules = context.configuration.modules + modules.forEach(::throwOnMissingModuleDocFile) + + val commentsConverter = context.plugin(DokkaBase::class)?.querySingle { commentsToContentConverter } + val signatureProvider = context.plugin(DokkaBase::class)?.querySingle { signatureProvider } + if (commentsConverter == null || signatureProvider == null) + throw IllegalStateException("Both comments converter and signature provider must not be null") + + val sourceSetData = emptySet<DokkaSourceSet>() + val builder = PageContentBuilder(commentsConverter, signatureProvider, context.logger) + val contentNode = builder.contentFor( + dri = DRI(MULTIMODULE_PACKAGE_PLACEHOLDER), + kind = ContentKind.Cover, + sourceSets = sourceSetData + ) { + header(2, "All modules:") + table(styles = setOf(MultimoduleTable)) { + modules.mapNotNull { module -> + val paragraph = module.docFile.let(::File).readText().let { parser.parse(it).firstParagraph() } + paragraph?.let { + val dri = DRI(packageName = MULTIMODULE_PACKAGE_PLACEHOLDER, classNames = module.name) + val dci = DCI(setOf(dri), ContentKind.Main) + val header = + ContentHeader(listOf(linkNode(module.name, dri)), 2, dci, emptySet(), emptySet()) + val content = ContentGroup( + DocTagToContentConverter.buildContent(it, dci, emptySet()), + dci, + emptySet(), + emptySet() + ) + ContentGroup(listOf(header, content), dci, emptySet(), emptySet()) + } + } + } + } + return MultimoduleRootPageNode( + "Modules", + setOf(DRI(packageName = MULTIMODULE_PACKAGE_PLACEHOLDER, classNames = "allModules")), + contentNode + ) + } + + private fun throwOnMissingModuleDocFile(module: DokkaConfiguration.DokkaModuleDescription) { + val docFile = File(module.docFile) + if (!docFile.exists() || !docFile.isFile) { + throw DokkaException( + "Missing documentation file for module ${module.name}: ${docFile.absolutePath}" + ) + } + } + + private fun DocumentationNode.firstParagraph() = + this.children.flatMap { it.root.children }.filterIsInstance<P>().firstOrNull() +} diff --git a/plugins/base/src/main/kotlin/parsers/HtmlParser.kt b/plugins/base/src/main/kotlin/parsers/HtmlParser.kt new file mode 100644 index 00000000..ece3cf24 --- /dev/null +++ b/plugins/base/src/main/kotlin/parsers/HtmlParser.kt @@ -0,0 +1,89 @@ +package org.jetbrains.dokka.base.parsers + +import org.jetbrains.dokka.model.doc.* +import org.jetbrains.dokka.base.parsers.factories.DocTagsFromStringFactory +import org.jsoup.Jsoup +import org.jsoup.nodes.Node +import org.jsoup.select.NodeFilter +import org.jsoup.select.NodeTraversor + +class HtmlParser : Parser() { + + inner class NodeFilterImpl : NodeFilter { + + private val nodesCache: MutableMap<Int, MutableList<DocTag>> = mutableMapOf() + private var currentDepth = 0 + + fun collect(): DocTag = nodesCache[currentDepth]!![0] + + override fun tail(node: Node?, depth: Int): NodeFilter.FilterResult { + val nodeName = node!!.nodeName() + val nodeAttributes = node.attributes() + + if(nodeName in listOf("#document", "html", "head")) + return NodeFilter.FilterResult.CONTINUE + + val body: String + val params: Map<String, String> + + + if(nodeName != "#text") { + body = "" + params = nodeAttributes.map { it.key to it.value }.toMap() + } else { + body = nodeAttributes["#text"] + params = emptyMap() + } + + val docNode = if(depth < currentDepth) { + DocTagsFromStringFactory.getInstance(nodeName, nodesCache.getOrDefault(currentDepth, mutableListOf()).toList(), params, body).also { + nodesCache[currentDepth] = mutableListOf() + currentDepth = depth + } + } else { + DocTagsFromStringFactory.getInstance(nodeName, emptyList(), params, body) + } + + nodesCache.getOrDefault(depth, mutableListOf()) += docNode + return NodeFilter.FilterResult.CONTINUE + } + + override fun head(node: Node?, depth: Int): NodeFilter.FilterResult { + + val nodeName = node!!.nodeName() + + if(currentDepth < depth) { + currentDepth = depth + nodesCache[currentDepth] = mutableListOf() + } + + if(nodeName in listOf("#document", "html", "head")) + return NodeFilter.FilterResult.CONTINUE + + return NodeFilter.FilterResult.CONTINUE + } + } + + + private fun htmlToDocNode(string: String): DocTag { + val document = Jsoup.parse(string) + val nodeFilterImpl = NodeFilterImpl() + NodeTraversor.filter(nodeFilterImpl, document.root()) + return nodeFilterImpl.collect() + } + + private fun replaceLinksWithHrefs(javadoc: String): String = Regex("\\{@link .*?}").replace(javadoc) { + val split = it.value.dropLast(1).split(" ") + if(split.size !in listOf(2, 3)) + return@replace it.value + if(split.size == 3) + return@replace "<documentationlink href=\"${split[1]}\">${split[2]}</documentationlink>" + else + return@replace "<documentationlink href=\"${split[1]}\">${split[1]}</documentationlink>" + } + + override fun parseStringToDocNode(extractedString: String) = htmlToDocNode(extractedString) + override fun preparse(text: String) = replaceLinksWithHrefs(text) +} + + diff --git a/plugins/base/src/main/kotlin/parsers/MarkdownParser.kt b/plugins/base/src/main/kotlin/parsers/MarkdownParser.kt new file mode 100644 index 00000000..05eb71ab --- /dev/null +++ b/plugins/base/src/main/kotlin/parsers/MarkdownParser.kt @@ -0,0 +1,420 @@ +package org.jetbrains.dokka.base.parsers + +import com.intellij.psi.PsiElement +import org.jetbrains.dokka.model.doc.* +import org.intellij.markdown.MarkdownElementTypes +import org.intellij.markdown.MarkdownTokenTypes +import org.intellij.markdown.ast.ASTNode +import org.intellij.markdown.ast.CompositeASTNode +import org.intellij.markdown.ast.LeafASTNode +import org.intellij.markdown.ast.impl.ListItemCompositeNode +import org.intellij.markdown.flavours.gfm.GFMElementTypes +import org.intellij.markdown.flavours.gfm.GFMFlavourDescriptor +import org.intellij.markdown.flavours.gfm.GFMTokenTypes +import org.jetbrains.dokka.analysis.DokkaResolutionFacade +import org.jetbrains.dokka.analysis.from +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.base.parsers.factories.DocTagsFromIElementFactory +import org.jetbrains.dokka.utilities.DokkaLogger +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.idea.kdoc.resolveKDocLink +import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag +import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection +import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag +import java.net.MalformedURLException +import org.intellij.markdown.parser.MarkdownParser as IntellijMarkdownParser + +class MarkdownParser( + private val resolutionFacade: DokkaResolutionFacade? = null, + private val declarationDescriptor: DeclarationDescriptor? = null, + private val logger: DokkaLogger +) : Parser() { + + inner class MarkdownVisitor(val text: String, val destinationLinksMap: Map<String, String>) { + + private fun headersHandler(node: ASTNode): DocTag = + DocTagsFromIElementFactory.getInstance( + node.type, + visitNode(node.children.find { it.type == MarkdownTokenTypes.ATX_CONTENT } + ?: throw IllegalStateException("Wrong AST Tree. ATX Header does not contain expected content")).children + ) + + private fun horizontalRulesHandler(node: ASTNode): DocTag = + DocTagsFromIElementFactory.getInstance(MarkdownTokenTypes.HORIZONTAL_RULE) + + private fun emphasisHandler(node: ASTNode): DocTag = + DocTagsFromIElementFactory.getInstance( + node.type, + children = listOf(visitNode(node.children[node.children.size / 2])) + ) + + private fun blockquotesHandler(node: ASTNode): DocTag = + DocTagsFromIElementFactory.getInstance( + node.type, children = node.children + .filterIsInstance<CompositeASTNode>() + .evaluateChildren() + ) + + private fun listsHandler(node: ASTNode): DocTag { + + val children = node.children.filterIsInstance<ListItemCompositeNode>().flatMap { + if (it.children.last().type in listOf( + MarkdownElementTypes.ORDERED_LIST, + MarkdownElementTypes.UNORDERED_LIST + ) + ) { + val nestedList = it.children.last() + (it.children as MutableList).removeAt(it.children.lastIndex) + listOf(it, nestedList) + } else + listOf(it) + } + + return DocTagsFromIElementFactory.getInstance( + node.type, + children = + children + .map { + if (it.type == MarkdownElementTypes.LIST_ITEM) + DocTagsFromIElementFactory.getInstance( + it.type, + children = it + .children + .filterIsInstance<CompositeASTNode>() + .evaluateChildren() + ) + else + visitNode(it) + }, + params = + if (node.type == MarkdownElementTypes.ORDERED_LIST) { + val listNumberNode = node.children.first().children.first() + mapOf( + "start" to text.substring( + listNumberNode.startOffset, + listNumberNode.endOffset + ).trim().dropLast(1) + ) + } else + emptyMap() + ) + } + + private fun resolveDRI(mdLink: String): DRI? = + mdLink + .removePrefix("[") + .removeSuffix("]") + .let { link -> + try { + java.net.URL(link) + null + } catch (e: MalformedURLException) { + try { + if (resolutionFacade != null && declarationDescriptor != null) { + resolveKDocLink( + resolutionFacade.resolveSession.bindingContext, + resolutionFacade, + declarationDescriptor, + null, + link.split('.') + ).minByOrNull { it is ClassDescriptor }?.let { DRI.from(it) } + } else null + } catch (e1: IllegalArgumentException) { + logger.warn("Couldn't resolve link for $mdLink") + null + } + } + } + + private fun referenceLinksHandler(node: ASTNode): DocTag { + val linkLabel = node.children.find { it.type == MarkdownElementTypes.LINK_LABEL } + ?: throw IllegalStateException("Wrong AST Tree. Reference link does not contain expected content") + val linkText = node.children.findLast { it.type == MarkdownElementTypes.LINK_TEXT } ?: linkLabel + + val linkKey = text.substring(linkLabel.startOffset, linkLabel.endOffset) + + val link = destinationLinksMap[linkKey.toLowerCase()] ?: linkKey + + return linksHandler(linkText, link) + } + + private fun inlineLinksHandler(node: ASTNode): DocTag { + val linkText = node.children.find { it.type == MarkdownElementTypes.LINK_TEXT } + ?: throw IllegalStateException("Wrong AST Tree. Inline link does not contain expected content") + val linkDestination = node.children.find { it.type == MarkdownElementTypes.LINK_DESTINATION } + ?: throw IllegalStateException("Wrong AST Tree. Inline link does not contain expected content") + val linkTitle = node.children.find { it.type == MarkdownElementTypes.LINK_TITLE } + + val link = text.substring(linkDestination.startOffset, linkDestination.endOffset) + + return linksHandler(linkText, link, linkTitle) + } + + private fun autoLinksHandler(node: ASTNode): DocTag { + val link = text.substring(node.startOffset + 1, node.endOffset - 1) + + return linksHandler(node, link) + } + + private fun linksHandler(linkText: ASTNode, link: String, linkTitle: ASTNode? = null): DocTag { + val dri: DRI? = resolveDRI(link) + val params = if (linkTitle == null) + mapOf("href" to link) + else + mapOf("href" to link, "title" to text.substring(linkTitle.startOffset + 1, linkTitle.endOffset - 1)) + + return DocTagsFromIElementFactory.getInstance( + MarkdownElementTypes.INLINE_LINK, + params = params, + children = linkText.children.drop(1).dropLast(1).evaluateChildren(), + dri = dri + ) + } + + private fun imagesHandler(node: ASTNode): DocTag { + val linkNode = + node.children.last().children.find { it.type == MarkdownElementTypes.LINK_LABEL }!!.children[1] + val link = text.substring(linkNode.startOffset, linkNode.endOffset) + val src = mapOf("src" to link) + return DocTagsFromIElementFactory.getInstance( + node.type, + params = src, + children = listOf(visitNode(node.children.last().children.find { it.type == MarkdownElementTypes.LINK_TEXT }!!)) + ) + } + + private fun codeSpansHandler(node: ASTNode): DocTag = + DocTagsFromIElementFactory.getInstance( + node.type, + children = listOf( + DocTagsFromIElementFactory.getInstance( + MarkdownTokenTypes.TEXT, + body = text.substring(node.startOffset + 1, node.endOffset - 1).replace('\n', ' ').trimIndent() + ) + + ) + ) + + private fun codeFencesHandler(node: ASTNode): DocTag = + DocTagsFromIElementFactory.getInstance( + node.type, + children = node + .children + .dropWhile { it.type != MarkdownTokenTypes.CODE_FENCE_CONTENT } + .dropLastWhile { it.type != MarkdownTokenTypes.CODE_FENCE_CONTENT } + .map { + if (it.type == MarkdownTokenTypes.EOL) + LeafASTNode(MarkdownTokenTypes.HARD_LINE_BREAK, 0, 0) + else + it + }.evaluateChildren(), + params = node + .children + .find { it.type == MarkdownTokenTypes.FENCE_LANG } + ?.let { mapOf("lang" to text.substring(it.startOffset, it.endOffset)) } + ?: emptyMap() + ) + + private fun codeBlocksHandler(node: ASTNode): DocTag = + DocTagsFromIElementFactory.getInstance(node.type, children = node.children.evaluateChildren()) + + private fun defaultHandler(node: ASTNode): DocTag = + DocTagsFromIElementFactory.getInstance( + MarkdownElementTypes.PARAGRAPH, + children = node.children.evaluateChildren() + ) + + fun visitNode(node: ASTNode): DocTag = + when (node.type) { + MarkdownElementTypes.ATX_1, + MarkdownElementTypes.ATX_2, + MarkdownElementTypes.ATX_3, + MarkdownElementTypes.ATX_4, + MarkdownElementTypes.ATX_5, + MarkdownElementTypes.ATX_6 -> headersHandler(node) + MarkdownTokenTypes.HORIZONTAL_RULE -> horizontalRulesHandler(node) + MarkdownElementTypes.STRONG, + MarkdownElementTypes.EMPH -> emphasisHandler(node) + MarkdownElementTypes.FULL_REFERENCE_LINK, + MarkdownElementTypes.SHORT_REFERENCE_LINK -> referenceLinksHandler(node) + MarkdownElementTypes.INLINE_LINK -> inlineLinksHandler(node) + MarkdownElementTypes.AUTOLINK -> autoLinksHandler(node) + MarkdownElementTypes.BLOCK_QUOTE -> blockquotesHandler(node) + MarkdownElementTypes.UNORDERED_LIST, + MarkdownElementTypes.ORDERED_LIST -> listsHandler(node) + MarkdownElementTypes.CODE_BLOCK -> codeBlocksHandler(node) + MarkdownElementTypes.CODE_FENCE -> codeFencesHandler(node) + MarkdownElementTypes.CODE_SPAN -> codeSpansHandler(node) + MarkdownElementTypes.IMAGE -> imagesHandler(node) + MarkdownTokenTypes.HARD_LINE_BREAK -> DocTagsFromIElementFactory.getInstance(node.type) + MarkdownTokenTypes.CODE_FENCE_CONTENT, + MarkdownTokenTypes.CODE_LINE, + MarkdownTokenTypes.TEXT -> DocTagsFromIElementFactory.getInstance( + MarkdownTokenTypes.TEXT, + body = text + .substring(node.startOffset, node.endOffset).transform() + ) + MarkdownElementTypes.MARKDOWN_FILE -> if (node.children.size == 1) visitNode(node.children.first()) else defaultHandler( + node + ) + GFMElementTypes.STRIKETHROUGH -> DocTagsFromIElementFactory.getInstance( + GFMElementTypes.STRIKETHROUGH, + body = text + .substring(node.startOffset, node.endOffset).transform() + ) + GFMElementTypes.TABLE -> DocTagsFromIElementFactory.getInstance( + GFMElementTypes.TABLE, + children = node.children.filterTabSeparators().evaluateChildren() + ) + GFMElementTypes.HEADER -> DocTagsFromIElementFactory.getInstance( + GFMElementTypes.HEADER, + children = node.children.filterTabSeparators().evaluateChildren() + ) + GFMElementTypes.ROW -> DocTagsFromIElementFactory.getInstance( + GFMElementTypes.ROW, + children = node.children.filterTabSeparators().evaluateChildren() + ) + else -> defaultHandler(node) + } + + private fun List<ASTNode>.filterTabSeparators() = + this.filterNot { it.type == GFMTokenTypes.TABLE_SEPARATOR } + + private fun List<ASTNode>.evaluateChildren(): List<DocTag> = + this.removeUselessTokens().mergeLeafASTNodes().map { visitNode(it) } + + private fun List<ASTNode>.removeUselessTokens(): List<ASTNode> = + this.filterIndexed { index, node -> + !(node.type == MarkdownElementTypes.LINK_DEFINITION || ( + node.type == MarkdownTokenTypes.EOL && + this.getOrNull(index - 1)?.type == MarkdownTokenTypes.HARD_LINE_BREAK + )) + } + + private val notLeafNodes = listOf(MarkdownTokenTypes.HORIZONTAL_RULE, MarkdownTokenTypes.HARD_LINE_BREAK) + + private fun List<ASTNode>.isNotLeaf(index: Int): Boolean = + if (index in 0..this.lastIndex) + (this[index] is CompositeASTNode) || this[index].type in notLeafNodes + else + false + + private fun List<ASTNode>.mergeLeafASTNodes(): List<ASTNode> { + val children: MutableList<ASTNode> = mutableListOf() + var index = 0 + while (index <= this.lastIndex) { + if (this.isNotLeaf(index)) { + children += this[index] + } else { + val startOffset = this[index].startOffset + while (index < this.lastIndex) { + if (this.isNotLeaf(index + 1) || this[index + 1].startOffset != this[index].endOffset) { + val endOffset = this[index].endOffset + if (text.substring(startOffset, endOffset).transform().trim().isNotEmpty()) + children += LeafASTNode(MarkdownTokenTypes.TEXT, startOffset, endOffset) + break + } + index++ + } + if (index == this.lastIndex) { + val endOffset = this[index].endOffset + if (text.substring(startOffset, endOffset).transform().trim().isNotEmpty()) + children += LeafASTNode(MarkdownTokenTypes.TEXT, startOffset, endOffset) + } + } + index++ + } + return children + } + + private fun String.transform() = this + .replace(Regex("\n\n+"), "") // Squashing new lines between paragraphs + .replace(Regex("\n"), " ") + .replace(Regex(" >+ +"), " ") // Replacement used in blockquotes, get rid of garbage + } + + + private fun getAllDestinationLinks(text: String, node: ASTNode): List<Pair<String, String>> = + node.children + .filter { it.type == MarkdownElementTypes.LINK_DEFINITION } + .map { + text.substring(it.children[0].startOffset, it.children[0].endOffset).toLowerCase() to + text.substring(it.children[2].startOffset, it.children[2].endOffset) + } + + node.children.filterIsInstance<CompositeASTNode>().flatMap { getAllDestinationLinks(text, it) } + + + private fun markdownToDocNode(text: String): DocTag { + + val flavourDescriptor = GFMFlavourDescriptor() + val markdownAstRoot: ASTNode = IntellijMarkdownParser(flavourDescriptor).buildMarkdownTreeFromString(text) + + return MarkdownVisitor(text, getAllDestinationLinks(text, markdownAstRoot).toMap()).visitNode(markdownAstRoot) + } + + override fun parseStringToDocNode(extractedString: String) = markdownToDocNode(extractedString) + override fun preparse(text: String) = text + + private fun findParent(kDoc: PsiElement): PsiElement = + if (kDoc is KDocSection) findParent(kDoc.parent) else kDoc + + private fun getAllKDocTags(kDocImpl: PsiElement): List<KDocTag> = + kDocImpl.children.filterIsInstance<KDocTag>().filterNot { it is KDocSection } + kDocImpl.children.flatMap { + getAllKDocTags( + it + ) + } + + fun parseFromKDocTag(kDocTag: KDocTag?): DocumentationNode { + return if (kDocTag == null) + DocumentationNode(emptyList()) + else + DocumentationNode( + (listOf(kDocTag) + getAllKDocTags(findParent(kDocTag))).map { + when (it.knownTag) { + null -> if (it.name == null) Description(parseStringToDocNode(it.getContent())) else CustomTagWrapper( + parseStringToDocNode(it.getContent()), + it.name!! + ) + KDocKnownTag.AUTHOR -> Author(parseStringToDocNode(it.getContent())) + KDocKnownTag.THROWS -> Throws( + parseStringToDocNode(it.getContent()), + it.getSubjectName().orEmpty() + ) + KDocKnownTag.EXCEPTION -> Throws( + parseStringToDocNode(it.getContent()), + it.getSubjectName().orEmpty() + ) + KDocKnownTag.PARAM -> Param( + parseStringToDocNode(it.getContent()), + it.getSubjectName().orEmpty() + ) + KDocKnownTag.RECEIVER -> Receiver(parseStringToDocNode(it.getContent())) + KDocKnownTag.RETURN -> Return(parseStringToDocNode(it.getContent())) + KDocKnownTag.SEE -> See( + parseStringToDocNode(it.getContent()), + it.getSubjectName().orEmpty(), + parseStringToDocNode("[${it.getSubjectName()}]") + .let { + val link = it.children[0] + if (link is DocumentationLink) link.dri + else null + } + ) + KDocKnownTag.SINCE -> Since(parseStringToDocNode(it.getContent())) + KDocKnownTag.CONSTRUCTOR -> Constructor(parseStringToDocNode(it.getContent())) + KDocKnownTag.PROPERTY -> Property( + parseStringToDocNode(it.getContent()), + it.getSubjectName().orEmpty() + ) + KDocKnownTag.SAMPLE -> Sample( + parseStringToDocNode(it.getContent()), + it.getSubjectName().orEmpty() + ) + KDocKnownTag.SUPPRESS -> Suppress(parseStringToDocNode(it.getContent())) + } + } + ) + } +}
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/parsers/Parser.kt b/plugins/base/src/main/kotlin/parsers/Parser.kt new file mode 100644 index 00000000..1dd0c34a --- /dev/null +++ b/plugins/base/src/main/kotlin/parsers/Parser.kt @@ -0,0 +1,42 @@ +package org.jetbrains.dokka.base.parsers + +import org.jetbrains.dokka.model.doc.* + +abstract class Parser { + + abstract fun parseStringToDocNode(extractedString: String): DocTag + abstract fun preparse(text: String): String + + fun parse(text: String): DocumentationNode { + + val list = jkdocToListOfPairs(preparse(text)) + + val mappedList: List<TagWrapper> = list.map { + when(it.first) { + "description" -> Description(parseStringToDocNode(it.second)) + "author" -> Author(parseStringToDocNode(it.second)) + "version" -> Version(parseStringToDocNode(it.second)) + "since" -> Since(parseStringToDocNode(it.second)) + "see" -> See(parseStringToDocNode(it.second.substringAfter(' ')), it.second.substringBefore(' '), null) + "param" -> Param(parseStringToDocNode(it.second.substringAfter(' ')), it.second.substringBefore(' ')) + "property" -> Property(parseStringToDocNode(it.second.substringAfter(' ')), it.second.substringBefore(' ')) + "return" -> Return(parseStringToDocNode(it.second)) + "constructor" -> Constructor(parseStringToDocNode(it.second)) + "receiver" -> Receiver(parseStringToDocNode(it.second)) + "throws", "exception" -> Throws(parseStringToDocNode(it.second.substringAfter(' ')), it.second.substringBefore(' ')) + "deprecated" -> Deprecated(parseStringToDocNode(it.second)) + "sample" -> Sample(parseStringToDocNode(it.second.substringAfter(' ')), it.second.substringBefore(' ')) + "suppress" -> Suppress(parseStringToDocNode(it.second)) + else -> CustomTagWrapper(parseStringToDocNode(it.second), it.first) + } + } + return DocumentationNode(mappedList) + } + + private fun jkdocToListOfPairs(javadoc: String): List<Pair<String, String>> = + "description $javadoc" + .split("\n@") + .map { + it.substringBefore(' ') to it.substringAfter(' ') + } +}
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/parsers/factories/DocTagsFromIElementFactory.kt b/plugins/base/src/main/kotlin/parsers/factories/DocTagsFromIElementFactory.kt new file mode 100644 index 00000000..277fd35e --- /dev/null +++ b/plugins/base/src/main/kotlin/parsers/factories/DocTagsFromIElementFactory.kt @@ -0,0 +1,43 @@ +package org.jetbrains.dokka.base.parsers.factories + +import org.jetbrains.dokka.model.doc.* +import org.intellij.markdown.IElementType +import org.intellij.markdown.MarkdownElementTypes +import org.intellij.markdown.MarkdownTokenTypes +import org.intellij.markdown.flavours.gfm.GFMElementTypes +import org.jetbrains.dokka.links.DRI +import java.lang.NullPointerException + +object DocTagsFromIElementFactory { + fun getInstance(type: IElementType, children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap(), body: String? = null, dri: DRI? = null) = + when(type) { + MarkdownElementTypes.SHORT_REFERENCE_LINK, + MarkdownElementTypes.FULL_REFERENCE_LINK, + MarkdownElementTypes.INLINE_LINK -> if(dri == null) A(children, params) else DocumentationLink(dri, children, params) + MarkdownElementTypes.STRONG -> B(children, params) + MarkdownElementTypes.BLOCK_QUOTE -> BlockQuote(children, params) + MarkdownElementTypes.CODE_SPAN -> CodeInline(children, params) + MarkdownElementTypes.CODE_BLOCK, + MarkdownElementTypes.CODE_FENCE -> CodeBlock(children, params) + MarkdownElementTypes.ATX_1 -> H1(children, params) + MarkdownElementTypes.ATX_2 -> H2(children, params) + MarkdownElementTypes.ATX_3 -> H3(children, params) + MarkdownElementTypes.ATX_4 -> H4(children, params) + MarkdownElementTypes.ATX_5 -> H5(children, params) + MarkdownElementTypes.ATX_6 -> H6(children, params) + MarkdownElementTypes.EMPH -> I(children, params) + MarkdownElementTypes.IMAGE -> Img(children, params) + MarkdownElementTypes.LIST_ITEM -> Li(children, params) + MarkdownElementTypes.ORDERED_LIST -> Ol(children, params) + MarkdownElementTypes.UNORDERED_LIST -> Ul(children, params) + MarkdownElementTypes.PARAGRAPH -> P(children, params) + MarkdownTokenTypes.TEXT -> Text(body ?: throw NullPointerException("Text body should be at least empty string passed to DocNodes factory!"), children, params ) + MarkdownTokenTypes.HORIZONTAL_RULE -> HorizontalRule + MarkdownTokenTypes.HARD_LINE_BREAK -> Br + GFMElementTypes.STRIKETHROUGH -> Strikethrough(children, params) + GFMElementTypes.TABLE -> Table(children, params) + GFMElementTypes.HEADER -> Th(children, params) + GFMElementTypes.ROW -> Tr(children, params) + else -> CustomDocTag(children, params) + } +}
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/parsers/factories/DocTagsFromStringFactory.kt b/plugins/base/src/main/kotlin/parsers/factories/DocTagsFromStringFactory.kt new file mode 100644 index 00000000..124dc3b4 --- /dev/null +++ b/plugins/base/src/main/kotlin/parsers/factories/DocTagsFromStringFactory.kt @@ -0,0 +1,77 @@ +package org.jetbrains.dokka.base.parsers.factories + +import org.jetbrains.dokka.model.doc.* +import org.jetbrains.dokka.links.DRI +import java.lang.NullPointerException + +object DocTagsFromStringFactory { + fun getInstance(name: String, children: List<DocTag> = emptyList(), params: Map<String, String> = emptyMap(), body: String? = null, dri: DRI? = null) = + when(name) { + "a" -> A(children, params) + "big" -> Big(children, params) + "b" -> B(children, params) + "blockquote" -> BlockQuote(children, params) + "br" -> Br + "cite" -> Cite(children, params) + "code" -> if(params.isEmpty()) CodeInline(children, params) else CodeBlock(children, params) + "dd" -> Dd(children, params) + "dfn" -> Dfn(children, params) + "dir" -> Dir(children, params) + "div" -> Div(children, params) + "dl" -> Dl(children, params) + "dt" -> Dt(children, params) + "Em" -> Em(children, params) + "font" -> Font(children, params) + "footer" -> Footer(children, params) + "frame" -> Frame(children, params) + "frameset" -> FrameSet(children, params) + "h1" -> H1(children, params) + "h2" -> H2(children, params) + "h3" -> H3(children, params) + "h4" -> H4(children, params) + "h5" -> H5(children, params) + "h6" -> H6(children, params) + "head" -> Head(children, params) + "header" -> Header(children, params) + "html" -> Html(children, params) + "i" -> I(children, params) + "iframe" -> IFrame(children, params) + "img" -> Img(children, params) + "input" -> Input(children, params) + "li" -> Li(children, params) + "link" -> Link(children, params) + "listing" -> Listing(children, params) + "main" -> Main(children, params) + "menu" -> Menu(children, params) + "meta" -> Meta(children, params) + "nav" -> Nav(children, params) + "noframes" -> NoFrames(children, params) + "noscript" -> NoScript(children, params) + "ol" -> Ol(children, params) + "p" -> P(children, params) + "pre" -> Pre(children, params) + "script" -> Script(children, params) + "section" -> Section(children, params) + "small" -> Small(children, params) + "span" -> Span(children, params) + "strong" -> Strong(children, params) + "sub" -> Sub(children, params) + "sup" -> Sup(children, params) + "table" -> Table(children, params) + "#text" -> Text(body ?: throw NullPointerException("Text body should be at least empty string passed to DocNodes factory!"), children, params) + "tBody" -> TBody(children, params) + "td" -> Td(children, params) + "tFoot" -> TFoot(children, params) + "th" -> Th(children, params) + "tHead" -> THead(children, params) + "title" -> Title(children, params) + "tr" -> Tr(children, params) + "tt" -> Tt(children, params) + "u" -> U(children, params) + "ul" -> Ul(children, params) + "var" -> Var(children, params) + "documentationlink" -> DocumentationLink(dri ?: throw NullPointerException("DRI cannot be passed null while constructing documentation link!"), children, params) + "hr" -> HorizontalRule + else -> CustomDocTag(children, params) + } +}
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt b/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt new file mode 100644 index 00000000..afee1b33 --- /dev/null +++ b/plugins/base/src/main/kotlin/renderers/DefaultRenderer.kt @@ -0,0 +1,200 @@ +package org.jetbrains.dokka.base.renderers + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.base.DokkaBase +import org.jetbrains.dokka.base.resolvers.local.LocationProvider +import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.plugin +import org.jetbrains.dokka.plugability.querySingle +import org.jetbrains.dokka.renderers.Renderer +import org.jetbrains.dokka.transformers.pages.PageTransformer + +abstract class DefaultRenderer<T>( + protected val context: DokkaContext +) : Renderer { + + protected val outputWriter = context.plugin<DokkaBase>().querySingle { outputWriter } + + protected lateinit var locationProvider: LocationProvider + private set + + protected open val preprocessors: Iterable<PageTransformer> = emptyList() + + abstract fun T.buildHeader(level: Int, node: ContentHeader, content: T.() -> Unit) + abstract fun T.buildLink(address: String, content: T.() -> Unit) + abstract fun T.buildList( + node: ContentList, + pageContext: ContentPage, + sourceSetRestriction: Set<DokkaSourceSet>? = null + ) + + abstract fun T.buildNewLine() + abstract fun T.buildResource(node: ContentEmbeddedResource, pageContext: ContentPage) + abstract fun T.buildTable( + node: ContentTable, + pageContext: ContentPage, + sourceSetRestriction: Set<DokkaSourceSet>? = null + ) + + abstract fun T.buildText(textNode: ContentText) + abstract fun T.buildNavigation(page: PageNode) + + abstract fun buildPage(page: ContentPage, content: (T, ContentPage) -> Unit): String + abstract fun buildError(node: ContentNode) + + open fun T.buildPlatformDependent( + content: PlatformHintedContent, + pageContext: ContentPage, + sourceSetRestriction: Set<DokkaSourceSet>? + ) = buildContentNode(content.inner, pageContext) + + open fun T.buildGroup( + node: ContentGroup, + pageContext: ContentPage, + sourceSetRestriction: Set<DokkaSourceSet>? = null + ) = + wrapGroup(node, pageContext) { node.children.forEach { it.build(this, pageContext, sourceSetRestriction) } } + + open fun T.buildDivergent(node: ContentDivergentGroup, pageContext: ContentPage) = + node.children.forEach { it.build(this, pageContext) } + + open fun T.wrapGroup(node: ContentGroup, pageContext: ContentPage, childrenCallback: T.() -> Unit) = + childrenCallback() + + open fun T.buildLinkText( + nodes: List<ContentNode>, + pageContext: ContentPage, + sourceSetRestriction: Set<DokkaSourceSet>? = null + ) { + nodes.forEach { it.build(this, pageContext, sourceSetRestriction) } + } + + open fun T.buildCodeBlock(code: ContentCodeBlock, pageContext: ContentPage) { + code.children.forEach { it.build(this, pageContext) } + } + + open fun T.buildCodeInline(code: ContentCodeInline, pageContext: ContentPage) { + code.children.forEach { it.build(this, pageContext) } + } + + open fun T.buildHeader( + node: ContentHeader, + pageContext: ContentPage, + sourceSetRestriction: Set<DokkaSourceSet>? = null + ) { + buildHeader(node.level, node) { node.children.forEach { it.build(this, pageContext, sourceSetRestriction) } } + } + + open fun ContentNode.build( + builder: T, + pageContext: ContentPage, + sourceSetRestriction: Set<DokkaSourceSet>? = null + ) = + builder.buildContentNode(this, pageContext, sourceSetRestriction) + + open fun T.buildContentNode( + node: ContentNode, + pageContext: ContentPage, + sourceSetRestriction: Set<DokkaSourceSet>? = null + ) { + if (sourceSetRestriction == null || node.sourceSets.any { it in sourceSetRestriction }) { + when (node) { + is ContentText -> buildText(node) + is ContentHeader -> buildHeader(node, pageContext, sourceSetRestriction) + is ContentCodeBlock -> buildCodeBlock(node, pageContext) + is ContentCodeInline -> buildCodeInline(node, pageContext) + is ContentDRILink -> + buildLink(locationProvider.resolve(node.address, node.sourceSets, pageContext)) { + buildLinkText(node.children, pageContext, sourceSetRestriction) + } + is ContentResolvedLink -> buildLink(node.address) { + buildLinkText(node.children, pageContext, sourceSetRestriction) + } + is ContentEmbeddedResource -> buildResource(node, pageContext) + is ContentList -> buildList(node, pageContext, sourceSetRestriction) + is ContentTable -> buildTable(node, pageContext, sourceSetRestriction) + is ContentGroup -> buildGroup(node, pageContext, sourceSetRestriction) + is ContentBreakLine -> buildNewLine() + is PlatformHintedContent -> buildPlatformDependent(node, pageContext, sourceSetRestriction) + is ContentDivergentGroup -> buildDivergent(node, pageContext) + is ContentDivergentInstance -> buildDivergentInstance(node, pageContext) + else -> buildError(node) + } + } + } + + open fun T.buildDivergentInstance(node: ContentDivergentInstance, pageContext: ContentPage) { + node.before?.build(this, pageContext) + node.divergent.build(this, pageContext) + node.after?.build(this, pageContext) + } + + open fun buildPageContent(context: T, page: ContentPage) { + context.buildNavigation(page) + page.content.build(context, page) + } + + open suspend fun renderPage(page: PageNode) { + val path by lazy { locationProvider.resolve(page, skipExtension = true) } + when (page) { + is ContentPage -> outputWriter.write(path, buildPage(page) { c, p -> buildPageContent(c, p) }, ".html") + is RendererSpecificPage -> when (val strategy = page.strategy) { + is RenderingStrategy.Copy -> outputWriter.writeResources(strategy.from, path) + is RenderingStrategy.Write -> outputWriter.write(path, strategy.text, "") + is RenderingStrategy.Callback -> outputWriter.write(path, strategy.instructions(this, page), ".html") + RenderingStrategy.DoNothing -> Unit + } + else -> throw AssertionError( + "Page ${page.name} cannot be rendered by renderer as it is not renderer specific nor contains content" + ) + } + } + + private suspend fun renderPages(root: PageNode) { + coroutineScope { + renderPage(root) + + root.children.forEach { + launch { renderPages(it) } + } + } + } + + override fun render(root: RootPageNode) { + val newRoot = preprocessors.fold(root) { acc, t -> t(acc) } + + locationProvider = + context.plugin<DokkaBase>().querySingle { locationProviderFactory }.getLocationProvider(newRoot) + + runBlocking(Dispatchers.Default) { + renderPages(newRoot) + } + } + + protected fun ContentDivergentGroup.groupDivergentInstances( + pageContext: ContentPage, + beforeTransformer: (ContentDivergentInstance, ContentPage, DokkaSourceSet) -> String, + afterTransformer: (ContentDivergentInstance, ContentPage, DokkaSourceSet) -> String + ): Map<SerializedBeforeAndAfter, List<InstanceWithSource>> = + children.flatMap { instance -> + instance.sourceSets.map { sourceSet -> + Pair(instance, sourceSet) to Pair( + beforeTransformer(instance, pageContext, sourceSet), + afterTransformer(instance, pageContext, sourceSet) + ) + } + }.groupBy( + Pair<InstanceWithSource, SerializedBeforeAndAfter>::second, + Pair<InstanceWithSource, SerializedBeforeAndAfter>::first + ) +} + +internal typealias SerializedBeforeAndAfter = Pair<String, String> +internal typealias InstanceWithSource = Pair<ContentDivergentInstance, DokkaSourceSet> + +fun ContentPage.sourceSets() = this.content.sourceSets
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/renderers/DefaultTabSortingStrategy.kt b/plugins/base/src/main/kotlin/renderers/DefaultTabSortingStrategy.kt new file mode 100644 index 00000000..3b849fec --- /dev/null +++ b/plugins/base/src/main/kotlin/renderers/DefaultTabSortingStrategy.kt @@ -0,0 +1,31 @@ +package org.jetbrains.dokka.base.renderers + +import org.jetbrains.dokka.pages.ContentKind +import org.jetbrains.dokka.pages.ContentNode +import org.jetbrains.dokka.pages.Kind +import org.jetbrains.dokka.utilities.DokkaLogger + +private val kindsOrder = listOf( + ContentKind.Classlikes, + ContentKind.Constructors, + ContentKind.Functions, + ContentKind.Properties, + ContentKind.Extensions, + ContentKind.Parameters, + ContentKind.Inheritors, + ContentKind.Source, + ContentKind.Sample, + ContentKind.Comment +) + +class DefaultTabSortingStrategy : TabSortingStrategy { + override fun <T: ContentNode> sort(tabs: Collection<T>): List<T> { + val tabMap: Map<Kind, MutableList<T>> = kindsOrder.asSequence().map { it to mutableListOf<T>() }.toMap() + val unrecognized: MutableList<T> = mutableListOf() + tabs.forEach { + tabMap[it.dci.kind]?.add(it) ?: unrecognized.add(it) + } + return tabMap.values.flatten() + unrecognized + } + +} diff --git a/plugins/base/src/main/kotlin/renderers/FileWriter.kt b/plugins/base/src/main/kotlin/renderers/FileWriter.kt new file mode 100644 index 00000000..181295c0 --- /dev/null +++ b/plugins/base/src/main/kotlin/renderers/FileWriter.kt @@ -0,0 +1,88 @@ +package org.jetbrains.dokka.base.renderers + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import org.jetbrains.dokka.plugability.DokkaContext +import java.io.File +import java.io.IOException +import java.net.URI +import java.nio.file.* + +class FileWriter(val context: DokkaContext): OutputWriter { + private val createdFiles: MutableSet<String> = mutableSetOf() + private val jarUriPrefix = "jar:file:" + private val root = context.configuration.outputDir + + override suspend fun write(path: String, text: String, ext: String) { + if (createdFiles.contains(path)) { + context.logger.error("An attempt to write ${root}/$path several times!") + return + } + createdFiles.add(path) + + try { + val dir = Paths.get(root, path.dropLastWhile { it != '/' }).toFile() + withContext(Dispatchers.IO) { + dir.mkdirsOrFail() + Files.write(Paths.get(root, "$path$ext"), text.lines()) + } + } catch (e: Throwable) { + context.logger.error("Failed to write $this. ${e.message}") + e.printStackTrace() + } + } + + override suspend fun writeResources(pathFrom: String, pathTo: String) = + if (javaClass.getResource(pathFrom).toURI().toString().startsWith(jarUriPrefix)) { + copyFromJar(pathFrom, pathTo) + } else { + copyFromDirectory(pathFrom, pathTo) + } + + + private suspend fun copyFromDirectory(pathFrom: String, pathTo: String) { + val dest = Paths.get(root, pathTo).toFile() + val uri = javaClass.getResource(pathFrom).toURI() + withContext(Dispatchers.IO) { + File(uri).copyRecursively(dest, true) + } + } + + private suspend fun copyFromJar(pathFrom: String, pathTo: String) { + val rebase = fun(path: String) = + "$pathTo/${path.removePrefix(pathFrom)}" + val dest = Paths.get(root, pathTo).toFile() + dest.mkdirsOrFail() + val uri = javaClass.getResource(pathFrom).toURI() + val fs = getFileSystemForURI(uri) + val path = fs.getPath(pathFrom) + for (file in Files.walk(path).iterator()) { + if (Files.isDirectory(file)) { + val dirPath = file.toAbsolutePath().toString() + withContext(Dispatchers.IO) { + Paths.get(root, rebase(dirPath)).toFile().mkdirsOrFail() + } + } else { + val filePath = file.toAbsolutePath().toString() + withContext(Dispatchers.IO) { + Paths.get(root, rebase(filePath)).toFile().writeBytes( + this@FileWriter.javaClass.getResourceAsStream(filePath).readBytes() + ) + } + } + } + } + + private fun File.mkdirsOrFail() { + if (!mkdirs() && !exists()) { + throw IOException("Failed to create directory $this") + } + } + + private fun getFileSystemForURI(uri: URI): FileSystem = + try { + FileSystems.newFileSystem(uri, emptyMap<String, Any>()) + } catch (e: FileSystemAlreadyExistsException) { + FileSystems.getFileSystem(uri) + } +}
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/renderers/OutputWriter.kt b/plugins/base/src/main/kotlin/renderers/OutputWriter.kt new file mode 100644 index 00000000..1827c7f0 --- /dev/null +++ b/plugins/base/src/main/kotlin/renderers/OutputWriter.kt @@ -0,0 +1,7 @@ +package org.jetbrains.dokka.base.renderers + +interface OutputWriter { + + suspend fun write(path: String, text: String, ext: String) + suspend fun writeResources(pathFrom: String, pathTo: String) +}
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/renderers/PackageListService.kt b/plugins/base/src/main/kotlin/renderers/PackageListService.kt new file mode 100644 index 00000000..d4333200 --- /dev/null +++ b/plugins/base/src/main/kotlin/renderers/PackageListService.kt @@ -0,0 +1,51 @@ +package org.jetbrains.dokka.base.renderers + +import org.jetbrains.dokka.base.DokkaBase +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.links.parent +import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.plugin +import org.jetbrains.dokka.plugability.querySingle +import org.jetbrains.kotlin.utils.addToStdlib.safeAs + +class PackageListService(val context: DokkaContext) { + + fun formatPackageList(module: RootPageNode, format: String, linkExtension: String): String { + + val packages = mutableSetOf<String>() + val nonStandardLocations = mutableMapOf<String, String>() + + val locationProvider = + context.plugin<DokkaBase>().querySingle { locationProviderFactory }.getLocationProvider(module) + + fun visit(node: PageNode, parentDris: Set<DRI>) { + + if (node is PackagePageNode) { + packages.add(node.name) + } + + val contentPage = node.safeAs<ContentPage>() + contentPage?.dri?.forEach { + if (parentDris.isNotEmpty() && it.parent !in parentDris) { + nonStandardLocations[it.toString()] = locationProvider.resolve(node) + } + } + + node.children.forEach { visit(it, contentPage?.dri ?: setOf()) } + } + + visit(module, setOf()) + + return buildString { + appendln("\$dokka.format:${format}") + appendln("\$dokka.linkExtension:${linkExtension}") + nonStandardLocations.map { (signature, location) -> "\$dokka.location:$signature\u001f$location" } + .sorted().joinTo(this, separator = "\n", postfix = "\n") + + packages.sorted().joinTo(this, separator = "\n", postfix = "\n") + } + + } + +}
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/renderers/TabSortingStrategy.kt b/plugins/base/src/main/kotlin/renderers/TabSortingStrategy.kt new file mode 100644 index 00000000..dcf49ca9 --- /dev/null +++ b/plugins/base/src/main/kotlin/renderers/TabSortingStrategy.kt @@ -0,0 +1,7 @@ +package org.jetbrains.dokka.base.renderers + +import org.jetbrains.dokka.pages.ContentNode + +interface TabSortingStrategy { + fun <T: ContentNode> sort(tabs: Collection<T>) : List<T> +}
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt new file mode 100644 index 00000000..614d8f6e --- /dev/null +++ b/plugins/base/src/main/kotlin/renderers/html/HtmlRenderer.kt @@ -0,0 +1,760 @@ +package org.jetbrains.dokka.base.renderers.html + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import kotlinx.html.* +import kotlinx.html.stream.createHTML +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.Platform +import org.jetbrains.dokka.base.DokkaBase +import org.jetbrains.dokka.base.renderers.DefaultRenderer +import org.jetbrains.dokka.base.renderers.TabSortingStrategy +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.properties.PropertyContainer +import org.jetbrains.dokka.model.withDescendants +import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.plugin +import org.jetbrains.dokka.plugability.query +import org.jetbrains.dokka.plugability.querySingle +import java.io.File +import java.net.URI + +open class HtmlRenderer( + context: DokkaContext +) : DefaultRenderer<FlowContent>(context) { + + private val sourceSetDependencyMap = context.configuration.sourceSets.map { sourceSet -> + sourceSet to context.configuration.sourceSets.filter { sourceSet.dependentSourceSets.contains(it.sourceSetID) } + }.toMap() + + private val isMultiplatform by lazy { + sourceSetDependencyMap.size > 1 + } + + private val pageList = mutableMapOf<String, Pair<String, String>>() + + override val preprocessors = context.plugin<DokkaBase>().query { htmlPreprocessors } + + private val tabSortingStrategy = context.plugin<DokkaBase>().querySingle { tabSortingStrategy } + + private fun <T : ContentNode> sortTabs(strategy: TabSortingStrategy, tabs: Collection<T>): List<T> { + val sorted = strategy.sort(tabs) + if (sorted.size != tabs.size) + context.logger.warn("Tab sorting strategy has changed number of tabs from ${tabs.size} to ${sorted.size}") + return sorted; + } + + override fun FlowContent.wrapGroup( + node: ContentGroup, + pageContext: ContentPage, + childrenCallback: FlowContent.() -> Unit + ) { + val additionalClasses = node.style.joinToString(" ") { it.toString().toLowerCase() } + return when { + node.hasStyle(ContentStyle.TabbedContent) -> div(additionalClasses) { + val secondLevel = node.children.filterIsInstance<ContentComposite>().flatMap { it.children } + .filterIsInstance<ContentHeader>().flatMap { it.children }.filterIsInstance<ContentText>() + val firstLevel = node.children.filterIsInstance<ContentHeader>().flatMap { it.children } + .filterIsInstance<ContentText>() + + val renderable = firstLevel.union(secondLevel).let { sortTabs(tabSortingStrategy, it) } + + div(classes = "tabs-section") { + attributes["tabs-section"] = "tabs-section" + renderable.forEachIndexed { index, node -> + button(classes = "section-tab") { + if (index == 0) attributes["data-active"] = "" + attributes["data-togglable"] = node.text + text(node.text) + } + } + } + div(classes = "tabs-section-body") { + childrenCallback() + } + } + node.hasStyle(ContentStyle.WithExtraAttributes) -> div() { + node.extra.extraHtmlAttributes().forEach { attributes[it.extraKey] = it.extraValue } + childrenCallback() + } + node.dci.kind in setOf(ContentKind.Symbol) -> div("symbol $additionalClasses") { + childrenCallback() + if (node.hasStyle(TextStyle.Monospace)) copyButton() + } + node.hasStyle(TextStyle.BreakableAfter) -> { + span() { childrenCallback() } + wbr { } + } + node.hasStyle(TextStyle.Breakable) -> { + span("breakable-word") { childrenCallback() } + } + node.hasStyle(TextStyle.Span) -> span() { childrenCallback() } + node.dci.kind == ContentKind.Symbol -> div("symbol $additionalClasses") { childrenCallback() } + node.dci.kind == ContentKind.BriefComment -> div("brief $additionalClasses") { childrenCallback() } + node.dci.kind == ContentKind.Cover -> div("cover $additionalClasses") { + filterButtons(pageContext) + childrenCallback() + } + node.hasStyle(TextStyle.Paragraph) -> p(additionalClasses) { childrenCallback() } + node.hasStyle(TextStyle.Block) -> div(additionalClasses) { childrenCallback() } + else -> childrenCallback() + } + } + + private fun FlowContent.filterButtons(page: ContentPage) { + if (isMultiplatform) { + div(classes = "filter-section") { + id = "filter-section" + page.content.withDescendants().flatMap { it.sourceSets }.distinct().forEach { + button(classes = "platform-tag platform-selector") { + attributes["data-active"] = "" + attributes["data-filter"] = it.sourceSetID.toString() + when (it.analysisPlatform.key) { + "common" -> classes = classes + "common-like" + "native" -> classes = classes + "native-like" + "jvm" -> classes = classes + "jvm-like" + "js" -> classes = classes + "js-like" + } + text(it.displayName) + } + } + } + } + } + + private fun FlowContent.copyButton() = span(classes = "top-right-position") { + span("copy-icon") { + unsafe { + raw( + """<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> + <path fill-rule="evenodd" clip-rule="evenodd" d="M5 4H15V16H5V4ZM17 7H19V18V20H17H8V18H17V7Z" fill="black"/> + </svg>""".trimIndent() + ) + } + } + copiedPopup("Content copied to clipboard", "popup-to-left") + } + + private fun FlowContent.copiedPopup(notificationContent: String, additionalClasses: String = "") = + div("copy-popup-wrapper $additionalClasses") { + unsafe { + raw( + """ + <svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg"> + <path d="M18 9C18 14 14 18 9 18C4 18 0 14 0 9C0 4 4 0 9 0C14 0 18 4 18 9ZM14.2 6.2L12.8 4.8L7.5 10.1L5.3 7.8L3.8 9.2L7.5 13L14.2 6.2Z" fill="#4DBB5F"/> + </svg> + """.trimIndent() + ) + } + span { + text(notificationContent) + } + } + + override fun FlowContent.buildPlatformDependent( + content: PlatformHintedContent, + pageContext: ContentPage, + sourceSetRestriction: Set<DokkaSourceSet>? + ) = + buildPlatformDependent( + content.sourceSets.filter { + sourceSetRestriction == null || it in sourceSetRestriction + }.map { it to setOf(content.inner) }.toMap(), + pageContext, + content.extra, + content.style + ) + + private fun FlowContent.buildPlatformDependent( + nodes: Map<DokkaSourceSet, Collection<ContentNode>>, + pageContext: ContentPage, + extra: PropertyContainer<ContentNode> = PropertyContainer.empty(), + styles: Set<Style> = emptySet() + ) { + val contents = contentsForSourceSetDependent(nodes, pageContext) + val shouldHaveTabs = contents.size != 1 + + val styles = "platform-hinted ${styles.joinToString()}" + if (shouldHaveTabs) " with-platform-tabs" else "" + div(styles) { + attributes["data-platform-hinted"] = "data-platform-hinted" + extra.extraHtmlAttributes().forEach { attributes[it.extraKey] = it.extraValue } + if (shouldHaveTabs) { + div("platform-bookmarks-row") { + attributes["data-toggle-list"] = "data-toggle-list" + contents.forEachIndexed { index, pair -> + button(classes = "platform-bookmark") { + attributes["data-filterable-current"] = pair.first.sourceSetID.toString() + attributes["data-filterable-set"] = pair.first.sourceSetID.toString() + if (index == 0) attributes["data-active"] = "" + attributes["data-toggle"] = pair.first.sourceSetID.toString() + when (pair.first.analysisPlatform.key) { + "common" -> classes = classes + "common-like" + "native" -> classes = classes + "native-like" + "jvm" -> classes = classes + "jvm-like" + "js" -> classes = classes + "js-like" + } + attributes["data-toggle"] = pair.first.sourceSetID.toString() + text(pair.first.displayName) + } + } + } + } + contents.forEach { + consumer.onTagContentUnsafe { +it.second } + } + } + } + + private fun contentsForSourceSetDependent( + nodes: Map<DokkaSourceSet, Collection<ContentNode>>, + pageContext: ContentPage, + ): List<Pair<DokkaSourceSet, String>> { + var counter = 0 + return nodes.toList().map { (sourceSet, elements) -> + sourceSet to createHTML(prettyPrint = false).div { + elements.forEach { + buildContentNode(it, pageContext, setOf(sourceSet)) + } + }.stripDiv() + }.groupBy( + Pair<DokkaSourceSet, String>::second, + Pair<DokkaSourceSet, String>::first + ).entries.flatMap { (html, sourceSets) -> + sourceSets.filterNot { + sourceSetDependencyMap[it].orEmpty().any { dependency -> sourceSets.contains(dependency) } + }.map { + it to createHTML(prettyPrint = false).div(classes = "content sourceset-depenent-content") { + if (counter++ == 0) attributes["data-active"] = "" + attributes["data-togglable"] = it.sourceSetID.toString() + unsafe { + +html + } + } + } + } + } + + override fun FlowContent.buildDivergent(node: ContentDivergentGroup, pageContext: ContentPage) { + + val distinct = + node.groupDivergentInstances(pageContext, { instance, contentPage, sourceSet -> + createHTML(prettyPrint = false).div { + instance.before?.let { before -> + buildContentNode(before, pageContext, setOf(sourceSet)) + } + }.stripDiv() + }, { instance, contentPage, sourceSet -> + createHTML(prettyPrint = false).div { + instance.after?.let { after -> + buildContentNode(after, pageContext, setOf(sourceSet)) + } + }.stripDiv() + }) + + distinct.forEach { + val groupedDivergent = it.value.groupBy { it.second } + + consumer.onTagContentUnsafe { + +createHTML().div("divergent-group") { + attributes["data-filterable-current"] = groupedDivergent.keys.joinToString(" ") { + it.sourceSetID.toString() + } + attributes["data-filterable-set"] = groupedDivergent.keys.joinToString(" ") { + it.sourceSetID.toString() + } + + val divergentForPlatformDependent = groupedDivergent.map { (sourceSet, elements) -> + sourceSet to elements.map { e -> e.first.divergent } + }.toMap() + + val content = contentsForSourceSetDependent(divergentForPlatformDependent, pageContext) + + consumer.onTagContentUnsafe { + +createHTML().div("brief-with-platform-tags") { + consumer.onTagContentUnsafe { + +createHTML().div("inner-brief-with-platform-tags") { + consumer.onTagContentUnsafe { +it.key.first } + } + } + + consumer.onTagContentUnsafe { + +createHTML().span("pull-right") { + if ((distinct.size > 1 && groupedDivergent.size == 1) || groupedDivergent.size == 1 || content.size == 1) { + if (node.sourceSets.size != 1) { + createPlatformTags(node, setOf(content.first().first)) + } + } + } + } + } + } + div("main-subrow") { + if (node.implicitlySourceSetHinted) { + buildPlatformDependent(divergentForPlatformDependent, pageContext) + } else { + it.value.forEach { + buildContentNode(it.first.divergent, pageContext, setOf(it.second)) + } + } + } + consumer.onTagContentUnsafe { +it.key.second } + } + } + } + } + + override fun FlowContent.buildList( + node: ContentList, + pageContext: ContentPage, + sourceSetRestriction: Set<DokkaSourceSet>? + ) = if (node.ordered) ol { buildListItems(node.children, pageContext, sourceSetRestriction) } + else ul { buildListItems(node.children, pageContext, sourceSetRestriction) } + + open fun OL.buildListItems( + items: List<ContentNode>, + pageContext: ContentPage, + sourceSetRestriction: Set<DokkaSourceSet>? = null + ) { + items.forEach { + if (it is ContentList) + buildList(it, pageContext) + else + li { it.build(this, pageContext, sourceSetRestriction) } + } + } + + open fun UL.buildListItems( + items: List<ContentNode>, + pageContext: ContentPage, + sourceSetRestriction: Set<DokkaSourceSet>? = null + ) { + items.forEach { + if (it is ContentList) + buildList(it, pageContext) + else + li { it.build(this, pageContext) } + } + } + + override fun FlowContent.buildResource( + node: ContentEmbeddedResource, + pageContext: ContentPage + ) { // TODO: extension point there + val imageExtensions = setOf("png", "jpg", "jpeg", "gif", "bmp", "tif", "webp", "svg") + return if (File(node.address).extension.toLowerCase() in imageExtensions) { + //TODO: add imgAttrs parsing + val imgAttrs = node.extra.allOfType<SimpleAttr>().joinAttr() + img(src = node.address, alt = node.altText) + } else { + println("Unrecognized resource type: $node") + } + } + + private fun FlowContent.buildRow( + node: ContentGroup, + pageContext: ContentPage, + sourceSetRestriction: Set<DokkaSourceSet>?, + style: Set<Style> + ) { + node.children + .filter { sourceSetRestriction == null || it.sourceSets.any { s -> s in sourceSetRestriction } } + .takeIf { it.isNotEmpty() } + ?.let { + val anchorName = node.dci.dri.first().toString() + withAnchor(anchorName) { + div(classes = "table-row") { + if (!style.contains(MultimoduleTable)) { + attributes["data-filterable-current"] = node.sourceSets.joinToString(" ") { + it.sourceSetID.toString() + } + attributes["data-filterable-set"] = node.sourceSets.joinToString(" ") { + it.sourceSetID.toString() + } + } + + it.filterIsInstance<ContentLink>().takeIf { it.isNotEmpty() }?.let { + div("main-subrow " + node.style.joinToString(" ")) { + it.filter { sourceSetRestriction == null || it.sourceSets.any { s -> s in sourceSetRestriction } } + .forEach { + span { + it.build(this, pageContext, sourceSetRestriction) + buildAnchor(anchorName) + } + if (ContentKind.shouldBePlatformTagged(node.dci.kind) && (node.sourceSets.size == 1)) + createPlatformTags(node) + } + } + } + + it.filter { it !is ContentLink }.takeIf { it.isNotEmpty() }?.let { + div("platform-dependent-row keyValue") { + val title = it.filter { it.style.contains(ContentStyle.RowTitle) } + div { + title.forEach { + it.build(this, pageContext, sourceSetRestriction) + } + } + div("title") { + (it - title).forEach { + it.build(this, pageContext, sourceSetRestriction) + } + } + } + } + } + } + } + } + + private fun FlowContent.createPlatformTagBubbles(sourceSets: List<DokkaSourceSet>) { + if (isMultiplatform) { + div("platform-tags") { + sourceSets.forEach { + div("platform-tag") { + when (it.analysisPlatform.key) { + "common" -> classes = classes + "common-like" + "native" -> classes = classes + "native-like" + "jvm" -> classes = classes + "jvm-like" + "js" -> classes = classes + "js-like" + } + text(it.displayName) + } + } + } + } + } + + private fun FlowContent.createPlatformTags(node: ContentNode, sourceSetRestriction: Set<DokkaSourceSet>? = null) { + node.takeIf { sourceSetRestriction == null || it.sourceSets.any { s -> s in sourceSetRestriction } }?.let { + createPlatformTagBubbles(node.sourceSets.filter { + sourceSetRestriction == null || it in sourceSetRestriction + }) + } + } + + override fun FlowContent.buildTable( + node: ContentTable, + pageContext: ContentPage, + sourceSetRestriction: Set<DokkaSourceSet>? + ) { + when (node.dci.kind) { + ContentKind.Comment -> buildDefaultTable(node, pageContext, sourceSetRestriction) + else -> div(classes = "table") { + node.extra.extraHtmlAttributes().forEach { attributes[it.extraKey] = it.extraValue } + node.children.forEach { + buildRow(it, pageContext, sourceSetRestriction, node.style) + } + } + } + + } + + fun FlowContent.buildDefaultTable( + node: ContentTable, + pageContext: ContentPage, + sourceSetRestriction: Set<DokkaSourceSet>? + ) { + table { + thead { + node.header.forEach { + tr { + it.children.forEach { + th { + it.build(this@table, pageContext, sourceSetRestriction) + } + } + } + } + } + tbody { + node.children.forEach { + tr { + it.children.forEach { + td { + it.build(this, pageContext, sourceSetRestriction) + } + } + } + } + } + } + } + + + override fun FlowContent.buildHeader(level: Int, node: ContentHeader, content: FlowContent.() -> Unit) { + val anchor = node.extra[SimpleAttr.SimpleAttrKey("anchor")]?.extraValue + val classes = node.style.joinToString { it.toString() }.toLowerCase() + when (level) { + 1 -> h1(classes = classes) { withAnchor(anchor, content) } + 2 -> h2(classes = classes) { withAnchor(anchor, content) } + 3 -> h3(classes = classes) { withAnchor(anchor, content) } + 4 -> h4(classes = classes) { withAnchor(anchor, content) } + 5 -> h5(classes = classes) { withAnchor(anchor, content) } + else -> h6(classes = classes) { withAnchor(anchor, content) } + } + } + + private fun FlowContent.withAnchor(anchorName: String?, content: FlowContent.() -> Unit) { + a { + anchorName?.let { attributes["data-name"] = it } + } + content() + } + + + override fun FlowContent.buildNavigation(page: PageNode) = + div(classes = "breadcrumbs") { + locationProvider.ancestors(page).asReversed().forEach { node -> + text("/") + if (node.isNavigable) buildLink(node, page) + else text(node.name) + } + } + + private fun FlowContent.buildLink(to: PageNode, from: PageNode) = + buildLink(locationProvider.resolve(to, from)) { + text(to.name) + } + + private fun FlowContent.buildAnchor(pointingTo: String) { + span(classes = "anchor-wrapper") { + span(classes = "anchor-icon") { + attributes["pointing-to"] = pointingTo + unsafe { + raw( + """ + <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> + <path d="M21.2496 5.3C20.3496 4.5 19.2496 4 18.0496 4C16.8496 4 15.6496 4.5 14.8496 5.3L10.3496 9.8L11.7496 11.2L16.2496 6.7C17.2496 5.7 18.8496 5.7 19.8496 6.7C20.8496 7.7 20.8496 9.3 19.8496 10.3L15.3496 14.8L16.7496 16.2L21.2496 11.7C22.1496 10.8 22.5496 9.7 22.5496 8.5C22.5496 7.3 22.1496 6.2 21.2496 5.3Z"/> + <path d="M8.35 16.7998C7.35 17.7998 5.75 17.7998 4.75 16.7998C3.75 15.7998 3.75 14.1998 4.75 13.1998L9.25 8.6998L7.85 7.2998L3.35 11.7998C1.55 13.5998 1.55 16.3998 3.35 18.1998C4.25 19.0998 5.35 19.4998 6.55 19.4998C7.75 19.4998 8.85 19.0998 9.75 18.1998L14.25 13.6998L12.85 12.2998L8.35 16.7998Z"/> + </svg> + """.trimIndent() + ) + } + } + copiedPopup("Link copied to clipboard") + } + } + + fun FlowContent.buildLink( + to: DRI, + platforms: List<DokkaSourceSet>, + from: PageNode? = null, + block: FlowContent.() -> Unit + ) = buildLink(locationProvider.resolve(to, platforms.toSet(), from), block) + + override fun buildError(node: ContentNode) { + context.logger.error("Unknown ContentNode type: $node") + } + + override fun FlowContent.buildNewLine() { + br() + } + + override fun FlowContent.buildLink(address: String, content: FlowContent.() -> Unit) = + a(href = address, block = content) + + override fun FlowContent.buildCodeBlock( + code: ContentCodeBlock, + pageContext: ContentPage + ) { + div("sample-container") { + code(code.style.joinToString(" ") { it.toString().toLowerCase() }) { + attributes["theme"] = "idea" + code.children.forEach { buildContentNode(it, pageContext) } + } + } + } + + override fun FlowContent.buildCodeInline( + code: ContentCodeInline, + pageContext: ContentPage + ) { + code { + code.children.forEach { buildContentNode(it, pageContext) } + } + } + + private fun getSymbolSignature(page: ContentPage) = page.content.dfs { it.dci.kind == ContentKind.Symbol } + + private fun flattenToText(node: ContentNode): String { + fun getContentTextNodes(node: ContentNode, sourceSetRestriction: DokkaSourceSet): List<ContentText> = + when (node) { + is ContentText -> listOf(node) + is ContentComposite -> node.children + .filter { sourceSetRestriction in it.sourceSets } + .flatMap { getContentTextNodes(it, sourceSetRestriction) } + .takeIf { node.dci.kind != ContentKind.Annotations } + .orEmpty() + else -> emptyList() + } + + val sourceSetRestriction = + node.sourceSets.find { it.analysisPlatform == Platform.common } ?: node.sourceSets.first() + return getContentTextNodes(node, sourceSetRestriction).joinToString("") { it.text } + } + + override suspend fun renderPage(page: PageNode) { + super.renderPage(page) + if (page is ContentPage && page !is ModulePageNode && page !is PackagePageNode) { + val signature = getSymbolSignature(page) + val textNodes = signature?.let { flattenToText(it) } + val documentable = page.documentable + if (documentable != null) { + listOf( + documentable.dri.packageName, + documentable.dri.classNames, + documentable.dri.callable?.name + ) + .filter { !it.isNullOrEmpty() } + .takeIf { it.isNotEmpty() } + ?.joinToString(".") + ?.let { + pageList.put(it, Pair(textNodes ?: page.name, locationProvider.resolve(page))) + } + + } + } + } + + override fun FlowContent.buildText(textNode: ContentText) { + when { + textNode.hasStyle(TextStyle.Indented) -> consumer.onTagContentEntity(Entities.nbsp) + } + text(textNode.text) + } + + private fun generatePagesList() = + pageList.entries + .filter { !it.key.isNullOrEmpty() } + .groupBy { it.key.substringAfterLast(".") } + .entries + .mapIndexed { topLevelIndex, entry -> + if (entry.value.size > 1) { + listOf( + "{\'name\': \'${entry.key}\', \'index\': \'$topLevelIndex\', \'disabled\': true, \'searchKey\':\'${entry.key}\' }" + ) + entry.value.mapIndexed { index, subentry -> + "{\'name\': \'${subentry.value.first}\', \'level\': 1, \'index\': \'$topLevelIndex.$index\', \'description\':\'${subentry.key}\', \'location\':\'${subentry.value.second}\', 'searchKey':'${entry.key}'}" + } + } else { + val subentry = entry.value.single() + listOf( + "{\'name\': \'${subentry.value.first}\', \'index\': \'$topLevelIndex\', \'description\':\'${subentry.key}\', \'location\':\'${subentry.value.second}\', 'searchKey':'${entry.key}'}" + ) + } + } + .flatten() + .joinToString(prefix = "[", separator = ",\n", postfix = "]") + + override fun render(root: RootPageNode) { + super.render(root) + runBlocking(Dispatchers.Default) { + launch { + outputWriter.write("scripts/pages", "var pages = ${generatePagesList()}", ".js") + } + } + } + + private fun PageNode.root(path: String) = locationProvider.resolveRoot(this) + path + + override fun buildPage(page: ContentPage, content: (FlowContent, ContentPage) -> Unit): String = + buildHtml(page, page.embeddedResources) { + div { + id = "content" + attributes["pageIds"] = page.dri.first().toString() + content(this, page) + } + } + + private fun resolveLink(link: String, page: PageNode): String = if (URI(link).isAbsolute) link else page.root(link) + + open fun buildHtml(page: PageNode, resources: List<String>, content: FlowContent.() -> Unit) = + createHTML().html { + head { + meta(name = "viewport", content = "width=device-width, initial-scale=1", charset = "UTF-8") + title(page.name) + resources.forEach { + when { + it.substringBefore('?').substringAfterLast('.') == "css" -> link( + rel = LinkRel.stylesheet, + href = resolveLink(it, page) + ) + it.substringBefore('?').substringAfterLast('.') == "js" -> script( + type = ScriptType.textJavaScript, + src = resolveLink(it, page) + ) { + async = true + } + else -> unsafe { +it } + } + } + script { unsafe { +"""var pathToRoot = "${locationProvider.resolveRoot(page)}";""" } } + } + body { + div { + id = "container" + div { + id = "leftColumn" + div { + id = "logo" + } + div { + id = "sideMenu" + } + } + div { + id = "main" + div { + id = "searchBar" + } + script(type = ScriptType.textJavaScript, src = page.root("scripts/pages.js")) {} + script(type = ScriptType.textJavaScript, src = page.root("scripts/main.js")) {} + content() + div(classes = "footer") { + span("go-to-top-icon") { + a(href = "#container") { + unsafe { + raw( + """ + <svg width="12" height="10" viewBox="0 0 12 10" fill="none" xmlns="http://www.w3.org/2000/svg"> + <path d="M11.3337 9.66683H0.666992L6.00033 3.66683L11.3337 9.66683Z" fill="black"/> + <path d="M0.666992 0.333496H11.3337V1.66683H0.666992V0.333496Z" fill="black"/> + </svg> + """.trimIndent() + ) + } + } + } + span { text("© 2020 Copyright") } + span("pull-right") { + span { text("Sponsored and developed by dokka") } + a(href= "https://github.com/Kotlin/dokka") { + span(classes = "padded-icon") { + unsafe { + raw( + """ + <svg width="8" height="8" viewBox="0 0 8 8" fill="none" xmlns="http://www.w3.org/2000/svg"> + <path d="M8 0H2.3949L4.84076 2.44586L0 7.28662L0.713376 8L5.55414 3.15924L8 5.6051V0Z" fill="black"/> + </svg> + """.trimIndent() + ) + } + } + } + } + } + } + } + } + } +} + +fun List<SimpleAttr>.joinAttr() = joinToString(" ") { it.extraKey + "=" + it.extraValue } + +private fun String.stripDiv() = drop(5).dropLast(6) // TODO: Find a way to do it without arbitrary trims + +private val PageNode.isNavigable: Boolean + get() = this !is RendererSpecificPage || strategy != RenderingStrategy.DoNothing + +fun PropertyContainer<ContentNode>.extraHtmlAttributes() = allOfType<SimpleAttr>() diff --git a/plugins/base/src/main/kotlin/renderers/html/NavigationPage.kt b/plugins/base/src/main/kotlin/renderers/html/NavigationPage.kt new file mode 100644 index 00000000..8fe2a6c8 --- /dev/null +++ b/plugins/base/src/main/kotlin/renderers/html/NavigationPage.kt @@ -0,0 +1,51 @@ +package org.jetbrains.dokka.base.renderers.html + +import kotlinx.html.* +import kotlinx.html.stream.createHTML +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.pages.PageNode +import org.jetbrains.dokka.pages.RendererSpecificPage +import org.jetbrains.dokka.pages.RenderingStrategy + +class NavigationPage(val root: NavigationNode) : RendererSpecificPage { + override val name = "navigation" + + override val children = emptyList<PageNode>() + + override fun modified(name: String, children: List<PageNode>) = this + + override val strategy = RenderingStrategy<HtmlRenderer> { + createHTML().visit(root, "nav-submenu", this) + } + + private fun <R> TagConsumer<R>.visit(node: NavigationNode, navId: String, renderer: HtmlRenderer): R = + with(renderer) { + div("sideMenuPart") { + id = navId + attributes["pageId"] = node.dri.toString() + div("overview") { + buildLink(node.dri, node.sourceSets.toList()) { +node.name } + if (node.children.isNotEmpty()) { + span("navButton") { + onClick = """document.getElementById("$navId").classList.toggle("hidden");""" + span("navButtonContent") + } + } + } + node.children.withIndex().forEach { (n, p) -> visit(p, "$navId-$n", renderer) } + } + } +} + +class NavigationNode( + val name: String, + val dri: DRI, + val sourceSets: Set<DokkaSourceSet>, + val children: List<NavigationNode> +) + +fun NavigationPage.transform(block: (NavigationNode) -> NavigationNode) = NavigationPage(root.transform(block)) + +fun NavigationNode.transform(block: (NavigationNode) -> NavigationNode) = + run(block).let { NavigationNode(it.name, it.dri, it.sourceSets, it.children.map(block)) } diff --git a/plugins/base/src/main/kotlin/renderers/html/Tags.kt b/plugins/base/src/main/kotlin/renderers/html/Tags.kt new file mode 100644 index 00000000..a3951eff --- /dev/null +++ b/plugins/base/src/main/kotlin/renderers/html/Tags.kt @@ -0,0 +1,12 @@ +package org.jetbrains.dokka.base.renderers.html + +import kotlinx.html.* + +@HtmlTagMarker +fun FlowOrPhrasingContent.wbr(classes : String? = null, block : WBR.() -> Unit = {}) : Unit = WBR(attributesMapOf("class", classes), consumer).visit(block) + +@Suppress("unused") +open class WBR(initialAttributes : Map<String, String>, override val consumer : TagConsumer<*>) : HTMLTag("wbr", consumer, initialAttributes, null, true, false), + HtmlBlockInlineTag { + +}
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt b/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt new file mode 100644 index 00000000..2f7c8ee1 --- /dev/null +++ b/plugins/base/src/main/kotlin/renderers/html/htmlPreprocessors.kt @@ -0,0 +1,112 @@ +package org.jetbrains.dokka.base.renderers.html + +import kotlinx.html.h1 +import kotlinx.html.id +import kotlinx.html.table +import kotlinx.html.tbody +import org.jetbrains.dokka.base.renderers.sourceSets +import org.jetbrains.dokka.model.DEnum +import org.jetbrains.dokka.model.DEnumEntry +import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.transformers.pages.PageTransformer + + +object SearchPageInstaller : PageTransformer { + override fun invoke(input: RootPageNode) = input.modified(children = input.children + searchPage) + + private val searchPage = RendererSpecificResourcePage( + name = "Search", + children = emptyList(), + strategy = RenderingStrategy<HtmlRenderer> { + buildHtml(it, listOf("styles/style.css", "scripts/pages.js", "scripts/search.js")) { + h1 { + id = "searchTitle" + text("Search results for ") + } + table { + tbody { + id = "searchTable" + } + } + } + }) +} + +object NavigationPageInstaller : PageTransformer { + override fun invoke(input: RootPageNode) = input.modified( + children = input.children + NavigationPage( + input.children.filterIsInstance<ContentPage>().single() + .let(NavigationPageInstaller::visit) + ) + ) + + private fun visit(page: ContentPage): NavigationNode = + NavigationNode( + page.name, + page.dri.first(), + page.sourceSets(), + page.navigableChildren() + ) + + private fun ContentPage.navigableChildren(): List<NavigationNode> { + if (this !is ClasslikePageNode) { + return children.filterIsInstance<ContentPage>() + .map { visit(it) } + } else if (documentable is DEnum) { + return children.filter { it is ContentPage && it.documentable is DEnumEntry } + .map { visit(it as ContentPage) } + } + + return emptyList() + } +} + +object ResourceInstaller : PageTransformer { + override fun invoke(input: RootPageNode) = input.modified(children = input.children + resourcePages) + + private val resourcePages = listOf("styles", "scripts", "images").map { + RendererSpecificResourcePage(it, emptyList(), RenderingStrategy.Copy("/dokka/$it")) + } +} + +object StyleAndScriptsAppender : PageTransformer { + override fun invoke(input: RootPageNode) = input.transformContentPagesTree { + it.modified( + embeddedResources = it.embeddedResources + listOf( + "styles/style.css", + "scripts/navigationLoader.js", + "scripts/platformContentHandler.js", + "scripts/sourceset_dependencies.js", + "scripts/clipboard.js", + "styles/jetbrains-mono.css" + ) + ) + } +} + +class SourcesetDependencyAppender(val context: DokkaContext) : PageTransformer { + override fun invoke(input: RootPageNode): RootPageNode { + val dependenciesMap = context.configuration.sourceSets.map { + it.sourceSetID to it.dependentSourceSets + }.toMap() + + fun createDependenciesJson(): String = "sourceset_dependencies = '{${ + dependenciesMap.entries.joinToString(", ") { + "\"${it.key}\": [${it.value.joinToString(",") { + "\"$it\"" + }}]" + } + }}'" + + val deps = RendererSpecificResourcePage( + name = "scripts/sourceset_dependencies.js", + children = emptyList(), + strategy = RenderingStrategy.Write(createDependenciesJson()) + ) + + return input.modified( + children = input.children + deps + ) + } +} diff --git a/plugins/base/src/main/kotlin/renderers/preprocessors.kt b/plugins/base/src/main/kotlin/renderers/preprocessors.kt new file mode 100644 index 00000000..bf2a9eb4 --- /dev/null +++ b/plugins/base/src/main/kotlin/renderers/preprocessors.kt @@ -0,0 +1,27 @@ +package org.jetbrains.dokka.base.renderers + +import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.transformers.pages.PageTransformer + +object RootCreator : PageTransformer { + override fun invoke(input: RootPageNode) = + RendererSpecificRootPage("", listOf(input), RenderingStrategy.DoNothing) +} + + +class PackageListCreator(val context: DokkaContext, val format: String, val linkExtension: String) : PageTransformer { + override fun invoke(input: RootPageNode) = + input.modified(children = input.children.map { + it.takeUnless { it is ModulePageNode } + ?: it.modified(children = it.children + packageList(input)) // TODO packageList should take module as an input + }) + + + private fun packageList(pageNode: RootPageNode) = + RendererSpecificResourcePage( + "${pageNode.name}/package-list", + emptyList(), + RenderingStrategy.Write(PackageListService(context).formatPackageList(pageNode, format, linkExtension)) + ) +} diff --git a/plugins/base/src/main/kotlin/resolvers/anchors/AnchorsHint.kt b/plugins/base/src/main/kotlin/resolvers/anchors/AnchorsHint.kt new file mode 100644 index 00000000..1b741484 --- /dev/null +++ b/plugins/base/src/main/kotlin/resolvers/anchors/AnchorsHint.kt @@ -0,0 +1,9 @@ +package org.jetbrains.dokka.base.resolvers.anchors + +import org.jetbrains.dokka.model.properties.ExtraProperty +import org.jetbrains.dokka.pages.ContentNode + +// TODO IMPORTANT: https://github.com/Kotlin/dokka/issues/1054 +object SymbolAnchorHint: ExtraProperty<ContentNode>, ExtraProperty.Key<ContentNode, SymbolAnchorHint> { + override val key = this +}
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/resolvers/external/DokkaExternalLocationProviderFactory.kt b/plugins/base/src/main/kotlin/resolvers/external/DokkaExternalLocationProviderFactory.kt new file mode 100644 index 00000000..ff9186f7 --- /dev/null +++ b/plugins/base/src/main/kotlin/resolvers/external/DokkaExternalLocationProviderFactory.kt @@ -0,0 +1,35 @@ +package org.jetbrains.dokka.base.resolvers.external + +import org.jetbrains.dokka.base.resolvers.local.identifierToFilename +import org.jetbrains.dokka.links.DRI + + +class DokkaExternalLocationProviderFactory : ExternalLocationProviderFactory by ExternalLocationProviderFactoryWithCache( + object : ExternalLocationProviderFactory { + override fun getExternalLocationProvider(param: String): ExternalLocationProvider? = + when (param) { + "kotlin-website-html", "html" -> DokkaExternalLocationProvider(param, ".html") + "markdown" -> DokkaExternalLocationProvider(param, ".md") + else -> null + } + } +) + +class DokkaExternalLocationProvider(override val param: String, val extension: String) : ExternalLocationProvider { + + override fun DRI.toLocation(): String { // TODO: classes without packages? + + val classNamesChecked = classNames ?: return "${packageName ?: ""}/index$extension" + + val classLink = (listOfNotNull(packageName) + classNamesChecked.split('.')).joinToString( + "/", + transform = ::identifierToFilename + ) + + val callableChecked = callable ?: return "$classLink/index$extension" + + return "$classLink/${identifierToFilename( + callableChecked.name + )}$extension" + } +} diff --git a/plugins/base/src/main/kotlin/resolvers/external/ExternalLocationProviderFactory.kt b/plugins/base/src/main/kotlin/resolvers/external/ExternalLocationProviderFactory.kt new file mode 100644 index 00000000..83de9911 --- /dev/null +++ b/plugins/base/src/main/kotlin/resolvers/external/ExternalLocationProviderFactory.kt @@ -0,0 +1,26 @@ +package org.jetbrains.dokka.base.resolvers.external + +import org.jetbrains.dokka.links.DRI +import java.util.concurrent.ConcurrentHashMap + + +interface ExternalLocationProvider { + + val param: String + fun DRI.toLocation(): String +} + +interface ExternalLocationProviderFactory { + + fun getExternalLocationProvider(param: String): ExternalLocationProvider? +} + +class ExternalLocationProviderFactoryWithCache(val ext: ExternalLocationProviderFactory) : ExternalLocationProviderFactory { + + private val locationProviders = ConcurrentHashMap<String, CacheWrapper>() + + override fun getExternalLocationProvider(param: String): ExternalLocationProvider? = + locationProviders.getOrPut(param) { CacheWrapper(ext.getExternalLocationProvider(param)) }.provider +} + +private class CacheWrapper(val provider: ExternalLocationProvider?)
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/resolvers/external/JavadocExternalLocationProviderFactory.kt b/plugins/base/src/main/kotlin/resolvers/external/JavadocExternalLocationProviderFactory.kt new file mode 100644 index 00000000..c52c9bbb --- /dev/null +++ b/plugins/base/src/main/kotlin/resolvers/external/JavadocExternalLocationProviderFactory.kt @@ -0,0 +1,37 @@ +package org.jetbrains.dokka.base.resolvers.external + +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.utilities.htmlEscape + +class JavadocExternalLocationProviderFactory : ExternalLocationProviderFactory by ExternalLocationProviderFactoryWithCache( + object : ExternalLocationProviderFactory { + override fun getExternalLocationProvider(param: String): ExternalLocationProvider? = + when(param) { + "javadoc1" -> JavadocExternalLocationProvider(param, "()", ", ") // Covers JDK 1 - 7 + "javadoc8" -> JavadocExternalLocationProvider(param, "--", "-") // Covers JDK 8 - 9 + "javadoc10" -> JavadocExternalLocationProvider(param, "()", ",") // Covers JDK 10 + else -> null + } + } +) + +class JavadocExternalLocationProvider(override val param: String, val brackets: String, val separator: String) : ExternalLocationProvider { + + override fun DRI.toLocation(): String { + + val packageLink = packageName?.replace(".", "/") + if (classNames == null) { + return "$packageLink/package-summary.html".htmlEscape() + } + val classLink = if (packageLink == null) "$classNames.html" else "$packageLink/$classNames.html" + val callableChecked = callable ?: return classLink.htmlEscape() + + val callableLink = "$classLink#" + + callableChecked.name + + "${brackets.first()}" + + callableChecked.params.joinToString(separator) + + "${brackets.last()}" + + return callableLink.htmlEscape() + } +}
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/resolvers/local/BaseLocationProvider.kt b/plugins/base/src/main/kotlin/resolvers/local/BaseLocationProvider.kt new file mode 100644 index 00000000..4204006e --- /dev/null +++ b/plugins/base/src/main/kotlin/resolvers/local/BaseLocationProvider.kt @@ -0,0 +1,142 @@ +package org.jetbrains.dokka.base.resolvers.local + +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.base.DokkaBase +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.plugin +import org.jetbrains.dokka.plugability.query +import java.net.HttpURLConnection +import java.net.URL +import java.net.URLConnection +import java.util.concurrent.locks.ReentrantReadWriteLock +import kotlin.concurrent.read +import kotlin.concurrent.write + +abstract class BaseLocationProvider(protected val dokkaContext: DokkaContext) : LocationProvider { + + protected val externalLocationProviderFactories = + dokkaContext.plugin<DokkaBase>().query { externalLocationProviderFactory } + private val cache: MutableMap<URL, DefaultLocationProvider.LocationInfo> = mutableMapOf() + private val lock = ReentrantReadWriteLock() + + protected fun getExternalLocation( + dri: DRI, + sourceSets: Set<DokkaSourceSet> + ): String { + val jdkToExternalDocumentationLinks = dokkaContext.configuration.sourceSets + .filter { sourceSet -> + sourceSets.contains(sourceSet) + } + .groupBy({ it.jdkVersion }, { it.externalDocumentationLinks }) + .map { it.key to it.value.flatten().distinct() }.toMap() + + val toResolve: MutableMap<Int, MutableList<DokkaConfiguration.ExternalDocumentationLink>> = mutableMapOf() + for ((jdk, links) in jdkToExternalDocumentationLinks) { + for (link in links) { + val info = lock.read { cache[link.packageListUrl] } + if (info == null) { + toResolve.getOrPut(jdk) { mutableListOf() }.add(link) + } else if (info.packages.contains(dri.packageName)) { + return link.url.toExternalForm() + getLink(dri, info) + } + } + } + // Not in cache, resolve packageLists + for ((jdk, links) in toResolve) { + for (link in links) { + if (dokkaContext.configuration.offlineMode && link.packageListUrl.protocol.toLowerCase() != "file") + continue + val locationInfo = + loadPackageList(jdk, link.packageListUrl) + if (locationInfo.packages.contains(dri.packageName)) { + return link.url.toExternalForm() + getLink(dri, locationInfo) + } + } + toResolve.remove(jdk) + } + return "" + } + + private fun getLink(dri: DRI, locationInfo: DefaultLocationProvider.LocationInfo): String = + locationInfo.locations[dri.packageName + "." + dri.classNames] + ?: // Not sure if it can be here, previously it shadowed only kotlin/dokka related sources, here it shadows both dokka/javadoc, cause I cannot distinguish what LocationProvider has been hypothetically chosen + if (locationInfo.externalLocationProvider != null) + with(locationInfo.externalLocationProvider) { + dri.toLocation() + } + else + throw IllegalStateException("Have not found any convenient ExternalLocationProvider for $dri DRI!") + + private fun loadPackageList(jdk: Int, url: URL): DefaultLocationProvider.LocationInfo = lock.write { + val packageListStream = url.doOpenConnectionToReadContent().getInputStream() + val (params, packages) = + packageListStream + .bufferedReader() + .useLines { lines -> lines.partition { it.startsWith(DOKKA_PARAM_PREFIX) } } + + val paramsMap = params.asSequence() + .map { it.removePrefix(DOKKA_PARAM_PREFIX).split(":", limit = 2) } + .groupBy({ (key, _) -> key }, { (_, value) -> value }) + + val format = paramsMap["format"]?.singleOrNull() ?: when { + jdk < 8 -> "javadoc1" // Covers JDK 1 - 7 + jdk < 10 -> "javadoc8" // Covers JDK 8 - 9 + else -> "javadoc10" // Covers JDK 10+ + } + + val locations = paramsMap["location"].orEmpty() + .map { it.split("\u001f", limit = 2) } + .map { (key, value) -> key to value } + .toMap() + + val externalLocationProvider = + externalLocationProviderFactories.asSequence().map { it.getExternalLocationProvider(format) } + .filterNotNull().take(1).firstOrNull() + + val info = DefaultLocationProvider.LocationInfo( + externalLocationProvider, + packages.toSet(), + locations + ) + cache[url] = info + return info + } + + private fun URL.doOpenConnectionToReadContent(timeout: Int = 10000, redirectsAllowed: Int = 16): URLConnection { + val connection = this.openConnection().apply { + connectTimeout = timeout + readTimeout = timeout + } + + when (connection) { + is HttpURLConnection -> { + return when (connection.responseCode) { + in 200..299 -> { + connection + } + HttpURLConnection.HTTP_MOVED_PERM, + HttpURLConnection.HTTP_MOVED_TEMP, + HttpURLConnection.HTTP_SEE_OTHER -> { + if (redirectsAllowed > 0) { + val newUrl = connection.getHeaderField("Location") + URL(newUrl).doOpenConnectionToReadContent(timeout, redirectsAllowed - 1) + } else { + throw RuntimeException("Too many redirects") + } + } + else -> { + throw RuntimeException("Unhandled http code: ${connection.responseCode}") + } + } + } + else -> return connection + } + } + + companion object { + const val DOKKA_PARAM_PREFIX = "\$dokka." + } + +} diff --git a/plugins/base/src/main/kotlin/resolvers/local/DefaultLocationProvider.kt b/plugins/base/src/main/kotlin/resolvers/local/DefaultLocationProvider.kt new file mode 100644 index 00000000..1df0a700 --- /dev/null +++ b/plugins/base/src/main/kotlin/resolvers/local/DefaultLocationProvider.kt @@ -0,0 +1,217 @@ +package org.jetbrains.dokka.base.resolvers.local + +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.base.resolvers.anchors.SymbolAnchorHint +import org.jetbrains.dokka.base.resolvers.external.ExternalLocationProvider +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.withDescendants +import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.plugability.DokkaContext +import java.net.HttpURLConnection +import java.net.URL +import java.net.URLConnection +import java.util.* + +private const val PAGE_WITH_CHILDREN_SUFFIX = "index" + +open class DefaultLocationProvider( + protected val pageGraphRoot: RootPageNode, + dokkaContext: DokkaContext +) : BaseLocationProvider(dokkaContext) { + protected open val extension = ".html" + + protected val pagesIndex: Map<DRI, ContentPage> = pageGraphRoot.withDescendants().filterIsInstance<ContentPage>() + .map { it.dri.map { dri -> dri to it } }.flatten() + .groupingBy { it.first } + .aggregate { dri, _, (_, page), first -> + if (first) page else throw AssertionError("Multiple pages associated with dri: $dri") + } + + protected val anchorsIndex = pageGraphRoot.withDescendants().filterIsInstance<ContentPage>() + .flatMap { page -> + page.content.withDescendants() + .filter { it.extra[SymbolAnchorHint] != null } + .mapNotNull { it.dci.dri.singleOrNull() } + .distinct() + .map { it to page } + }.toMap() + + + protected val pathsIndex: Map<PageNode, List<String>> = IdentityHashMap<PageNode, List<String>>().apply { + fun registerPath(page: PageNode, prefix: List<String>) { + val newPrefix = prefix + page.pathName + put(page, newPrefix) + page.children.forEach { registerPath(it, newPrefix) } + } + put(pageGraphRoot, emptyList()) + pageGraphRoot.children.forEach { registerPath(it, emptyList()) } + } + + override fun resolve(node: PageNode, context: PageNode?, skipExtension: Boolean): String = + pathTo(node, context) + if (!skipExtension) extension else "" + + override fun resolve(dri: DRI, sourceSets: Set<DokkaSourceSet>, context: PageNode?): String = + pagesIndex[dri]?.let { resolve(it, context) } + ?: anchorsIndex[dri]?.let { resolve(it, context) + "#$dri" } + // Not found in PageGraph, that means it's an external link + ?: getExternalLocation(dri, sourceSets) + + override fun resolveRoot(node: PageNode): String = + pathTo(pageGraphRoot, node).removeSuffix(PAGE_WITH_CHILDREN_SUFFIX) + + override fun ancestors(node: PageNode): List<PageNode> = + generateSequence(node) { it.parent() }.toList() + + protected open fun pathTo(node: PageNode, context: PageNode?): String { + fun pathFor(page: PageNode) = pathsIndex[page] ?: throw AssertionError( + "${page::class.simpleName}(${page.name}) does not belong to current page graph so it is impossible to compute its path" + ) + + val contextNode = + if (context !is ClasslikePageNode && context?.children?.isEmpty() == true && context.parent() != null) context.parent() else context + val nodePath = pathFor(node) + val contextPath = contextNode?.let { pathFor(it) }.orEmpty() + + val commonPathElements = nodePath.asSequence().zip(contextPath.asSequence()) + .takeWhile { (a, b) -> a == b }.count() + + return (List(contextPath.size - commonPathElements) { ".." } + nodePath.drop(commonPathElements) + + if (node is ClasslikePageNode || node.children.isNotEmpty()) + listOf(PAGE_WITH_CHILDREN_SUFFIX) + else + emptyList() + ).joinToString("/") + } + + private fun PageNode.parent() = pageGraphRoot.parentMap[this] + + private val cache: MutableMap<URL, LocationInfo> = mutableMapOf() + + private fun getLocation( + dri: DRI, + jdkToExternalDocumentationLinks: Map<Int, List<DokkaConfiguration.ExternalDocumentationLink>> + ): String { + val toResolve: MutableMap<Int, MutableList<DokkaConfiguration.ExternalDocumentationLink>> = mutableMapOf() + for ((jdk, links) in jdkToExternalDocumentationLinks) { + for (link in links) { + val info = cache[link.packageListUrl] + if (info == null) { + toResolve.getOrPut(jdk) { mutableListOf() }.add(link) + } else if (info.packages.contains(dri.packageName)) { + return link.url.toExternalForm() + getLink(dri, info) + } + } + } + // Not in cache, resolve packageLists + for ((jdk, links) in toResolve) { + for (link in links) { + if(dokkaContext.configuration.offlineMode && link.packageListUrl.protocol.toLowerCase() != "file") + continue + val locationInfo = + loadPackageList(jdk, link.packageListUrl) + if (locationInfo.packages.contains(dri.packageName)) { + return link.url.toExternalForm() + getLink(dri, locationInfo) + } + } + toResolve.remove(jdk) + } + return "" + } + + private fun getLink(dri: DRI, locationInfo: LocationInfo): String = + locationInfo.locations[dri.packageName + "." + dri.classNames] + ?: // Not sure if it can be here, previously it shadowed only kotlin/dokka related sources, here it shadows both dokka/javadoc, cause I cannot distinguish what LocationProvider has been hypothetically chosen + if (locationInfo.externalLocationProvider != null) + with(locationInfo.externalLocationProvider) { + dri.toLocation() + } + else + throw IllegalStateException("Have not found any convenient ExternalLocationProvider for $dri DRI!") + + private fun loadPackageList(jdk: Int, url: URL): LocationInfo { + val packageListStream = url.doOpenConnectionToReadContent().getInputStream() + val (params, packages) = + packageListStream + .bufferedReader() + .useLines { lines -> lines.partition { it.startsWith(DOKKA_PARAM_PREFIX) } } + + val paramsMap = params.asSequence() + .map { it.removePrefix(DOKKA_PARAM_PREFIX).split(":", limit = 2) } + .groupBy({ (key, _) -> key }, { (_, value) -> value }) + + val format = paramsMap["format"]?.singleOrNull() ?: when { + jdk < 8 -> "javadoc1" // Covers JDK 1 - 7 + jdk < 10 -> "javadoc8" // Covers JDK 8 - 9 + else -> "javadoc10" // Covers JDK 10+ + } + + val locations = paramsMap["location"].orEmpty() + .map { it.split("\u001f", limit = 2) } + .map { (key, value) -> key to value } + .toMap() + + val externalLocationProvider = + externalLocationProviderFactories.asSequence().map { it.getExternalLocationProvider(format) } + .filterNotNull().take(1).firstOrNull() + + val info = LocationInfo( + externalLocationProvider, + packages.toSet(), + locations + ) + cache[url] = info + return info + } + + private fun URL.doOpenConnectionToReadContent(timeout: Int = 10000, redirectsAllowed: Int = 16): URLConnection { + val connection = this.openConnection().apply { + connectTimeout = timeout + readTimeout = timeout + } + + when (connection) { + is HttpURLConnection -> { + return when (connection.responseCode) { + in 200..299 -> { + connection + } + HttpURLConnection.HTTP_MOVED_PERM, + HttpURLConnection.HTTP_MOVED_TEMP, + HttpURLConnection.HTTP_SEE_OTHER -> { + if (redirectsAllowed > 0) { + val newUrl = connection.getHeaderField("Location") + URL(newUrl).doOpenConnectionToReadContent(timeout, redirectsAllowed - 1) + } else { + throw RuntimeException("Too many redirects") + } + } + else -> { + throw RuntimeException("Unhandled http code: ${connection.responseCode}") + } + } + } + else -> return connection + } + } + + data class LocationInfo( + val externalLocationProvider: ExternalLocationProvider?, + val packages: Set<String>, + val locations: Map<String, String> + ) +} + +private val reservedFilenames = setOf("index", "con", "aux", "lst", "prn", "nul", "eof", "inp", "out") + +internal fun identifierToFilename(name: String): String { + if (name.isEmpty()) return "--root--" + val escaped = name.replace("<|>".toRegex(), "-") + val lowercase = escaped.replace("[A-Z]".toRegex()) { matchResult -> "-" + matchResult.value.toLowerCase() } + return if (lowercase in reservedFilenames) "--$lowercase--" else lowercase +} + +private val PageNode.pathName: String + get() = if (this is PackagePageNode) name else identifierToFilename( + name + ) diff --git a/plugins/base/src/main/kotlin/resolvers/local/DefaultLocationProviderFactory.kt b/plugins/base/src/main/kotlin/resolvers/local/DefaultLocationProviderFactory.kt new file mode 100644 index 00000000..442d2e6d --- /dev/null +++ b/plugins/base/src/main/kotlin/resolvers/local/DefaultLocationProviderFactory.kt @@ -0,0 +1,23 @@ +package org.jetbrains.dokka.base.resolvers.local + +import org.jetbrains.dokka.pages.MultimoduleRootPageNode +import org.jetbrains.dokka.pages.RootPageNode +import org.jetbrains.dokka.plugability.DokkaContext +import java.util.* +import java.util.concurrent.ConcurrentHashMap + +class DefaultLocationProviderFactory(private val context: DokkaContext) : LocationProviderFactory { + + private val cache = ConcurrentHashMap<CacheWrapper, LocationProvider>() + + override fun getLocationProvider(pageNode: RootPageNode) = cache.computeIfAbsent(CacheWrapper(pageNode)) { + if (pageNode.children.first() is MultimoduleRootPageNode) MultimoduleLocationProvider(pageNode, context) + else DefaultLocationProvider(pageNode, context) + } +} + +private class CacheWrapper(val pageNode: RootPageNode) { + override fun equals(other: Any?) = other is CacheWrapper && other.pageNode == this.pageNode + + override fun hashCode() = System.identityHashCode(pageNode) +}
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/resolvers/local/LocationProvider.kt b/plugins/base/src/main/kotlin/resolvers/local/LocationProvider.kt new file mode 100644 index 00000000..745636d0 --- /dev/null +++ b/plugins/base/src/main/kotlin/resolvers/local/LocationProvider.kt @@ -0,0 +1,18 @@ +package org.jetbrains.dokka.base.resolvers.local + +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.pages.PageNode +import org.jetbrains.dokka.pages.RootPageNode + +interface LocationProvider { + fun resolve(dri: DRI, sourceSets: Set<DokkaSourceSet>, context: PageNode? = null): String + fun resolve(node: PageNode, context: PageNode? = null, skipExtension: Boolean = false): String + fun resolveRoot(node: PageNode): String + fun ancestors(node: PageNode): List<PageNode> +} + +interface LocationProviderFactory { + fun getLocationProvider(pageNode: RootPageNode): LocationProvider +} + diff --git a/plugins/base/src/main/kotlin/resolvers/local/MultimoduleLocationProvider.kt b/plugins/base/src/main/kotlin/resolvers/local/MultimoduleLocationProvider.kt new file mode 100644 index 00000000..54aded35 --- /dev/null +++ b/plugins/base/src/main/kotlin/resolvers/local/MultimoduleLocationProvider.kt @@ -0,0 +1,32 @@ +package org.jetbrains.dokka.base.resolvers.local + +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.pages.PageNode +import org.jetbrains.dokka.pages.RootPageNode +import org.jetbrains.dokka.plugability.DokkaContext + +class MultimoduleLocationProvider(private val root: RootPageNode, context: DokkaContext) : LocationProvider { + + private val defaultLocationProvider = DefaultLocationProvider(root, context) + + val paths = context.configuration.modules.map { + it.name to it.path + }.toMap() + + override fun resolve(dri: DRI, sourceSets: Set<DokkaSourceSet>, context: PageNode?): String = + dri.takeIf { it.packageName == MULTIMODULE_PACKAGE_PLACEHOLDER }?.classNames?.let { paths[it] }?.let { + "$it/${identifierToFilename(dri.classNames.orEmpty())}/index.html" + } ?: defaultLocationProvider.resolve(dri, sourceSets, context) + + override fun resolve(node: PageNode, context: PageNode?, skipExtension: Boolean): String = + defaultLocationProvider.resolve(node, context, skipExtension) + + override fun resolveRoot(node: PageNode): String = defaultLocationProvider.resolveRoot(node) + + override fun ancestors(node: PageNode): List<PageNode> = listOf(root) + + companion object { + const val MULTIMODULE_PACKAGE_PLACEHOLDER = ".ext" + } +} diff --git a/plugins/base/src/main/kotlin/signatures/JvmSignatureUtils.kt b/plugins/base/src/main/kotlin/signatures/JvmSignatureUtils.kt new file mode 100644 index 00000000..689f6db5 --- /dev/null +++ b/plugins/base/src/main/kotlin/signatures/JvmSignatureUtils.kt @@ -0,0 +1,141 @@ +package org.jetbrains.dokka.base.signatures + +import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.properties.WithExtraProperties +import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet + +interface JvmSignatureUtils { + + fun PageContentBuilder.DocumentableContentBuilder.annotationsBlock(d: Documentable) + + fun PageContentBuilder.DocumentableContentBuilder.annotationsInline(d: Documentable) + + fun <T : Documentable> WithExtraProperties<T>.modifiers(): SourceSetDependent<Set<ExtraModifiers>> + + fun Collection<ExtraModifiers>.toSignatureString(): String = + joinToString("") { it.name.toLowerCase() + " " } + + fun <T : Documentable> WithExtraProperties<T>.annotations(): SourceSetDependent<List<Annotations.Annotation>> = + extra[Annotations]?.content ?: emptyMap() + + private fun PageContentBuilder.DocumentableContentBuilder.annotations( + d: Documentable, + ignored: Set<Annotations.Annotation>, + styles: Set<Style>, + operation: PageContentBuilder.DocumentableContentBuilder.(Annotations.Annotation) -> Unit + ): Unit = when (d) { + is DFunction -> d.annotations() + is DProperty -> d.annotations() + is DClass -> d.annotations() + is DInterface -> d.annotations() + is DObject -> d.annotations() + is DEnum -> d.annotations() + is DAnnotation -> d.annotations() + is DTypeParameter -> d.annotations() + is DEnumEntry -> d.annotations() + is DTypeAlias -> d.annotations() + is DParameter -> d.annotations() + else -> null + }?.let { + it.entries.forEach { + it.value.filter { it !in ignored && it.mustBeDocumented }.takeIf { it.isNotEmpty() }?.let { annotations -> + group(sourceSets = setOf(it.key), styles = styles, kind = ContentKind.Annotations) { + annotations.forEach { + operation(it) + } + } + } + } + } ?: Unit + + fun PageContentBuilder.DocumentableContentBuilder.toSignatureString( + a: Annotations.Annotation, + renderAtStrategy: AtStrategy, + listBrackets: Pair<Char, Char>, + classExtension: String + ) { + + when (renderAtStrategy) { + is All, is OnlyOnce -> text("@") + is Never -> Unit + } + link(a.dri.classNames!!, a.dri) + text("(") + a.params.entries.forEachIndexed { i, it -> + group(styles = setOf(TextStyle.BreakableAfter)) { + text(it.key + " = ") + when (renderAtStrategy) { + is All -> All + is Never, is OnlyOnce -> Never + }.let { strategy -> + valueToSignature(it.value, strategy, listBrackets, classExtension) + } + if (i != a.params.entries.size - 1) text(", ") + } + } + text(")") + } + + private fun PageContentBuilder.DocumentableContentBuilder.valueToSignature( + a: AnnotationParameterValue, + renderAtStrategy: AtStrategy, + listBrackets: Pair<Char, Char>, + classExtension: String + ): Unit = when (a) { + is AnnotationValue -> toSignatureString(a.annotation, renderAtStrategy, listBrackets, classExtension) + is ArrayValue -> { + text(listBrackets.first.toString()) + a.value.forEachIndexed { i, it -> + group(styles = setOf(TextStyle.BreakableAfter)) { + valueToSignature(it, renderAtStrategy, listBrackets, classExtension) + if (i != a.value.size - 1) text(", ") + } + } + text(listBrackets.second.toString()) + } + is EnumValue -> link(a.enumName, a.enumDri) + is ClassValue -> link(a.className + classExtension, a.classDRI) + is StringValue -> group(styles = setOf(TextStyle.Breakable)) { text(a.value) } + } + + fun PageContentBuilder.DocumentableContentBuilder.annotationsBlockWithIgnored( + d: Documentable, + ignored: Set<Annotations.Annotation>, + renderAtStrategy: AtStrategy, + listBrackets: Pair<Char, Char>, + classExtension: String + ) { + annotations(d, ignored, setOf(TextStyle.Block)) { + group { + toSignatureString(it, renderAtStrategy, listBrackets, classExtension) + } + } + } + + fun PageContentBuilder.DocumentableContentBuilder.annotationsInlineWithIgnored( + d: Documentable, + ignored: Set<Annotations.Annotation>, + renderAtStrategy: AtStrategy, + listBrackets: Pair<Char, Char>, + classExtension: String + ) { + annotations(d, ignored, setOf(TextStyle.Span)) { + toSignatureString(it, renderAtStrategy, listBrackets, classExtension) + text(Typography.nbsp.toString()) + } + } + + fun <T : Documentable> WithExtraProperties<T>.stylesIfDeprecated(sourceSetData: DokkaSourceSet): Set<TextStyle> = + if (extra[Annotations]?.content?.get(sourceSetData)?.any { + it.dri == DRI("kotlin", "Deprecated") + || it.dri == DRI("java.lang", "Deprecated") + } == true) setOf(TextStyle.Strikethrough) else emptySet() +} + +sealed class AtStrategy +object All : AtStrategy() +object OnlyOnce : AtStrategy() +object Never : AtStrategy() diff --git a/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt b/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt new file mode 100644 index 00000000..37e0ea83 --- /dev/null +++ b/plugins/base/src/main/kotlin/signatures/KotlinSignatureProvider.kt @@ -0,0 +1,382 @@ +package org.jetbrains.dokka.base.signatures + +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.Platform +import org.jetbrains.dokka.base.signatures.KotlinSignatureUtils.dri +import org.jetbrains.dokka.base.signatures.KotlinSignatureUtils.driOrNull +import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter +import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder +import org.jetbrains.dokka.links.* +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.Nullable +import org.jetbrains.dokka.model.TypeConstructor +import org.jetbrains.dokka.model.properties.WithExtraProperties +import org.jetbrains.dokka.pages.ContentKind +import org.jetbrains.dokka.pages.ContentNode +import org.jetbrains.dokka.pages.TextStyle +import org.jetbrains.dokka.utilities.DokkaLogger +import kotlin.text.Typography.nbsp + +class KotlinSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLogger) : SignatureProvider, + JvmSignatureUtils by KotlinSignatureUtils { + private val contentBuilder = PageContentBuilder(ctcc, this, logger) + + private val ignoredVisibilities = setOf(JavaVisibility.Public, KotlinVisibility.Public) + private val ignoredModifiers = setOf(JavaModifier.Final, KotlinModifier.Final) + private val ignoredExtraModifiers = setOf( + ExtraModifiers.KotlinOnlyModifiers.TailRec, + ExtraModifiers.KotlinOnlyModifiers.External + ) + private val platformSpecificModifiers: Map<ExtraModifiers, Set<Platform>> = mapOf( + ExtraModifiers.KotlinOnlyModifiers.External to setOf(Platform.js) + ) + + override fun signature(documentable: Documentable): List<ContentNode> = when (documentable) { + is DFunction -> functionSignature(documentable) + is DProperty -> propertySignature(documentable) + is DClasslike -> classlikeSignature(documentable) + is DTypeParameter -> signature(documentable) + is DEnumEntry -> signature(documentable) + is DTypeAlias -> signature(documentable) + else -> throw NotImplementedError( + "Cannot generate signature for ${documentable::class.qualifiedName} ${documentable.name}" + ) + } + + private fun <T> PageContentBuilder.DocumentableContentBuilder.processExtraModifiers(t: T) + where T : Documentable, T : WithExtraProperties<T> { + sourceSetDependentText( + t.modifiers() + .mapValues { entry -> + entry.value.filter { + it !in ignoredExtraModifiers || entry.key.analysisPlatform in (platformSpecificModifiers[it] + ?: emptySet()) + } + } + ) { + it.toSignatureString() + } + } + + private fun signature(e: DEnumEntry): List<ContentNode> = + e.sourceSets.map { + contentBuilder.contentFor( + e, + ContentKind.Symbol, + setOf(TextStyle.Monospace, TextStyle.Block) + e.stylesIfDeprecated(it), + sourceSets = setOf(it) + ) { + group(styles = setOf(TextStyle.Block)) { + annotationsBlock(e) + link(e.name, e.dri, styles = emptySet()) + e.extra[ConstructorValues]?.let { constructorValues -> + constructorValues.values[it] + text(constructorValues.values[it]?.joinToString(prefix = "(", postfix = ")") ?: "") + } + } + } + } + + private fun actualTypealiasedSignature(c: DClasslike, sourceSet: DokkaSourceSet, aliasedType: Bound) = + contentBuilder.contentFor( + c, + ContentKind.Symbol, + setOf(TextStyle.Monospace) + ((c as? WithExtraProperties<out Documentable>)?.stylesIfDeprecated(sourceSet) + ?: emptySet()), + sourceSets = setOf(sourceSet) + ) { + text("actual typealias ") + link(c.name.orEmpty(), c.dri) + text(" = ") + signatureForProjection(aliasedType) + } + + @Suppress("UNCHECKED_CAST") + private fun <T : DClasslike> classlikeSignature(c: T): List<ContentNode> = + c.sourceSets.map { sourceSetData -> + (c as? WithExtraProperties<out DClasslike>)?.extra?.get(ActualTypealias)?.underlyingType?.get(sourceSetData) + ?.let { + actualTypealiasedSignature(c, sourceSetData, it) + } ?: regularSignature(c, sourceSetData) + } + + + private fun regularSignature(c: DClasslike, sourceSet: DokkaSourceSet) = + contentBuilder.contentFor( + c, + ContentKind.Symbol, + setOf(TextStyle.Monospace) + ((c as? WithExtraProperties<out Documentable>)?.stylesIfDeprecated(sourceSet) + ?: emptySet()), + sourceSets = setOf(sourceSet) + ) { + annotationsBlock(c) + text(c.visibility[sourceSet]?.takeIf { it !in ignoredVisibilities }?.name?.let { "$it " } ?: "") + if (c is DClass) { + text( + if (c.modifier[sourceSet] !in ignoredModifiers) + when { + c.extra[AdditionalModifiers]?.content?.contains(ExtraModifiers.KotlinOnlyModifiers.Data) == true -> "" + c.modifier[sourceSet] is JavaModifier.Empty -> "${KotlinModifier.Open.name} " + else -> c.modifier[sourceSet]?.name?.let { "$it " } ?: "" + } + else + "" + ) + } + if (c is DInterface) { + c.extra[AdditionalModifiers]?.content?.let { additionalModifiers -> + sourceSetDependentText(additionalModifiers, setOf(sourceSet)) { extraModifiers -> + if (ExtraModifiers.KotlinOnlyModifiers.Fun in extraModifiers) "fun " + else "" + } + } + } + when (c) { + is DClass -> { + processExtraModifiers(c) + text("class ") + } + is DInterface -> { + processExtraModifiers(c) + text("interface ") + } + is DEnum -> { + processExtraModifiers(c) + text("enum ") + } + is DObject -> { + processExtraModifiers(c) + text("object ") + } + is DAnnotation -> { + processExtraModifiers(c) + text("annotation class ") + } + } + link(c.name!!, c.dri) + if (c is WithGenerics) { + list(c.generics, prefix = "<", suffix = "> ") { + +buildSignature(it) + } + } + if (c is WithConstructors) { + val pConstructor = c.constructors.singleOrNull { it.extra[PrimaryConstructorExtra] != null } + if (pConstructor?.sourceSets?.contains(sourceSet) == true) { + if (pConstructor.annotations().values.any { it.isNotEmpty() }) { + text(nbsp.toString()) + annotationsInline(pConstructor) + text("constructor") + } + list( + pConstructor.parameters, + "(", + ")", + ",", + pConstructor.sourceSets.toSet() + ) { + annotationsInline(it) + text(it.name ?: "", styles = mainStyles.plus(TextStyle.Bold)) + text(": ") + signatureForProjection(it.type) + } + } + } + if (c is WithSupertypes) { + c.supertypes.filter { it.key == sourceSet }.map { (s, dris) -> + list(dris, prefix = " : ", sourceSets = setOf(s)) { + link(it.dri.sureClassNames, it.dri, sourceSets = setOf(s)) + } + } + } + } + + private fun propertySignature(p: DProperty) = + p.sourceSets.map { + contentBuilder.contentFor( + p, + ContentKind.Symbol, + setOf(TextStyle.Monospace) + p.stylesIfDeprecated(it), + sourceSets = setOf(it) + ) { + annotationsBlock(p) + text(p.visibility[it].takeIf { it !in ignoredVisibilities }?.name?.let { "$it " } ?: "") + text( + p.modifier[it].takeIf { it !in ignoredModifiers }?.let { + if (it is JavaModifier.Empty) KotlinModifier.Open else it + }?.name?.let { "$it " } ?: "" + ) + text(p.modifiers()[it]?.toSignatureString() ?: "") + p.setter?.let { text("var ") } ?: text("val ") + list(p.generics, prefix = "<", suffix = "> ") { + +buildSignature(it) + } + p.receiver?.also { + signatureForProjection(it.type) + text(".") + } + link(p.name, p.dri) + text(": ") + signatureForProjection(p.type) + } + } + + private fun functionSignature(f: DFunction) = + f.sourceSets.map { + contentBuilder.contentFor( + f, + ContentKind.Symbol, + setOf(TextStyle.Monospace) + f.stylesIfDeprecated(it), + sourceSets = setOf(it) + ) { + annotationsBlock(f) + text(f.visibility[it]?.takeIf { it !in ignoredVisibilities }?.name?.let { "$it " } ?: "") + text(f.modifier[it]?.takeIf { it !in ignoredModifiers }?.let { + if (it is JavaModifier.Empty) KotlinModifier.Open else it + }?.name?.let { "$it " } ?: "" + ) + text(f.modifiers()[it]?.toSignatureString() ?: "") + text("fun ") + list(f.generics, prefix = "<", suffix = "> ") { + +buildSignature(it) + } + f.receiver?.also { + signatureForProjection(it.type) + text(".") + } + link(f.name, f.dri) + text("(") + list(f.parameters) { + annotationsInline(it) + processExtraModifiers(it) + text(it.name!!) + text(": ") + signatureForProjection(it.type) + } + text(")") + if (f.documentReturnType()) { + text(": ") + signatureForProjection(f.type) + } + } + } + + private fun DFunction.documentReturnType() = when { + this.isConstructor -> false + this.type is TypeConstructor && (this.type as TypeConstructor).dri == DriOfUnit -> false + this.type is Void -> false + else -> true + } + + private fun signature(t: DTypeAlias) = + t.sourceSets.map { + contentBuilder.contentFor(t, styles = t.stylesIfDeprecated(it), sourceSets = setOf(it)) { + t.underlyingType.entries.groupBy({ it.value }, { it.key }).map { (type, platforms) -> + +contentBuilder.contentFor( + t, + ContentKind.Symbol, + setOf(TextStyle.Monospace), + sourceSets = platforms.toSet() + ) { + text(t.visibility[it]?.takeIf { it !in ignoredVisibilities }?.name?.let { "$it " } ?: "") + processExtraModifiers(t) + text("typealias ") + signatureForProjection(t.type) + text(" = ") + signatureForTypealiasTarget(t, type) + } + } + } + } + + private fun signature(t: DTypeParameter) = + t.sourceSets.map { + contentBuilder.contentFor(t, styles = t.stylesIfDeprecated(it), sourceSets = setOf(it)) { + link(t.name, t.dri.withTargetToDeclaration()) + list(t.bounds, prefix = " : ") { + signatureForProjection(it) + } + } + } + + private fun PageContentBuilder.DocumentableContentBuilder.signatureForTypealiasTarget( + typeAlias: DTypeAlias, bound: Bound + ) { + signatureForProjection( + p = bound, + showFullyQualifiedName = + bound.driOrNull?.packageName != typeAlias.dri.packageName && + bound.driOrNull?.packageName != "kotlin" + ) + } + + private fun PageContentBuilder.DocumentableContentBuilder.signatureForProjection( + p: Projection, showFullyQualifiedName: Boolean = false + ): Unit = + when (p) { + is OtherParameter -> link(p.name, p.declarationDRI) + + is TypeConstructor -> if (p.function) + +funType(mainDRI.single(), mainSourcesetData, p) + else + group(styles = emptySet()) { + val linkText = if (showFullyQualifiedName && p.dri.packageName != null) { + "${p.dri.packageName}.${p.dri.classNames.orEmpty()}" + } else p.dri.classNames.orEmpty() + + link(linkText, p.dri) + list(p.projections, prefix = "<", suffix = ">") { + signatureForProjection(it, showFullyQualifiedName) + } + } + + is Variance -> group(styles = emptySet()) { + text(p.kind.toString() + " ") + signatureForProjection(p.inner, showFullyQualifiedName) + } + + is Star -> text("*") + + is Nullable -> group(styles = emptySet()) { + signatureForProjection(p.inner, showFullyQualifiedName) + text("?") + } + + is JavaObject -> link("Any", DriOfAny) + is Void -> link("Unit", DriOfUnit) + is PrimitiveJavaType -> signatureForProjection(p.translateToKotlin(), showFullyQualifiedName) + is Dynamic -> text("dynamic") + is UnresolvedBound -> text(p.name) + } + + private fun funType(dri: DRI, sourceSets: Set<DokkaSourceSet>, type: TypeConstructor) = + contentBuilder.contentFor(dri, sourceSets, ContentKind.Main) { + if (type.extension) { + signatureForProjection(type.projections.first()) + text(".") + } + + val args = if (type.extension) + type.projections.drop(1) + else + type.projections + + text("(") + args.subList(0, args.size - 1).forEachIndexed { i, arg -> + signatureForProjection(arg) + if (i < args.size - 2) text(", ") + } + text(") -> ") + signatureForProjection(args.last()) + } +} + +private fun PrimitiveJavaType.translateToKotlin() = TypeConstructor( + dri = dri, + projections = emptyList() +) + +val TypeConstructor.function + get() = modifier == FunctionModifiers.FUNCTION || modifier == FunctionModifiers.EXTENSION + +val TypeConstructor.extension + get() = modifier == FunctionModifiers.EXTENSION diff --git a/plugins/base/src/main/kotlin/signatures/KotlinSignatureUtils.kt b/plugins/base/src/main/kotlin/signatures/KotlinSignatureUtils.kt new file mode 100644 index 00000000..0a10875a --- /dev/null +++ b/plugins/base/src/main/kotlin/signatures/KotlinSignatureUtils.kt @@ -0,0 +1,48 @@ +package org.jetbrains.dokka.base.signatures + +import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.links.DriOfAny +import org.jetbrains.dokka.links.DriOfUnit +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.properties.WithExtraProperties + +object KotlinSignatureUtils : JvmSignatureUtils { + + private val strategy = OnlyOnce + private val listBrackets = Pair('[', ']') + private val classExtension = "::class" + private val ignoredAnnotations = setOf( + Annotations.Annotation(DRI("kotlin", "SinceKotlin"), emptyMap()), + Annotations.Annotation(DRI("kotlin", "Deprecated"), emptyMap()) + ) + + + override fun PageContentBuilder.DocumentableContentBuilder.annotationsBlock(d: Documentable) = + annotationsBlockWithIgnored(d, ignoredAnnotations, strategy, listBrackets, classExtension) + + override fun PageContentBuilder.DocumentableContentBuilder.annotationsInline(d: Documentable) = + annotationsInlineWithIgnored(d, ignoredAnnotations, strategy, listBrackets, classExtension) + + override fun <T : Documentable> WithExtraProperties<T>.modifiers() = + extra[AdditionalModifiers]?.content?.entries?.map { + it.key to it.value.filterIsInstance<ExtraModifiers.KotlinOnlyModifiers>().toSet() + }?.toMap() ?: emptyMap() + + + val PrimitiveJavaType.dri: DRI get() = DRI("kotlin", name.capitalize()) + + val Bound.driOrNull: DRI? + get() { + return when (this) { + is OtherParameter -> this.declarationDRI + is TypeConstructor -> this.dri + is Nullable -> this.inner.driOrNull + is PrimitiveJavaType -> this.dri + is Void -> DriOfUnit + is JavaObject -> DriOfAny + is Dynamic -> null + is UnresolvedBound -> null + } + } +} diff --git a/plugins/base/src/main/kotlin/signatures/SignatureProvider.kt b/plugins/base/src/main/kotlin/signatures/SignatureProvider.kt new file mode 100644 index 00000000..e1933fb8 --- /dev/null +++ b/plugins/base/src/main/kotlin/signatures/SignatureProvider.kt @@ -0,0 +1,8 @@ +package org.jetbrains.dokka.base.signatures + +import org.jetbrains.dokka.model.Documentable +import org.jetbrains.dokka.pages.ContentNode + +interface SignatureProvider { + fun signature(documentable: Documentable): List<ContentNode> +} diff --git a/plugins/base/src/main/kotlin/transformers/documentables/ActualTypealiasAdder.kt b/plugins/base/src/main/kotlin/transformers/documentables/ActualTypealiasAdder.kt new file mode 100644 index 00000000..1b65fc22 --- /dev/null +++ b/plugins/base/src/main/kotlin/transformers/documentables/ActualTypealiasAdder.kt @@ -0,0 +1,84 @@ +package org.jetbrains.dokka.base.transformers.documentables + +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.properties.WithExtraProperties +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.transformers.documentation.DocumentableTransformer + +class ActualTypealiasAdder : DocumentableTransformer { + + override fun invoke(modules: DModule, context: DokkaContext) = modules.generateTypealiasesMap().let { aliases -> + modules.copy(packages = modules.packages.map { it.copy(classlikes = addActualTypeAliasToClasslikes(it.classlikes, aliases)) }) + } + + private fun DModule.generateTypealiasesMap(): Map<DRI, DTypeAlias> = + packages.flatMap { pkg -> + pkg.typealiases.map { typeAlias -> + typeAlias.dri to typeAlias + } + }.toMap() + + + private fun addActualTypeAliasToClasslikes( + elements: Iterable<DClasslike>, + typealiases: Map<DRI, DTypeAlias> + ): List<DClasslike> = elements.flatMap { + when (it) { + is DClass -> addActualTypeAlias( + it.copy( + classlikes = addActualTypeAliasToClasslikes(it.classlikes, typealiases) + ).let(::listOf), + typealiases + ) + is DEnum -> addActualTypeAlias( + it.copy( + classlikes = addActualTypeAliasToClasslikes(it.classlikes, typealiases) + ).let(::listOf), + typealiases + ) + is DInterface -> addActualTypeAlias( + it.copy( + classlikes = addActualTypeAliasToClasslikes(it.classlikes, typealiases) + ).let(::listOf), + typealiases + ) + is DObject -> addActualTypeAlias( + it.copy( + classlikes = addActualTypeAliasToClasslikes(it.classlikes, typealiases) + ).let(::listOf), + typealiases + ) + is DAnnotation -> addActualTypeAlias( + it.copy( + classlikes = addActualTypeAliasToClasslikes(it.classlikes, typealiases) + ).let(::listOf), + typealiases + ) + else -> throw IllegalStateException("${it::class.qualifiedName} ${it.name} cannot have extra added") + } + } + + private fun <T> addActualTypeAlias( + elements: Iterable<T>, + typealiases: Map<DRI, DTypeAlias> + ): List<T> where T : DClasslike, T : WithExtraProperties<T>, T : WithExpectActual = + elements.map { element -> + if (element.expectPresentInSet != null) { + typealiases[element.dri]?.let { ta -> + element.withNewExtras(element.extra + ActualTypealias(ta.underlyingType)).let { + when(it) { + is DClass -> it.copy(sourceSets = element.sourceSets + ta.sourceSets) + is DEnum -> it.copy(sourceSets = element.sourceSets + ta.sourceSets) + is DInterface -> it.copy(sourceSets = element.sourceSets + ta.sourceSets) + is DObject -> it.copy(sourceSets = element.sourceSets + ta.sourceSets) + is DAnnotation -> it.copy(sourceSets = element.sourceSets + ta.sourceSets) + else -> throw IllegalStateException("${it::class.qualifiedName} ${it.name} cannot have copy its sourceSets") + } + } as T + } ?: element + } else { + element + } + } +}
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/transformers/documentables/DefaultDocumentableMerger.kt b/plugins/base/src/main/kotlin/transformers/documentables/DefaultDocumentableMerger.kt new file mode 100644 index 00000000..c8e4f565 --- /dev/null +++ b/plugins/base/src/main/kotlin/transformers/documentables/DefaultDocumentableMerger.kt @@ -0,0 +1,200 @@ +package org.jetbrains.dokka.base.transformers.documentables + +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.properties.mergeExtras +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.transformers.documentation.DocumentableMerger +import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult + +internal object DefaultDocumentableMerger : DocumentableMerger { + + override fun invoke(modules: Collection<DModule>, context: DokkaContext): DModule { + + val projectName = + modules.fold(modules.first().name) { acc, module -> acc.commonPrefixWith(module.name) } + .takeIf { it.isNotEmpty() } + ?: "project" + + return modules.reduce { left, right -> + val list = listOf(left, right) + DModule( + name = projectName, + packages = merge( + list.flatMap { it.packages }, + DPackage::mergeWith + ), + documentation = list.map { it.documentation }.flatMap { it.entries }.associate { (k,v) -> k to v }, + expectPresentInSet = list.firstNotNullResult { it.expectPresentInSet }, + sourceSets = list.flatMap { it.sourceSets }.toSet() + ).mergeExtras(left, right) + } + } +} + +private fun <T : Documentable> merge(elements: List<T>, reducer: (T, T) -> T): List<T> = + elements.groupingBy { it.dri } + .reduce { _, left, right -> reducer(left, right) } + .values.toList() + +private fun <T> mergeExpectActual( + elements: List<T>, + reducer: (T, T) -> T +): List<T> where T : Documentable, T : WithExpectActual { + + fun analyzeExpectActual(sameDriElements: List<T>) = sameDriElements.reduce(reducer) + + return elements.groupBy { it.dri }.values.map(::analyzeExpectActual) +} + +fun DPackage.mergeWith(other: DPackage): DPackage = copy( + functions = mergeExpectActual(functions + other.functions, DFunction::mergeWith), + properties = mergeExpectActual(properties + other.properties, DProperty::mergeWith), + classlikes = mergeExpectActual(classlikes + other.classlikes, DClasslike::mergeWith), + documentation = documentation + other.documentation, + expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, + typealiases = merge(typealiases + other.typealiases, DTypeAlias::mergeWith), + sourceSets = sourceSets + other.sourceSets +).mergeExtras(this, other) + +fun DFunction.mergeWith(other: DFunction): DFunction = copy( + parameters = merge(this.parameters + other.parameters, DParameter::mergeWith), + receiver = receiver?.let { r -> other.receiver?.let { r.mergeWith(it) } ?: r } ?: other.receiver, + documentation = documentation + other.documentation, + expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, + sources = sources+ other.sources, + visibility = visibility + other.visibility, + modifier = modifier + other.modifier, + sourceSets = sourceSets + other.sourceSets, + generics = merge(generics + other.generics, DTypeParameter::mergeWith) +).mergeExtras(this, other) + +fun DProperty.mergeWith(other: DProperty): DProperty = copy( + receiver = receiver?.let { r -> other.receiver?.let { r.mergeWith(it) } ?: r } ?: other.receiver, + documentation = documentation + other.documentation, + expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, + sources = sources+ other.sources, + visibility = visibility + other.visibility, + modifier = modifier + other.modifier, + sourceSets = sourceSets + other.sourceSets, + getter = getter?.let { g -> other.getter?.let { g.mergeWith(it) } ?: g } ?: other.getter, + setter = setter?.let { s -> other.setter?.let { s.mergeWith(it) } ?: s } ?: other.setter, + generics = merge(generics + other.generics, DTypeParameter::mergeWith) +).mergeExtras(this, other) + +fun DClasslike.mergeWith(other: DClasslike): DClasslike = when { + this is DClass && other is DClass -> mergeWith(other) + this is DEnum && other is DEnum -> mergeWith(other) + this is DInterface && other is DInterface -> mergeWith(other) + this is DObject && other is DObject -> mergeWith(other) + this is DAnnotation && other is DAnnotation -> mergeWith(other) + else -> throw IllegalStateException("${this::class.qualifiedName} ${this.name} cannot be mergesd with ${other::class.qualifiedName} ${other.name}") +} + +fun DClass.mergeWith(other: DClass): DClass = copy( + constructors = mergeExpectActual( + constructors + other.constructors, + DFunction::mergeWith + ), + functions = mergeExpectActual(functions + other.functions, DFunction::mergeWith), + properties = mergeExpectActual(properties + other.properties, DProperty::mergeWith), + classlikes = mergeExpectActual(classlikes + other.classlikes, DClasslike::mergeWith), + companion = companion?.let { c -> other.companion?.let { c.mergeWith(it) } ?: c } ?: other.companion, + generics = merge(generics + other.generics, DTypeParameter::mergeWith), + modifier = modifier + other.modifier, + supertypes = supertypes + other.supertypes, + documentation = documentation + other.documentation, + expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, + sources = sources+ other.sources, + visibility = visibility + other.visibility, + sourceSets = sourceSets + other.sourceSets +).mergeExtras(this, other) + +fun DEnum.mergeWith(other: DEnum): DEnum = copy( + entries = merge(entries + other.entries, DEnumEntry::mergeWith), + constructors = mergeExpectActual( + constructors + other.constructors, + DFunction::mergeWith + ), + functions = mergeExpectActual(functions + other.functions, DFunction::mergeWith), + properties = mergeExpectActual(properties + other.properties, DProperty::mergeWith), + classlikes = mergeExpectActual(classlikes + other.classlikes, DClasslike::mergeWith), + companion = companion?.let { c -> other.companion?.let { c.mergeWith(it) } ?: c } ?: other.companion, + supertypes = supertypes + other.supertypes, + documentation = documentation + other.documentation, + expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, + sources = sources+ other.sources, + visibility = visibility + other.visibility, + sourceSets = sourceSets + other.sourceSets +).mergeExtras(this, other) + +fun DEnumEntry.mergeWith(other: DEnumEntry): DEnumEntry = copy( + functions = mergeExpectActual(functions + other.functions, DFunction::mergeWith), + properties = mergeExpectActual(properties + other.properties, DProperty::mergeWith), + classlikes = mergeExpectActual(classlikes + other.classlikes, DClasslike::mergeWith), + documentation = documentation + other.documentation, + expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, + sourceSets = sourceSets + other.sourceSets +).mergeExtras(this, other) + +fun DObject.mergeWith(other: DObject): DObject = copy( + functions = mergeExpectActual(functions + other.functions, DFunction::mergeWith), + properties = mergeExpectActual(properties + other.properties, DProperty::mergeWith), + classlikes = mergeExpectActual(classlikes + other.classlikes, DClasslike::mergeWith), + supertypes = supertypes + other.supertypes, + documentation = documentation + other.documentation, + expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, + sources = sources+ other.sources, + visibility = visibility + other.visibility, + sourceSets = sourceSets + other.sourceSets +).mergeExtras(this, other) + +fun DInterface.mergeWith(other: DInterface): DInterface = copy( + functions = mergeExpectActual(functions + other.functions, DFunction::mergeWith), + properties = mergeExpectActual(properties + other.properties, DProperty::mergeWith), + classlikes = mergeExpectActual(classlikes + other.classlikes, DClasslike::mergeWith), + companion = companion?.let { c -> other.companion?.let { c.mergeWith(it) } ?: c } ?: other.companion, + generics = merge(generics + other.generics, DTypeParameter::mergeWith), + supertypes = supertypes + other.supertypes, + documentation = documentation + other.documentation, + expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, + sources = sources+ other.sources, + visibility = visibility + other.visibility, + sourceSets = sourceSets + other.sourceSets +).mergeExtras(this, other) + +fun DAnnotation.mergeWith(other: DAnnotation): DAnnotation = copy( + constructors = mergeExpectActual( + constructors + other.constructors, + DFunction::mergeWith + ), + functions = mergeExpectActual(functions + other.functions, DFunction::mergeWith), + properties = mergeExpectActual(properties + other.properties, DProperty::mergeWith), + classlikes = mergeExpectActual(classlikes + other.classlikes, DClasslike::mergeWith), + companion = companion?.let { c -> other.companion?.let { c.mergeWith(it) } ?: c } ?: other.companion, + documentation = documentation + other.documentation, + expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, + sources = sources+ other.sources, + visibility = visibility + other.visibility, + sourceSets = sourceSets + other.sourceSets, + generics = merge(generics + other.generics, DTypeParameter::mergeWith) +).mergeExtras(this, other) + +fun DParameter.mergeWith(other: DParameter): DParameter = copy( + documentation = documentation + other.documentation, + expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, + sourceSets = sourceSets + other.sourceSets +).mergeExtras(this, other) + +fun DTypeParameter.mergeWith(other: DTypeParameter): DTypeParameter = copy( + documentation = documentation + other.documentation, + expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, + sourceSets = sourceSets + other.sourceSets +).mergeExtras(this, other) + +fun DTypeAlias.mergeWith(other: DTypeAlias): DTypeAlias = copy( + documentation = documentation + other.documentation, + expectPresentInSet = expectPresentInSet ?: other.expectPresentInSet, + underlyingType = underlyingType + other.underlyingType, + visibility = visibility + other.visibility, + sourceSets = sourceSets + other.sourceSets +).mergeExtras(this, other)
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/transformers/documentables/DeprecatedDocumentableFilterTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/DeprecatedDocumentableFilterTransformer.kt new file mode 100644 index 00000000..109aa640 --- /dev/null +++ b/plugins/base/src/main/kotlin/transformers/documentables/DeprecatedDocumentableFilterTransformer.kt @@ -0,0 +1,258 @@ +package org.jetbrains.dokka.base.transformers.documentables + +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.properties.WithExtraProperties +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.transformers.documentation.PreMergeDocumentableTransformer + +class DeprecatedDocumentableFilterTransformer(val context: DokkaContext) : PreMergeDocumentableTransformer { + override fun invoke(modules: List<DModule>) = modules.map { original -> + val sourceSet = original.sourceSets.single() + val packageOptions = + sourceSet.perPackageOptions + original.let { + DeprecatedDocumentableFilter(sourceSet, packageOptions).processModule(it) + } + } + + private class DeprecatedDocumentableFilter( + val globalOptions: DokkaConfiguration.DokkaSourceSet, + val packageOptions: List<DokkaConfiguration.PackageOptions> + ) { + + fun <T> T.isAllowedInPackage(): Boolean where T : WithExtraProperties<T>, T : Documentable { + val packageName = this.dri.packageName + val condition = packageName != null && packageOptions.firstOrNull { + packageName.startsWith(it.prefix) + }?.skipDeprecated + ?: globalOptions.skipDeprecated + + fun T.isDeprecated() = extra[Annotations]?.let { annotations -> + annotations.content.values.flatten().any { + it.dri.toString() == "kotlin/Deprecated///PointingToDeclaration/" + } + } ?: false + + return !(condition && this.isDeprecated()) + } + + fun processModule(original: DModule) = + filterPackages(original.packages).let { (modified, packages) -> + if (!modified) original + else + DModule( + original.name, + packages = packages, + documentation = original.documentation, + sourceSets = original.sourceSets, + extra = original.extra + ) + } + + + private fun filterPackages(packages: List<DPackage>): Pair<Boolean, List<DPackage>> { + var packagesListChanged = false + val filteredPackages = packages.mapNotNull { pckg -> + var modified = false + val functions = filterFunctions(pckg.functions).let { (listModified, list) -> + modified = modified || listModified + list + } + val properties = filterProperties(pckg.properties).let { (listModified, list) -> + modified = modified || listModified + list + } + val classlikes = filterClasslikes(pckg.classlikes).let { (listModified, list) -> + modified = modified || listModified + list + } + when { + !modified -> pckg + else -> { + packagesListChanged = true + DPackage( + pckg.dri, + functions, + properties, + classlikes, + pckg.typealiases, + pckg.documentation, + pckg.expectPresentInSet, + pckg.sourceSets, + pckg.extra + ) + } + } + } + return Pair(packagesListChanged, filteredPackages) + } + + private fun filterFunctions( + functions: List<DFunction> + ) = functions.filter { it.isAllowedInPackage() }.let { + Pair(it.size != functions.size, it) + } + + private fun filterProperties( + properties: List<DProperty> + ): Pair<Boolean, List<DProperty>> = properties.filter { + it.isAllowedInPackage() + }.let { + Pair(properties.size != it.size, it) + } + + private fun filterEnumEntries(entries: List<DEnumEntry>) = + entries.filter { it.isAllowedInPackage() }.map { entry -> + DEnumEntry( + entry.dri, + entry.name, + entry.documentation, + entry.expectPresentInSet, + filterFunctions(entry.functions).second, + filterProperties(entry.properties).second, + filterClasslikes(entry.classlikes).second, + entry.sourceSets, + entry.extra + ) + } + + private fun filterClasslikes( + classlikeList: List<DClasslike> + ): Pair<Boolean, List<DClasslike>> { + var modified = false + return classlikeList.filter { classlike -> + when (classlike) { + is DClass -> classlike.isAllowedInPackage() + is DInterface -> classlike.isAllowedInPackage() + is DEnum -> classlike.isAllowedInPackage() + is DObject -> classlike.isAllowedInPackage() + is DAnnotation -> classlike.isAllowedInPackage() + } + }.map { classlike -> + fun helper(): DClasslike = when (classlike) { + is DClass -> DClass( + classlike.dri, + classlike.name, + filterFunctions(classlike.constructors).let { + modified = modified || it.first; it.second + }, + filterFunctions(classlike.functions).let { + modified = modified || it.first; it.second + }, + filterProperties(classlike.properties).let { + modified = modified || it.first; it.second + }, + filterClasslikes(classlike.classlikes).let { + modified = modified || it.first; it.second + }, + classlike.sources, + classlike.visibility, + classlike.companion, + classlike.generics, + classlike.supertypes, + classlike.documentation, + classlike.expectPresentInSet, + classlike.modifier, + classlike.sourceSets, + classlike.extra + ) + is DAnnotation -> DAnnotation( + classlike.name, + classlike.dri, + classlike.documentation, + classlike.expectPresentInSet, + classlike.sources, + filterFunctions(classlike.functions).let { + modified = modified || it.first; it.second + }, + filterProperties(classlike.properties).let { + modified = modified || it.first; it.second + }, + filterClasslikes(classlike.classlikes).let { + modified = modified || it.first; it.second + }, + classlike.visibility, + classlike.companion, + filterFunctions(classlike.constructors).let { + modified = modified || it.first; it.second + }, + classlike.generics, + classlike.sourceSets, + classlike.extra + ) + is DEnum -> DEnum( + classlike.dri, + classlike.name, + filterEnumEntries(classlike.entries), + classlike.documentation, + classlike.expectPresentInSet, + classlike.sources, + filterFunctions(classlike.functions).let { + modified = modified || it.first; it.second + }, + filterProperties(classlike.properties).let { + modified = modified || it.first; it.second + }, + filterClasslikes(classlike.classlikes).let { + modified = modified || it.first; it.second + }, + classlike.visibility, + classlike.companion, + filterFunctions(classlike.constructors).let { + modified = modified || it.first; it.second + }, + classlike.supertypes, + classlike.sourceSets, + classlike.extra + ) + is DInterface -> DInterface( + classlike.dri, + classlike.name, + classlike.documentation, + classlike.expectPresentInSet, + classlike.sources, + filterFunctions(classlike.functions).let { + modified = modified || it.first; it.second + }, + filterProperties(classlike.properties).let { + modified = modified || it.first; it.second + }, + filterClasslikes(classlike.classlikes).let { + modified = modified || it.first; it.second + }, + classlike.visibility, + classlike.companion, + classlike.generics, + classlike.supertypes, + classlike.sourceSets, + classlike.extra + ) + is DObject -> DObject( + classlike.name, + classlike.dri, + classlike.documentation, + classlike.expectPresentInSet, + classlike.sources, + filterFunctions(classlike.functions).let { + modified = modified || it.first; it.second + }, + filterProperties(classlike.properties).let { + modified = modified || it.first; it.second + }, + filterClasslikes(classlike.classlikes).let { + modified = modified || it.first; it.second + }, + classlike.visibility, + classlike.supertypes, + classlike.sourceSets, + classlike.extra + ) + } + helper() + }.let { + Pair(it.size != classlikeList.size || modified, it) + } + } + } +}
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/transformers/documentables/DocumentableVisibilityFilterTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/DocumentableVisibilityFilterTransformer.kt new file mode 100644 index 00000000..ff05beed --- /dev/null +++ b/plugins/base/src/main/kotlin/transformers/documentables/DocumentableVisibilityFilterTransformer.kt @@ -0,0 +1,327 @@ +package org.jetbrains.dokka.base.transformers.documentables + +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.transformers.documentation.PreMergeDocumentableTransformer +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet + +class DocumentableVisibilityFilterTransformer(val context: DokkaContext) : PreMergeDocumentableTransformer { + + override fun invoke(modules: List<DModule>) = modules.map { original -> + val sourceSet = original.sourceSets.single() + val packageOptions = sourceSet.perPackageOptions + DocumentableVisibilityFilter(packageOptions, sourceSet).processModule(original) + } + + private class DocumentableVisibilityFilter( + val packageOptions: List<DokkaConfiguration.PackageOptions>, + val globalOptions: DokkaSourceSet + ) { + fun Visibility.isAllowedInPackage(packageName: String?) = when (this) { + is JavaVisibility.Public, + is JavaVisibility.Default, + is KotlinVisibility.Public -> true + else -> packageName != null + && packageOptions.firstOrNull { packageName.startsWith(it.prefix) }?.includeNonPublic + ?: globalOptions.includeNonPublic + } + + fun processModule(original: DModule) = + filterPackages(original.packages).let { (modified, packages) -> + if (!modified) original + else + DModule( + original.name, + packages = packages, + documentation = original.documentation, + sourceSets = original.sourceSets, + extra = original.extra + ) + } + + + private fun filterPackages(packages: List<DPackage>): Pair<Boolean, List<DPackage>> { + var packagesListChanged = false + val filteredPackages = packages.map { + var modified = false + val functions = filterFunctions(it.functions).let { (listModified, list) -> + modified = modified || listModified + list + } + val properties = filterProperties(it.properties).let { (listModified, list) -> + modified = modified || listModified + list + } + val classlikes = filterClasslikes(it.classlikes).let { (listModified, list) -> + modified = modified || listModified + list + } + when { + !modified -> it + else -> { + packagesListChanged = true + DPackage( + it.dri, + functions, + properties, + classlikes, + it.typealiases, + it.documentation, + it.expectPresentInSet, + it.sourceSets, + it.extra + ) + } + } + } + return Pair(packagesListChanged, filteredPackages) + } + + private fun <T : WithVisibility> alwaysTrue(a: T, p: DokkaSourceSet) = true + private fun <T : WithVisibility> alwaysFalse(a: T, p: DokkaSourceSet) = false + + private fun WithVisibility.visibilityForPlatform(data: DokkaSourceSet): Visibility? = visibility[data] + + private fun <T> T.filterPlatforms( + additionalCondition: (T, DokkaSourceSet) -> Boolean = ::alwaysTrue, + alternativeCondition: (T, DokkaSourceSet) -> Boolean = ::alwaysFalse + ) where T : Documentable, T : WithVisibility = + sourceSets.filter { d -> + visibilityForPlatform(d)?.isAllowedInPackage(dri.packageName) == true && + additionalCondition(this, d) || + alternativeCondition(this, d) + }.toSet() + + private fun <T> List<T>.transform( + additionalCondition: (T, DokkaSourceSet) -> Boolean = ::alwaysTrue, + alternativeCondition: (T, DokkaSourceSet) -> Boolean = ::alwaysFalse, + recreate: (T, Set<DokkaSourceSet>) -> T + ): Pair<Boolean, List<T>> where T : Documentable, T : WithVisibility { + var changed = false + val values = mapNotNull { t -> + val filteredPlatforms = t.filterPlatforms(additionalCondition, alternativeCondition) + when (filteredPlatforms.size) { + t.visibility.size -> t + 0 -> { + changed = true + null + } + else -> { + changed = true + recreate(t, filteredPlatforms) + } + } + } + return Pair(changed, values) + } + + private fun filterFunctions( + functions: List<DFunction>, + additionalCondition: (DFunction, DokkaSourceSet) -> Boolean = ::alwaysTrue + ) = + functions.transform(additionalCondition) { original, filteredPlatforms -> + with(original) { + DFunction( + dri, + name, + isConstructor, + parameters, + documentation.filtered(filteredPlatforms), + expectPresentInSet.filtered(filteredPlatforms), + sources.filtered(filteredPlatforms), + visibility.filtered(filteredPlatforms), + type, + generics.mapNotNull { it.filter(filteredPlatforms) }, + receiver, + modifier, + filteredPlatforms, + extra + ) + } + } + + private fun hasVisibleAccessorsForPlatform(property: DProperty, data: DokkaSourceSet) = + property.getter?.visibilityForPlatform(data)?.isAllowedInPackage(property.dri.packageName) == true || + property.setter?.visibilityForPlatform(data)?.isAllowedInPackage(property.dri.packageName) == true + + private fun filterProperties( + properties: List<DProperty>, + additionalCondition: (DProperty, DokkaSourceSet) -> Boolean = ::alwaysTrue + ): Pair<Boolean, List<DProperty>> = + properties.transform(additionalCondition, ::hasVisibleAccessorsForPlatform) { original, filteredPlatforms -> + with(original) { + DProperty( + dri, + name, + documentation.filtered(filteredPlatforms), + expectPresentInSet.filtered(filteredPlatforms), + sources.filtered(filteredPlatforms), + visibility.filtered(filteredPlatforms), + type, + receiver, + setter, + getter, + modifier, + filteredPlatforms, + generics.mapNotNull { it.filter(filteredPlatforms) }, + extra + ) + } + } + + private fun filterEnumEntries(entries: List<DEnumEntry>, filteredPlatforms: Set<DokkaSourceSet>) = + entries.mapNotNull { entry -> + if (filteredPlatforms.containsAll(entry.sourceSets)) entry + else { + val intersection = filteredPlatforms.intersect(entry.sourceSets) + if (intersection.isEmpty()) null + else DEnumEntry( + entry.dri, + entry.name, + entry.documentation.filtered(intersection), + entry.expectPresentInSet.filtered(filteredPlatforms), + filterFunctions(entry.functions) { _, data -> data in intersection }.second, + filterProperties(entry.properties) { _, data -> data in intersection }.second, + filterClasslikes(entry.classlikes) { _, data -> data in intersection }.second, + intersection, + entry.extra + ) + } + } + + private fun filterClasslikes( + classlikeList: List<DClasslike>, + additionalCondition: (DClasslike, DokkaSourceSet) -> Boolean = ::alwaysTrue + ): Pair<Boolean, List<DClasslike>> { + var classlikesListChanged = false + val filteredClasslikes: List<DClasslike> = classlikeList.mapNotNull { + with(it) { + val filteredPlatforms = filterPlatforms(additionalCondition) + if (filteredPlatforms.isEmpty()) { + classlikesListChanged = true + null + } else { + var modified = sourceSets.size != filteredPlatforms.size + val functions = + filterFunctions(functions) { _, data -> data in filteredPlatforms }.let { (listModified, list) -> + modified = modified || listModified + list + } + val properties = + filterProperties(properties) { _, data -> data in filteredPlatforms }.let { (listModified, list) -> + modified = modified || listModified + list + } + val classlikes = + filterClasslikes(classlikes) { _, data -> data in filteredPlatforms }.let { (listModified, list) -> + modified = modified || listModified + list + } + val companion = + if (this is WithCompanion) filterClasslikes(listOfNotNull(companion)) { _, data -> data in filteredPlatforms }.let { (listModified, list) -> + modified = modified || listModified + list.firstOrNull() as DObject? + } else null + val constructors = if (this is WithConstructors) + filterFunctions(constructors) { _, data -> data in filteredPlatforms }.let { (listModified, list) -> + modified = modified || listModified + list + } else emptyList() + val generics = + if (this is WithGenerics) generics.mapNotNull { param -> param.filter(filteredPlatforms) } else emptyList() + val enumEntries = + if (this is DEnum) filterEnumEntries(entries, filteredPlatforms) else emptyList() + classlikesListChanged = classlikesListChanged || modified + when { + !modified -> this + this is DClass -> DClass( + dri, + name, + constructors, + functions, + properties, + classlikes, + sources.filtered(filteredPlatforms), + visibility.filtered(filteredPlatforms), + companion, + generics, + supertypes.filtered(filteredPlatforms), + documentation.filtered(filteredPlatforms), + expectPresentInSet.filtered(filteredPlatforms), + modifier, + filteredPlatforms, + extra + ) + this is DAnnotation -> DAnnotation( + name, + dri, + documentation.filtered(filteredPlatforms), + expectPresentInSet.filtered(filteredPlatforms), + sources.filtered(filteredPlatforms), + functions, + properties, + classlikes, + visibility.filtered(filteredPlatforms), + companion, + constructors, + generics, + filteredPlatforms, + extra + ) + this is DEnum -> DEnum( + dri, + name, + enumEntries, + documentation.filtered(filteredPlatforms), + expectPresentInSet.filtered(filteredPlatforms), + sources.filtered(filteredPlatforms), + functions, + properties, + classlikes, + visibility.filtered(filteredPlatforms), + companion, + constructors, + supertypes.filtered(filteredPlatforms), + filteredPlatforms, + extra + ) + this is DInterface -> DInterface( + dri, + name, + documentation.filtered(filteredPlatforms), + expectPresentInSet.filtered(filteredPlatforms), + sources.filtered(filteredPlatforms), + functions, + properties, + classlikes, + visibility.filtered(filteredPlatforms), + companion, + generics, + supertypes.filtered(filteredPlatforms), + filteredPlatforms, + extra + ) + this is DObject -> DObject( + name, + dri, + documentation.filtered(filteredPlatforms), + expectPresentInSet.filtered(filteredPlatforms), + sources.filtered(filteredPlatforms), + functions, + properties, + classlikes, + visibility, + supertypes.filtered(filteredPlatforms), + filteredPlatforms, + extra + ) + else -> null + } + } + } + } + return Pair(classlikesListChanged, filteredClasslikes) + } + } +} diff --git a/plugins/base/src/main/kotlin/transformers/documentables/EmptyPackagesFilterTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/EmptyPackagesFilterTransformer.kt new file mode 100644 index 00000000..61abfbd7 --- /dev/null +++ b/plugins/base/src/main/kotlin/transformers/documentables/EmptyPackagesFilterTransformer.kt @@ -0,0 +1,28 @@ +package org.jetbrains.dokka.base.transformers.documentables + +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.model.DModule +import org.jetbrains.dokka.model.DPackage +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.transformers.documentation.PreMergeDocumentableTransformer + +class EmptyPackagesFilterTransformer(val context: DokkaContext) : PreMergeDocumentableTransformer { + override fun invoke(modules: List<DModule>): List<DModule> = modules.map { original -> + original.let { + EmptyPackagesFilter(original.sourceSets.single()).processModule(it) + } + } + + private class EmptyPackagesFilter( + val sourceSet: DokkaConfiguration.DokkaSourceSet + ) { + fun DPackage.shouldBeSkipped() = sourceSet.skipEmptyPackages && + functions.isEmpty() && + properties.isEmpty() && + classlikes.isEmpty() + + fun processModule(module: DModule) = module.copy( + packages = module.packages.filter { !it.shouldBeSkipped() } + ) + } +} diff --git a/plugins/base/src/main/kotlin/transformers/documentables/ExtensionExtractorTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/ExtensionExtractorTransformer.kt new file mode 100644 index 00000000..2da25d4b --- /dev/null +++ b/plugins/base/src/main/kotlin/transformers/documentables/ExtensionExtractorTransformer.kt @@ -0,0 +1,127 @@ +package org.jetbrains.dokka.base.transformers.documentables + +import kotlinx.coroutines.* +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.channels.ClosedReceiveChannelException +import kotlinx.coroutines.channels.ReceiveChannel +import kotlinx.coroutines.channels.SendChannel +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.toList +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.links.DriOfAny +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.properties.ExtraProperty +import org.jetbrains.dokka.model.properties.MergeStrategy +import org.jetbrains.dokka.model.properties.plus +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.transformers.documentation.DocumentableTransformer + + +class ExtensionExtractorTransformer : DocumentableTransformer { + override fun invoke(original: DModule, context: DokkaContext): DModule = runBlocking(Dispatchers.Default) { + val channel = Channel<Pair<DRI, Callable>>(10) + launch { + coroutineScope { + original.packages.forEach { launch { collectExtensions(it, channel) } } + } + channel.close() + } + val extensionMap = channel.consumeAsFlow().toList().toMultiMap() + + val newPackages = original.packages.map { async { it.addExtensionInformation(extensionMap) } } + original.copy(packages = newPackages.awaitAll()) + } +} + +private suspend fun <T : Documentable> T.addExtensionInformation( + extensionMap: Map<DRI, List<Callable>> +): T = coroutineScope { + val newClasslikes = (this@addExtensionInformation as? WithScope) + ?.classlikes + ?.map { async { it.addExtensionInformation(extensionMap) } } + .orEmpty() + + @Suppress("UNCHECKED_CAST") + when (this@addExtensionInformation) { + is DPackage -> { + val newTypealiases = typealiases.map { async { it.addExtensionInformation(extensionMap) } } + copy(classlikes = newClasslikes.awaitAll(), typealiases = newTypealiases.awaitAll()) + } + is DClass -> copy(classlikes = newClasslikes.awaitAll(), extra = extra + extensionMap.find(dri)) + is DEnum -> copy(classlikes = newClasslikes.awaitAll(), extra = extra + extensionMap.find(dri)) + is DInterface -> copy(classlikes = newClasslikes.awaitAll(), extra = extra + extensionMap.find(dri)) + is DObject -> copy(classlikes = newClasslikes.awaitAll(), extra = extra + extensionMap.find(dri)) + is DAnnotation -> copy(classlikes = newClasslikes.awaitAll(), extra = extra + extensionMap.find(dri)) + is DTypeAlias -> copy(extra = extra + extensionMap.find(dri)) + else -> throw IllegalStateException( + "${this@addExtensionInformation::class.simpleName} is not expected to have extensions" + ) + } as T +} + +private fun Map<DRI, List<Callable>>.find(dri: DRI) = get(dri)?.toSet()?.let(::CallableExtensions) + +private suspend fun collectExtensions( + documentable: Documentable, + channel: SendChannel<Pair<DRI, Callable>> +): Unit = coroutineScope { + if (documentable is WithScope) { + documentable.classlikes.forEach { + launch { collectExtensions(it, channel) } + } + + if (documentable is DObject || documentable is DPackage) { + (documentable.properties.asSequence() + documentable.functions.asSequence()) + .flatMap(Callable::asPairsWithReceiverDRIs) + .forEach { channel.send(it) } + } + } +} + + +private fun Callable.asPairsWithReceiverDRIs(): Sequence<Pair<DRI, Callable>> = + receiver?.type?.let(::findReceiverDRIs).orEmpty().map { it to this } + +// In normal cases we return at max one DRI, but sometimes receiver type can be bound by more than one type constructor +// for example `fun <T> T.example() where T: A, T: B` is extension of both types A and B +// Note: in some cases returning empty sequence doesn't mean that we cannot determine the DRI but only that we don't +// care about it since there is nowhere to put documentation of given extension. +private fun Callable.findReceiverDRIs(bound: Bound): Sequence<DRI> = when (bound) { + is Nullable -> findReceiverDRIs(bound.inner) + is OtherParameter -> + if (this is DFunction && bound.declarationDRI == this.dri) + generics.find { it.name == bound.name }?.bounds?.asSequence()?.flatMap(::findReceiverDRIs).orEmpty() + else + emptySequence() + is TypeConstructor -> sequenceOf(bound.dri) + is PrimitiveJavaType -> emptySequence() + is Void -> emptySequence() + is JavaObject -> sequenceOf(DriOfAny) + is Dynamic -> sequenceOf(DriOfAny) + is UnresolvedBound -> emptySequence() +} + +private fun <T, U> Iterable<Pair<T, U>>.toMultiMap(): Map<T, List<U>> = + groupBy(Pair<T, *>::first, Pair<*, U>::second) + +data class CallableExtensions(val extensions: Set<Callable>) : ExtraProperty<Documentable> { + companion object Key : ExtraProperty.Key<Documentable, CallableExtensions> { + override fun mergeStrategyFor(left: CallableExtensions, right: CallableExtensions) = + MergeStrategy.Replace(CallableExtensions(left.extensions + right.extensions)) + } + + override val key = Key +} + +//TODO IMPORTANT remove this terrible hack after updating to 1.4-M3 +fun <T : Any> ReceiveChannel<T>.consumeAsFlow(): Flow<T> = flow { + try { + while (true) { + emit(receive()) + } + } catch (_: ClosedReceiveChannelException) { + // cool and good + } +}.flowOn(Dispatchers.Default)
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/transformers/documentables/InheritorsExtractorTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/InheritorsExtractorTransformer.kt new file mode 100644 index 00000000..85256d51 --- /dev/null +++ b/plugins/base/src/main/kotlin/transformers/documentables/InheritorsExtractorTransformer.kt @@ -0,0 +1,85 @@ +package org.jetbrains.dokka.base.transformers.documentables + +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.properties.ExtraProperty +import org.jetbrains.dokka.model.properties.MergeStrategy +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.transformers.documentation.DocumentableTransformer + +class InheritorsExtractorTransformer : DocumentableTransformer { + override fun invoke(original: DModule, context: DokkaContext): DModule = + original.generateInheritanceMap().let { inheritanceMap -> original.appendInheritors(inheritanceMap) as DModule } + + private fun <T : Documentable> T.appendInheritors(inheritanceMap: Map<DokkaSourceSet, Map<DRI, List<DRI>>>): Documentable = + InheritorsInfo(inheritanceMap.getForDRI(dri)).let { info -> + when (this) { + is DModule -> copy(packages = packages.map { it.appendInheritors(inheritanceMap) as DPackage }) + is DPackage -> copy(classlikes = classlikes.map { it.appendInheritors(inheritanceMap) as DClasslike }) + is DClass -> if (info.isNotEmpty()) { + copy( + extra = extra + info, + classlikes = classlikes.map { it.appendInheritors(inheritanceMap) as DClasslike }) + } else { + copy(classlikes = classlikes.map { it.appendInheritors(inheritanceMap) as DClasslike }) + } + is DEnum -> if (info.isNotEmpty()) { + copy( + extra = extra + info, + classlikes = classlikes.map { it.appendInheritors(inheritanceMap) as DClasslike }) + } else { + copy(classlikes = classlikes.map { it.appendInheritors(inheritanceMap) as DClasslike }) + } + is DInterface -> if (info.isNotEmpty()) { + copy( + extra = extra + info, + classlikes = classlikes.map { it.appendInheritors(inheritanceMap) as DClasslike }) + } else { + copy(classlikes = classlikes.map { it.appendInheritors(inheritanceMap) as DClasslike }) + } + is DObject -> copy(classlikes = classlikes.map { it.appendInheritors(inheritanceMap) as DClasslike }) + is DAnnotation -> copy(classlikes = classlikes.map { it.appendInheritors(inheritanceMap) as DClasslike }) + else -> this + } + } + + private fun InheritorsInfo.isNotEmpty() = this.value.values.fold(0) { acc, list -> acc + list.size } > 0 + + private fun Map<DokkaSourceSet, Map<DRI, List<DRI>>>.getForDRI(dri: DRI) = + map { (v, k) -> + v to k[dri] + }.map { (k, v) -> k to v.orEmpty() }.toMap() + + private fun DModule.generateInheritanceMap() = + getInheritanceEntriesRec().filterNot { it.second.isEmpty() }.groupBy({ it.first }) { it.second } + .map { (k, v) -> + k to v.flatMap { p -> p.groupBy({ it.first }) { it.second }.toList() } + .groupBy({ it.first }) { it.second }.map { (k2, v2) -> k2 to v2.flatten() }.toMap() + }.filter { it.second.values.isNotEmpty() }.toMap() + + private fun <T : Documentable> T.getInheritanceEntriesRec(): List<Pair<DokkaSourceSet, List<Pair<DRI, DRI>>>> = + this.toInheritanceEntries() + children.flatMap { it.getInheritanceEntriesRec() } + + private fun <T : Documentable> T.toInheritanceEntries() = + (this as? WithSupertypes)?.let { + it.supertypes.map { (k, v) -> k to v.map { it.dri to dri } } + }.orEmpty() + +} + +class InheritorsInfo(val value: SourceSetDependent<List<DRI>>) : ExtraProperty<Documentable> { + companion object : ExtraProperty.Key<Documentable, InheritorsInfo> { + override fun mergeStrategyFor(left: InheritorsInfo, right: InheritorsInfo): MergeStrategy<Documentable> = + MergeStrategy.Replace( + InheritorsInfo( + (left.value.entries.toList() + right.value.entries.toList()) + .groupBy({ it.key }) { it.value } + .map { (k, v) -> k to v.flatten() }.toMap() + ) + ) + } + + override val key: ExtraProperty.Key<Documentable, *> = InheritorsInfo +} + diff --git a/plugins/base/src/main/kotlin/transformers/documentables/ModuleAndPackageDocumentationTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/ModuleAndPackageDocumentationTransformer.kt new file mode 100644 index 00000000..4a98a5e0 --- /dev/null +++ b/plugins/base/src/main/kotlin/transformers/documentables/ModuleAndPackageDocumentationTransformer.kt @@ -0,0 +1,108 @@ +package org.jetbrains.dokka.base.transformers.documentables + +import org.jetbrains.dokka.analysis.KotlinAnalysis +import org.jetbrains.dokka.model.DModule +import org.jetbrains.dokka.model.doc.DocumentationNode +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.base.parsers.MarkdownParser +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.transformers.documentation.PreMergeDocumentableTransformer +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import java.nio.file.Files +import java.nio.file.Paths + + +internal class ModuleAndPackageDocumentationTransformer( + private val context: DokkaContext, + private val kotlinAnalysis: KotlinAnalysis +) : PreMergeDocumentableTransformer { + + override fun invoke(modules: List<DModule>): List<DModule> { + + val modulesAndPackagesDocumentation = + context.configuration.sourceSets + .map { + Pair(it.moduleDisplayName, it) to + it.includes.map { Paths.get(it) } + .also { + it.forEach { + if (Files.notExists(it)) + context.logger.warn("Not found file under this path ${it.toAbsolutePath()}") + } + } + .filter { Files.exists(it) } + .flatMap { + it.toFile() + .readText() + .split(Regex("(\n|^)# (?=(Module|Package))")) // Matches heading with Module/Package to split by + .filter { it.isNotEmpty() } + .map { + it.split( + Regex(" "), + 2 + ) + } // Matches space between Module/Package and fully qualified name + }.groupBy({ it[0] }, { + it[1].split(Regex("\n"), 2) // Matches new line after fully qualified name + .let { it[0].trim() to it[1].trim() } + }).mapValues { + it.value.toMap() + } + }.toMap() + + return modules.map { module -> + + val moduleDocumentation = + module.sourceSets.mapNotNull { pd -> + val doc = modulesAndPackagesDocumentation[Pair(module.name, pd)] + val facade = kotlinAnalysis[pd].facade + try { + doc?.get("Module")?.get(module.name)?.run { + pd to MarkdownParser( + facade, + facade.moduleDescriptor.getPackage(FqName.topLevel(Name.identifier(""))), + context.logger + ).parse(this) + } + } catch (e: IllegalArgumentException) { + context.logger.error(e.message.orEmpty()) + null + } + }.toMap() + + val packagesDocumentation = module.packages.map { + it.name to it.sourceSets.mapNotNull { pd -> + val doc = modulesAndPackagesDocumentation[Pair(module.name, pd)] + val facade = kotlinAnalysis[pd].facade + val descriptor = facade.moduleDescriptor.getPackage(FqName(it.name.let { if(it == "[JS root]") "" else it })) + doc?.get("Package")?.get(it.name)?.run { + pd to MarkdownParser( + facade, + descriptor, + context.logger + ).parse(this) + } + }.toMap() + }.toMap() + + module.copy( + documentation = mergeDocumentation(module.documentation, moduleDocumentation), + packages = module.packages.map { + val packageDocumentation = packagesDocumentation[it.name] + if (packageDocumentation != null && packageDocumentation.isNotEmpty()) + it.copy(documentation = mergeDocumentation(it.documentation, packageDocumentation)) + else + it + } + ) + } + } + + private fun mergeDocumentation(origin: Map<DokkaSourceSet, DocumentationNode>, new: Map<DokkaSourceSet, DocumentationNode>) = + (origin.asSequence() + new.asSequence()) + .distinct() + .groupBy({ it.key }, { it.value }) + .mapValues { (_, values) -> DocumentationNode(values.flatMap { it.children }) } + +} diff --git a/plugins/base/src/main/kotlin/transformers/documentables/ReportUndocumentedTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/ReportUndocumentedTransformer.kt new file mode 100644 index 00000000..2ebd4c62 --- /dev/null +++ b/plugins/base/src/main/kotlin/transformers/documentables/ReportUndocumentedTransformer.kt @@ -0,0 +1,165 @@ +package org.jetbrains.dokka.base.transformers.documentables + +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.analysis.DescriptorDocumentableSource +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.transformers.documentation.DocumentableTransformer +import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor +import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.FAKE_OVERRIDE +import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind.SYNTHESIZED +import org.jetbrains.kotlin.utils.addToStdlib.safeAs + +internal class ReportUndocumentedTransformer : DocumentableTransformer { + + override fun invoke(original: DModule, context: DokkaContext): DModule = original.apply { + withDescendants().forEach { documentable -> invoke(documentable, context) } + } + + private fun invoke(documentable: Documentable, context: DokkaContext) { + documentable.sourceSets.forEach { sourceSet -> + if (shouldBeReportedIfNotDocumented(documentable, sourceSet, context)) { + reportIfUndocumented(context, documentable, sourceSet) + } + } + } + + private fun shouldBeReportedIfNotDocumented( + documentable: Documentable, sourceSet: DokkaSourceSet, context: DokkaContext + ): Boolean { + val packageOptionsOrNull = packageOptionsOrNull(sourceSet, documentable) + + if (!(packageOptionsOrNull?.reportUndocumented ?: sourceSet.reportUndocumented)) { + return false + } + + if (documentable is DParameter || documentable is DPackage || documentable is DModule) { + return false + } + + if (isConstructor(documentable)) { + return false + } + + if (isFakeOverride(documentable, sourceSet)) { + return false + } + + if (isSynthesized(documentable, sourceSet)) { + return false + } + + if (isPrivateOrInternalApi(documentable, sourceSet)) { + return false + } + + return true + } + + private fun reportIfUndocumented( + context: DokkaContext, + documentable: Documentable, + sourceSet: DokkaSourceSet + ) { + if (isUndocumented(documentable, sourceSet)) { + val documentableDescription = with(documentable) { + buildString { + dri.packageName?.run { + append(this) + append("/") + } + + dri.classNames?.run { + append(this) + append("/") + } + + dri.callable?.run { + append(name) + append("/") + append(signature()) + append("/") + } + + val sourceSetName = sourceSet.displayName + if (sourceSetName != null.toString()) { + append(" ($sourceSetName)") + } + } + } + + context.logger.warn("Undocumented: $documentableDescription") + } + } + + private fun isUndocumented(documentable: Documentable, sourceSet: DokkaSourceSet): Boolean { + fun resolveDependentSourceSets(sourceSet: DokkaSourceSet): List<DokkaSourceSet> { + return sourceSet.dependentSourceSets.mapNotNull { sourceSetID -> + documentable.sourceSets.singleOrNull { it.sourceSetID == sourceSetID } + } + } + + fun withAllDependentSourceSets(sourceSet: DokkaSourceSet): Sequence<DokkaSourceSet> { + return sequence { + yield(sourceSet) + for (dependentSourceSet in resolveDependentSourceSets(sourceSet)) { + yieldAll(withAllDependentSourceSets(dependentSourceSet)) + } + } + } + + return withAllDependentSourceSets(sourceSet).all { sourceSetOrDependentSourceSet -> + documentable.documentation[sourceSetOrDependentSourceSet]?.children?.isEmpty() ?: true + } + } + + private fun isConstructor(documentable: Documentable): Boolean { + if (documentable !is DFunction) return false + return documentable.isConstructor + } + + private fun isFakeOverride(documentable: Documentable, sourceSet: DokkaSourceSet): Boolean { + return callableMemberDescriptorOrNull(documentable, sourceSet)?.kind == FAKE_OVERRIDE + } + + private fun isSynthesized(documentable: Documentable, sourceSet: DokkaSourceSet): Boolean { + return callableMemberDescriptorOrNull(documentable, sourceSet)?.kind == SYNTHESIZED + } + + private fun callableMemberDescriptorOrNull( + documentable: Documentable, sourceSet: DokkaSourceSet + ): CallableMemberDescriptor? { + if (documentable is WithExpectActual) { + return documentable.sources[sourceSet] + .safeAs<DescriptorDocumentableSource>()?.descriptor + .safeAs() + } + + return null + } + + private fun isPrivateOrInternalApi(documentable: Documentable, sourceSet: DokkaSourceSet): Boolean { + return when (documentable.safeAs<WithVisibility>()?.visibility?.get(sourceSet)) { + KotlinVisibility.Public -> false + KotlinVisibility.Private -> true + KotlinVisibility.Protected -> true + KotlinVisibility.Internal -> true + JavaVisibility.Public -> false + JavaVisibility.Private -> true + JavaVisibility.Protected -> true + JavaVisibility.Default -> true + null -> false + } + } + + private fun packageOptionsOrNull( + dokkaSourceSet: DokkaSourceSet, + documentable: Documentable + ): DokkaConfiguration.PackageOptions? { + val packageName = documentable.dri.packageName ?: return null + return dokkaSourceSet.perPackageOptions + .filter { packageOptions -> packageName.startsWith(packageOptions.prefix) } + .maxBy { packageOptions -> packageOptions.prefix.length } + } +} diff --git a/plugins/base/src/main/kotlin/transformers/pages/annotations/SinceKotlinTransformer.kt b/plugins/base/src/main/kotlin/transformers/pages/annotations/SinceKotlinTransformer.kt new file mode 100644 index 00000000..7914e88f --- /dev/null +++ b/plugins/base/src/main/kotlin/transformers/pages/annotations/SinceKotlinTransformer.kt @@ -0,0 +1,82 @@ +package org.jetbrains.dokka.base.transformers.pages.annotations + +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.doc.CustomTagWrapper +import org.jetbrains.dokka.model.doc.Text +import org.jetbrains.dokka.model.properties.WithExtraProperties +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.transformers.documentation.DocumentableTransformer +import org.jetbrains.kotlin.utils.addToStdlib.safeAs + +class SinceKotlinTransformer(val context: DokkaContext) : DocumentableTransformer { + + override fun invoke(original: DModule, context: DokkaContext) = original.transform() as DModule + + private fun <T : Documentable> T.transform(): Documentable = + when (this) { + is DModule -> copy( + packages = packages.map { it.transform() as DPackage } + ) + is DPackage -> copy( + classlikes = classlikes.map { it.transform() as DClasslike }, + functions = functions.map { it.transform() as DFunction }, + properties = properties.map { it.transform() as DProperty } + ) + is DClass -> copy( + documentation = appendSinceKotlin(), + classlikes = classlikes.map { it.transform() as DClasslike }, + functions = functions.map { it.transform() as DFunction }, + properties = properties.map { it.transform() as DProperty } + ) + is DEnum -> copy( + documentation = appendSinceKotlin(), + classlikes = classlikes.map { it.transform() as DClasslike }, + functions = functions.map { it.transform() as DFunction }, + properties = properties.map { it.transform() as DProperty } + ) + is DInterface -> copy( + documentation = appendSinceKotlin(), + classlikes = classlikes.map { it.transform() as DClasslike }, + functions = functions.map { it.transform() as DFunction }, + properties = properties.map { it.transform() as DProperty } + ) + is DObject -> copy( + documentation = appendSinceKotlin(), + classlikes = classlikes.map { it.transform() as DClasslike }, + functions = functions.map { it.transform() as DFunction }, + properties = properties.map { it.transform() as DProperty } + ) + is DAnnotation -> copy( + documentation = appendSinceKotlin(), + classlikes = classlikes.map { it.transform() as DClasslike }, + functions = functions.map { it.transform() as DFunction }, + properties = properties.map { it.transform() as DProperty } + ) + is DFunction -> copy( + documentation = appendSinceKotlin() + ) + is DProperty -> copy( + documentation = appendSinceKotlin() + ) + is DParameter -> copy( + documentation = appendSinceKotlin() + ) + else -> this.also { context.logger.warn("Unrecognized documentable $this while SinceKotlin transformation") } + } + + private fun Documentable.appendSinceKotlin() = + sourceSets.fold(documentation) { acc, sourceSet -> + safeAs<WithExtraProperties<Documentable>>()?.extra?.get(Annotations)?.content?.get(sourceSet)?.find { + it.dri == DRI("kotlin", "SinceKotlin") + }?.params?.get("version").safeAs<StringValue>()?.value?.let { version -> + acc.mapValues { + if (it.key == sourceSet) it.value.copy( + it.value.children + listOf( + CustomTagWrapper(Text(version.dropWhile { it == '"' }.dropLastWhile { it == '"' }), "Since Kotlin") + ) + ) else it.value + } + } ?: acc + } +}
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/transformers/pages/comments/CommentsToContentConverter.kt b/plugins/base/src/main/kotlin/transformers/pages/comments/CommentsToContentConverter.kt new file mode 100644 index 00000000..fa9ce37e --- /dev/null +++ b/plugins/base/src/main/kotlin/transformers/pages/comments/CommentsToContentConverter.kt @@ -0,0 +1,16 @@ +package org.jetbrains.dokka.base.transformers.pages.comments + +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.model.doc.DocTag +import org.jetbrains.dokka.model.properties.PropertyContainer +import org.jetbrains.dokka.pages.* + +interface CommentsToContentConverter { + fun buildContent( + docTag: DocTag, + dci: DCI, + sourceSets: Set<DokkaSourceSet>, + styles: Set<Style> = emptySet(), + extras: PropertyContainer<ContentNode> = PropertyContainer.empty() + ): List<ContentNode> +} diff --git a/plugins/base/src/main/kotlin/transformers/pages/comments/DocTagToContentConverter.kt b/plugins/base/src/main/kotlin/transformers/pages/comments/DocTagToContentConverter.kt new file mode 100644 index 00000000..0f953e0f --- /dev/null +++ b/plugins/base/src/main/kotlin/transformers/pages/comments/DocTagToContentConverter.kt @@ -0,0 +1,178 @@ +package org.jetbrains.dokka.base.transformers.pages.comments + +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.model.doc.* +import org.jetbrains.dokka.model.properties.PropertyContainer +import org.jetbrains.dokka.pages.* + +object DocTagToContentConverter : CommentsToContentConverter { + override fun buildContent( + docTag: DocTag, + dci: DCI, + sourceSets: Set<DokkaSourceSet>, + styles: Set<Style>, + extra: PropertyContainer<ContentNode> + ): List<ContentNode> { + + fun buildChildren(docTag: DocTag, newStyles: Set<Style> = emptySet(), newExtras: SimpleAttr? = null) = + docTag.children.flatMap { + buildContent(it, dci, sourceSets, styles + newStyles, newExtras?.let { extra + it } ?: extra) + } + + fun buildTableRows(rows: List<DocTag>, newStyle: Style): List<ContentGroup> = + rows.flatMap { + buildContent(it, dci, sourceSets, styles + newStyle, extra) as List<ContentGroup> + } + + fun buildHeader(level: Int) = + listOf( + ContentHeader( + buildChildren(docTag), + level, + dci, + sourceSets, + styles + ) + ) + + fun buildList(ordered: Boolean, start: Int = 1) = + listOf( + ContentList( + buildChildren(docTag), + ordered, + dci, + sourceSets, + styles, + ((PropertyContainer.empty<ContentNode>()) + SimpleAttr("start", start.toString())) + ) + ) + + fun buildNewLine() = listOf( + ContentBreakLine( + sourceSets + ) + ) + + return when (docTag) { + is H1 -> buildHeader(1) + is H2 -> buildHeader(2) + is H3 -> buildHeader(3) + is H4 -> buildHeader(4) + is H5 -> buildHeader(5) + is H6 -> buildHeader(6) + is Ul -> buildList(false) + is Ol -> buildList(true, docTag.params["start"]?.toInt() ?: 1) + is Li -> listOf( + ContentGroup(children = buildChildren(docTag), dci, sourceSets, styles, extra) + ) + is Br -> buildNewLine() + is B -> buildChildren(docTag, setOf(TextStyle.Strong)) + is I -> buildChildren(docTag, setOf(TextStyle.Italic)) + is P -> buildChildren(docTag, newStyles = setOf(TextStyle.Paragraph)) + is A -> listOf( + ContentResolvedLink( + buildChildren(docTag), + docTag.params.get("href")!!, + dci, + sourceSets, + styles + ) + ) + is DocumentationLink -> listOf( + ContentDRILink( + buildChildren(docTag), + docTag.dri, + DCI( + setOf(docTag.dri), + ContentKind.Main + ), + sourceSets, + styles + ) + ) + is BlockQuote -> listOf( + ContentCodeBlock( + buildChildren(docTag), + "", + dci, + sourceSets, + styles + ) + ) + is CodeInline -> listOf( + ContentCodeInline( + buildChildren(docTag), + "", + dci, + sourceSets, + styles + ) + ) + is CodeBlock -> listOf( + ContentCodeBlock( + buildChildren(docTag), + "", + dci, + sourceSets, + styles + ) + ) + is Img -> listOf( + ContentEmbeddedResource( + address = docTag.params["href"]!!, + altText = docTag.params["alt"], + dci = dci, + sourceSets = sourceSets, + style = styles, + extra = extra + ) + ) + is HorizontalRule -> listOf( + ContentText( + "", + dci, + sourceSets, + setOf() + ) + ) + is Text -> listOf( + ContentText( + docTag.body, + dci, + sourceSets, + styles + ) + ) + is Strikethrough -> buildChildren(docTag, setOf(TextStyle.Strikethrough)) + is Table -> listOf( + ContentTable( + buildTableRows(docTag.children.filterIsInstance<Th>(), CommentTable), + buildTableRows(docTag.children.filterIsInstance<Tr>(), CommentTable), + dci, + sourceSets, + styles + CommentTable + ) + ) + is Th, + is Tr -> listOf( + ContentGroup( + docTag.children.map { + ContentGroup(buildChildren(it), dci, sourceSets, styles, extra) + }, + dci, + sourceSets, + styles + ) + ) + is Index -> listOf( + ContentGroup( + buildChildren(docTag, newStyles = styles + ContentStyle.InDocumentationAnchor), + dci, + sourceSets, + styles + ) + ) + else -> buildChildren(docTag) + } + } +} diff --git a/plugins/base/src/main/kotlin/transformers/pages/merger/FallbackPageMergerStrategy.kt b/plugins/base/src/main/kotlin/transformers/pages/merger/FallbackPageMergerStrategy.kt new file mode 100644 index 00000000..df0c27ee --- /dev/null +++ b/plugins/base/src/main/kotlin/transformers/pages/merger/FallbackPageMergerStrategy.kt @@ -0,0 +1,12 @@ +package org.jetbrains.dokka.base.transformers.pages.merger + +import org.jetbrains.dokka.pages.PageNode +import org.jetbrains.dokka.utilities.DokkaLogger + +class FallbackPageMergerStrategy(private val logger: DokkaLogger) : PageMergerStrategy { + override fun tryMerge(pages: List<PageNode>, path: List<String>): List<PageNode> { + val renderedPath = path.joinToString(separator = "/") + if (pages.size != 1) logger.warn("For $renderedPath: expected 1 page, but got ${pages.size}") + return listOf(pages.first()) + } +}
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/transformers/pages/merger/PageMerger.kt b/plugins/base/src/main/kotlin/transformers/pages/merger/PageMerger.kt new file mode 100644 index 00000000..4faf3ad4 --- /dev/null +++ b/plugins/base/src/main/kotlin/transformers/pages/merger/PageMerger.kt @@ -0,0 +1,29 @@ +package org.jetbrains.dokka.base.transformers.pages.merger + +import org.jetbrains.dokka.pages.PageNode +import org.jetbrains.dokka.pages.RootPageNode +import org.jetbrains.dokka.transformers.pages.PageTransformer + +class PageMerger(private val strategies: Iterable<PageMergerStrategy>) : PageTransformer { + override fun invoke(input: RootPageNode): RootPageNode = + input.modified(children = input.children.map { it.mergeChildren(emptyList()) }) + + private fun PageNode.mergeChildren(path: List<String>): PageNode = children.groupBy { it::class }.map { + it.value.groupBy { it.name }.map { (n, v) -> mergePageNodes(v, path + n) }.map { it.assertSingle(path) } + }.let { pages -> + modified(children = pages.flatten().map { it.mergeChildren(path + it.name) }) + } + + private fun mergePageNodes(pages: List<PageNode>, path: List<String>): List<PageNode> = + strategies.fold(pages) { acc, strategy -> tryMerge(strategy, acc, path) } + + private fun tryMerge(strategy: PageMergerStrategy, pages: List<PageNode>, path: List<String>) = + if (pages.size > 1) strategy.tryMerge(pages, path) else pages +} + +private fun <T> Iterable<T>.assertSingle(path: List<String>): T = try { + single() + } catch (e: Exception) { + val renderedPath = path.joinToString(separator = "/") + throw IllegalStateException("Page merger is misconfigured. Error for $renderedPath: ${e.message}") + }
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/transformers/pages/merger/PageMergerStrategy.kt b/plugins/base/src/main/kotlin/transformers/pages/merger/PageMergerStrategy.kt new file mode 100644 index 00000000..b73b17e0 --- /dev/null +++ b/plugins/base/src/main/kotlin/transformers/pages/merger/PageMergerStrategy.kt @@ -0,0 +1,9 @@ +package org.jetbrains.dokka.base.transformers.pages.merger + +import org.jetbrains.dokka.pages.PageNode + +interface PageMergerStrategy { + + fun tryMerge(pages: List<PageNode>, path: List<String>): List<PageNode> + +}
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/transformers/pages/merger/SameMethodNamePageMergerStrategy.kt b/plugins/base/src/main/kotlin/transformers/pages/merger/SameMethodNamePageMergerStrategy.kt new file mode 100644 index 00000000..d81f131b --- /dev/null +++ b/plugins/base/src/main/kotlin/transformers/pages/merger/SameMethodNamePageMergerStrategy.kt @@ -0,0 +1,40 @@ +package org.jetbrains.dokka.base.transformers.pages.merger + +import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.utilities.DokkaLogger + +class SameMethodNamePageMergerStrategy(val logger: DokkaLogger) : PageMergerStrategy { + override fun tryMerge(pages: List<PageNode>, path: List<String>): List<PageNode> { + val members = pages.filterIsInstance<MemberPageNode>().takeIf { it.isNotEmpty() } ?: return pages + val name = pages.first().name.also { + if (pages.any { page -> page.name != it }) { // Is this even possible? + logger.error("Page names for $it do not match!") + } + } + val dri = members.flatMap { it.dri }.toSet() + + + val merged = MemberPageNode( + dri = dri, + name = name, + children = members.flatMap { it.children }.distinct(), + content = squashDivergentInstances(members), + embeddedResources = members.flatMap { it.embeddedResources }.distinct(), + documentable = null + ) + + return (pages - members) + listOf(merged) + } + + private fun squashDivergentInstances(nodes: List<MemberPageNode>): ContentNode = + nodes.map { it.content } + .reduce { acc, node -> + acc.mapTransform<ContentDivergentGroup, ContentNode> { g -> + g.copy(children = (g.children + + (node.dfs { it is ContentDivergentGroup && it.groupID == g.groupID } as? ContentDivergentGroup) + ?.children?.single() + ).filterNotNull() + ) + } + } +}
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/transformers/pages/samples/DefaultSamplesTransformer.kt b/plugins/base/src/main/kotlin/transformers/pages/samples/DefaultSamplesTransformer.kt new file mode 100644 index 00000000..a391b534 --- /dev/null +++ b/plugins/base/src/main/kotlin/transformers/pages/samples/DefaultSamplesTransformer.kt @@ -0,0 +1,38 @@ +package org.jetbrains.dokka.base.transformers.pages.samples + +import com.intellij.psi.PsiElement +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.kotlin.idea.kdoc.resolveKDocSampleLink +import org.jetbrains.kotlin.psi.KtBlockExpression +import org.jetbrains.kotlin.psi.KtDeclarationWithBody +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.utils.addToStdlib.safeAs + +class DefaultSamplesTransformer(context: DokkaContext) : SamplesTransformer(context) { + + override fun processBody(psiElement: PsiElement): String { + val text = processSampleBody(psiElement).trim { it == '\n' || it == '\r' }.trimEnd() + val lines = text.split("\n") + val indent = lines.filter(String::isNotBlank).map { it.takeWhile(Char::isWhitespace).count() }.min() ?: 0 + return lines.joinToString("\n") { it.drop(indent) } + } + + private fun processSampleBody(psiElement: PsiElement): String = when (psiElement) { + is KtDeclarationWithBody -> { + val bodyExpression = psiElement.bodyExpression + when (bodyExpression) { + is KtBlockExpression -> bodyExpression.text.removeSurrounding("{", "}") + else -> bodyExpression!!.text + } + } + else -> psiElement.text + } + + override fun processImports(psiElement: PsiElement): String { + val psiFile = psiElement.containingFile + return when(val text = psiFile.safeAs<KtFile>()?.importList?.text) { + is String -> text + else -> "" + } + } +}
\ No newline at end of file diff --git a/core/src/main/kotlin/Samples/KotlinWebsiteSampleProcessingService.kt b/plugins/base/src/main/kotlin/transformers/pages/samples/KotlinWebsiteSamplesTransformer.kt index a67306f4..c099644f 100644 --- a/core/src/main/kotlin/Samples/KotlinWebsiteSampleProcessingService.kt +++ b/plugins/base/src/main/kotlin/transformers/pages/samples/KotlinWebsiteSamplesTransformer.kt @@ -1,27 +1,23 @@ -package org.jetbrains.dokka.Samples +package org.jetbrains.dokka.base.transformers.pages.samples -import com.google.inject.Inject import com.intellij.psi.PsiDocumentManager import com.intellij.psi.PsiElement import com.intellij.psi.PsiElementVisitor import com.intellij.psi.PsiWhiteSpace import com.intellij.psi.impl.source.tree.LeafPsiElement import com.intellij.psi.util.PsiTreeUtil -import org.jetbrains.dokka.* +import org.jetbrains.dokka.plugability.DokkaContext import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.allChildren import org.jetbrains.kotlin.psi.psiUtil.prevLeaf import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.resolve.ImportPath +import org.jetbrains.kotlin.utils.addToStdlib.safeAs import java.io.PrintWriter import java.io.StringWriter - -open class KotlinWebsiteSampleProcessingService -@Inject constructor(dokkaConfiguration: DokkaConfiguration, - logger: DokkaLogger, - resolutionFacade: DokkaResolutionFacade) - : DefaultSampleProcessingService(dokkaConfiguration, logger, resolutionFacade) { +// TODO Inspect below class for any bugs. Big chunk of was ripped from 0.10.1 +class KotlinWebsiteSamplesTransformer(context: DokkaContext): SamplesTransformer(context) { private class SampleBuilder : KtTreeVisitorVoid() { val builder = StringBuilder() @@ -33,8 +29,8 @@ open class KotlinWebsiteSampleProcessingService data class ConvertError(val e: Exception, val text: String, val loc: String) fun KtValueArgument.extractStringArgumentValue() = - (getArgumentExpression() as KtStringTemplateExpression) - .entries.joinToString("") { it.text } + (getArgumentExpression() as KtStringTemplateExpression) + .entries.joinToString("") { it.text } fun convertAssertPrints(expression: KtCallExpression) { @@ -160,29 +156,33 @@ open class KotlinWebsiteSampleProcessingService val pw = PrintWriter(sw) it.e.printStackTrace(pw) - logger.error("${containingFile.name}: (${it.loc}): Exception thrown while converting \n```\n${it.text}\n```\n$sw") + this@KotlinWebsiteSamplesTransformer.context.logger.error("${containingFile.name}: (${it.loc}): Exception thrown while converting \n```\n${it.text}\n```\n$sw") } return sampleBuilder.text } val importsToIgnore = arrayOf("samples.*", "samples.Sample").map { ImportPath.fromString(it) } - override fun processImports(psiElement: PsiElement): ContentBlockCode { + override fun processImports(psiElement: PsiElement): String { val psiFile = psiElement.containingFile - if (psiFile is KtFile) { - return ContentBlockCode("kotlin").apply { - append(ContentText("\n")) - psiFile.importList?.let { - it.allChildren.filter { - it !is KtImportDirective || it.importPath !in importsToIgnore - }.forEach { append(ContentText(it.text)) } - } + return when(val text = psiFile.safeAs<KtFile>()?.importList) { + is KtImportList -> text.let { + it.allChildren.filter { + it !is KtImportDirective || it.importPath !in importsToIgnore + }.joinToString(separator = "\n") { it.text } } + else -> "" } - return super.processImports(psiElement) } - override fun processSampleBody(psiElement: PsiElement) = when (psiElement) { + override fun processBody(psiElement: PsiElement): String { + val text = processSampleBody(psiElement).trim { it == '\n' || it == '\r' }.trimEnd() + val lines = text.split("\n") + val indent = lines.filter(String::isNotBlank).map { it.takeWhile(Char::isWhitespace).count() }.min() ?: 0 + return lines.joinToString("\n") { it.drop(indent) } + } + + private fun processSampleBody(psiElement: PsiElement) = when (psiElement) { is KtDeclarationWithBody -> { val bodyExpression = psiElement.bodyExpression val bodyExpressionText = bodyExpression!!.buildSampleText() @@ -193,5 +193,4 @@ open class KotlinWebsiteSampleProcessingService } else -> psiElement.buildSampleText() } -} - +}
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/transformers/pages/samples/SamplesTransformer.kt b/plugins/base/src/main/kotlin/transformers/pages/samples/SamplesTransformer.kt new file mode 100644 index 00000000..695ef050 --- /dev/null +++ b/plugins/base/src/main/kotlin/transformers/pages/samples/SamplesTransformer.kt @@ -0,0 +1,151 @@ +package org.jetbrains.dokka.base.transformers.pages.samples + +import com.intellij.psi.PsiElement +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.Platform +import org.jetbrains.dokka.analysis.AnalysisEnvironment +import org.jetbrains.dokka.analysis.DokkaMessageCollector +import org.jetbrains.dokka.analysis.DokkaResolutionFacade +import org.jetbrains.dokka.analysis.EnvironmentAndFacade +import org.jetbrains.dokka.base.renderers.sourceSets +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.doc.Sample +import org.jetbrains.dokka.model.properties.PropertyContainer +import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.transformers.pages.PageTransformer +import org.jetbrains.kotlin.idea.kdoc.resolveKDocSampleLink +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils +import org.jetbrains.kotlin.utils.PathUtil +import java.io.File + +abstract class SamplesTransformer(val context: DokkaContext) : PageTransformer { + + abstract fun processBody(psiElement: PsiElement): String + abstract fun processImports(psiElement: PsiElement): String + + final override fun invoke(input: RootPageNode): RootPageNode { + val analysis = setUpAnalysis(context) + val kotlinPlaygroundScript = + "<script src=\"https://unpkg.com/kotlin-playground@1\" data-selector=\"code.runnablesample\"></script>" + + return input.transformContentPagesTree { page -> + page.documentable?.documentation?.entries?.fold(page) { acc, entry -> + entry.value.children.filterIsInstance<Sample>().fold(acc) { acc, sample -> + acc.modified( + content = acc.content.addSample(page, entry.key, sample.name, analysis), + embeddedResources = acc.embeddedResources + kotlinPlaygroundScript + ) + } + } ?: page + } + } + + private fun setUpAnalysis(context: DokkaContext) = context.configuration.sourceSets.map { + it to AnalysisEnvironment(DokkaMessageCollector(context.logger), it.analysisPlatform).run { + if (analysisPlatform == Platform.jvm) { + addClasspath(PathUtil.getJdkClassesRootsFromCurrentJre()) + } + it.classpath.forEach { addClasspath(File(it)) } + + addSources(it.samples.map { it }) + + loadLanguageVersionSettings(it.languageVersion, it.apiVersion) + + val environment = createCoreEnvironment() + val (facade, _) = createResolutionFacade(environment) + EnvironmentAndFacade(environment, facade) + } + }.toMap() + + private fun ContentNode.addSample( + contentPage: ContentPage, + platform: DokkaSourceSet, + fqName: String, + analysis: Map<DokkaSourceSet, EnvironmentAndFacade> + ): ContentNode { + val facade = analysis[platform]?.facade + ?: return this.also { context.logger.warn("Cannot resolve facade for platform ${platform.moduleDisplayName}") } + val psiElement = fqNameToPsiElement(facade, fqName) + ?: return this.also { context.logger.warn("Cannot find PsiElement corresponding to $fqName") } + val imports = + processImports(psiElement) + val body = processBody(psiElement) + val node = contentCode(contentPage.sourceSets(), contentPage.dri, createSampleBody(imports, body), "kotlin") + + return dfs(fqName, node) + } + + protected open fun createSampleBody(imports: String, body: String) = + """ |$imports + |fun main() { + | //sampleStart + | $body + | //sampleEnd + |}""".trimMargin() + + private fun ContentNode.dfs(fqName: String, node: ContentCodeBlock): ContentNode { + return when (this) { + is ContentHeader -> copy(children.map { it.dfs(fqName, node) }) + is ContentDivergentGroup -> @Suppress("UNCHECKED_CAST") copy(children.map { + it.dfs(fqName, node) + } as List<ContentDivergentInstance>) + is ContentDivergentInstance -> copy( + before.let { it?.dfs(fqName, node) }, + divergent.dfs(fqName, node), + after.let { it?.dfs(fqName, node) }) + is ContentCodeBlock -> copy(children.map { it.dfs(fqName, node) }) + is ContentCodeInline -> copy(children.map { it.dfs(fqName, node) }) + is ContentDRILink -> copy(children.map { it.dfs(fqName, node) }) + is ContentResolvedLink -> copy(children.map { it.dfs(fqName, node) }) + is ContentEmbeddedResource -> copy(children.map { it.dfs(fqName, node) }) + is ContentTable -> copy(children = children.map { it.dfs(fqName, node) as ContentGroup }) + is ContentList -> copy(children.map { it.dfs(fqName, node) }) + is ContentGroup -> copy(children.map { it.dfs(fqName, node) }) + is PlatformHintedContent -> copy(inner.dfs(fqName, node)) + is ContentText -> if (text == fqName) node else this + is ContentBreakLine -> this + else -> this.also { context.logger.error("Could not recognize $this ContentNode in SamplesTransformer") } + } + } + + private fun fqNameToPsiElement(resolutionFacade: DokkaResolutionFacade, functionName: String): PsiElement? { + val packageName = functionName.takeWhile { it != '.' } + val descriptor = resolutionFacade.resolveSession.getPackageFragment(FqName(packageName)) + ?: return null.also { context.logger.warn("Cannot find descriptor for package $packageName") } + val symbol = resolveKDocSampleLink( + BindingContext.EMPTY, + resolutionFacade, + descriptor, + functionName.split(".") + ).firstOrNull() ?: return null.also { context.logger.warn("Unresolved function $functionName in @sample") } + return DescriptorToSourceUtils.descriptorToDeclaration(symbol) + } + + private fun contentCode( + sourceSets: Set<DokkaSourceSet>, + dri: Set<DRI>, + content: String, + language: String, + styles: Set<Style> = emptySet(), + extra: PropertyContainer<ContentNode> = PropertyContainer.empty() + ) = + ContentCodeBlock( + children = listOf( + ContentText( + text = content, + dci = DCI(dri, ContentKind.Sample), + sourceSets = sourceSets, + style = emptySet(), + extra = PropertyContainer.empty() + ) + ), + language = language, + dci = DCI(dri, ContentKind.Sample), + sourceSets = sourceSets, + style = styles + ContentStyle.RunnableSample + TextStyle.Monospace, + extra = extra + ) +} diff --git a/plugins/base/src/main/kotlin/transformers/pages/sourcelinks/SourceLinksTransformer.kt b/plugins/base/src/main/kotlin/transformers/pages/sourcelinks/SourceLinksTransformer.kt new file mode 100644 index 00000000..f0e62f11 --- /dev/null +++ b/plugins/base/src/main/kotlin/transformers/pages/sourcelinks/SourceLinksTransformer.kt @@ -0,0 +1,130 @@ +package org.jetbrains.dokka.base.transformers.pages.sourcelinks + +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiDocumentManager +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder +import org.jetbrains.dokka.model.DocumentableSource +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.analysis.DescriptorDocumentableSource +import org.jetbrains.dokka.analysis.PsiDocumentableSource +import org.jetbrains.dokka.model.WithExpectActual +import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.transformers.pages.PageTransformer +import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource +import org.jetbrains.kotlin.resolve.source.getPsi +import org.jetbrains.kotlin.utils.addToStdlib.cast + +class SourceLinksTransformer(val context: DokkaContext, val builder: PageContentBuilder) : PageTransformer { + + override fun invoke(input: RootPageNode) = + input.transformContentPagesTree { node -> + when (val documentable = node.documentable) { + is WithExpectActual -> resolveSources(documentable) + .takeIf { it.isNotEmpty() } + ?.let { node.addSourcesContent(it) } + ?: node + else -> node + } + } + + private fun getSourceLinks() = context.configuration.sourceSets + .flatMap { it.sourceLinks.map { sl -> SourceLink(sl, it) } } + + private fun resolveSources(documentable: WithExpectActual) = documentable.sources + .mapNotNull { entry -> + getSourceLinks().find { entry.value.path.contains(it.path) && it.sourceSetData == entry.key }?.let { + Pair( + entry.key, + entry.value.toLink(it) + ) + } + } + + private fun ContentPage.addSourcesContent(sources: List<Pair<DokkaSourceSet, String>>) = builder + .buildSourcesContent(this, sources) + .let { + this.modified( + content = this.content.addTable(it) + ) + } + + private fun PageContentBuilder.buildSourcesContent( + node: ContentPage, + sources: List<Pair<DokkaSourceSet, String>> + ) = contentFor( + node.dri.first(), + node.documentable!!.sourceSets.toSet() + ) { + header(2, "Sources", kind = ContentKind.Source) + +ContentTable( + emptyList(), + sources.map { + buildGroup(node.dri, setOf(it.first), kind = ContentKind.Source) { + link("(source)", it.second) + } + }, + DCI(node.dri, ContentKind.Source), + node.documentable!!.sourceSets.toSet(), + style = emptySet(), + extra = mainExtra + SimpleAttr.header("Sources") + ) + } + + private fun DocumentableSource.toLink(sourceLink: SourceLink): String { + val lineNumber = when (this) { + is DescriptorDocumentableSource -> this.descriptor + .cast<DeclarationDescriptorWithSource>() + .source.getPsi() + ?.lineNumber() + is PsiDocumentableSource -> this.psi.lineNumber() + else -> null + } + return sourceLink.url + + this.path.split(sourceLink.path)[1] + + sourceLink.lineSuffix + + "${lineNumber ?: 1}" + } + + private fun ContentNode.addTable(table: ContentGroup): ContentNode = + when (this) { + is ContentGroup -> { + if(hasTabbedContent()){ + copy( + children = children.map { + if(it.hasStyle(ContentStyle.TabbedContent) && it is ContentGroup){ + it.copy(children = it.children + table) + } else { + it + } + } + ) + } else { + copy(children = children + table) + } + + } + else -> ContentGroup( + children = listOf(this, table), + extra = this.extra, + sourceSets = this.sourceSets, + dci = this.dci, + style = this.style + ) + } + + private fun PsiElement.lineNumber(): Int? { + val doc = PsiDocumentManager.getInstance(project).getDocument(containingFile) + // IJ uses 0-based line-numbers; external source browsers use 1-based + return doc?.getLineNumber(textRange.startOffset)?.plus(1) + } +} + +data class SourceLink(val path: String, val url: String, val lineSuffix: String?, val sourceSetData: DokkaSourceSet) { + constructor(sourceLinkDefinition: DokkaConfiguration.SourceLinkDefinition, sourceSetData: DokkaSourceSet) : this( + sourceLinkDefinition.path, sourceLinkDefinition.url, sourceLinkDefinition.lineSuffix, sourceSetData + ) +} + +fun ContentGroup.hasTabbedContent(): Boolean = children.any { it.hasStyle(ContentStyle.TabbedContent) }
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt b/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt new file mode 100644 index 00000000..ffceaaa7 --- /dev/null +++ b/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt @@ -0,0 +1,782 @@ +package org.jetbrains.dokka.base.translators.descriptors + +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.analysis.DescriptorDocumentableSource +import org.jetbrains.dokka.analysis.DokkaResolutionFacade +import org.jetbrains.dokka.analysis.KotlinAnalysis +import org.jetbrains.dokka.analysis.from +import org.jetbrains.dokka.base.parsers.MarkdownParser +import org.jetbrains.dokka.links.* +import org.jetbrains.dokka.links.Callable +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.Nullable +import org.jetbrains.dokka.model.TypeConstructor +import org.jetbrains.dokka.model.doc.* +import org.jetbrains.dokka.model.properties.PropertyContainer +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.transformers.sources.SourceToDocumentableTranslator +import org.jetbrains.dokka.utilities.DokkaLogger +import org.jetbrains.kotlin.builtins.isExtensionFunctionType +import org.jetbrains.kotlin.builtins.isFunctionType +import org.jetbrains.kotlin.codegen.isJvmStaticInObjectOrClassOrInterface +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.Visibility +import org.jetbrains.kotlin.descriptors.annotations.Annotated +import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor +import org.jetbrains.kotlin.descriptors.impl.DeclarationDescriptorVisitorEmptyBodies +import org.jetbrains.kotlin.idea.kdoc.findKDoc +import org.jetbrains.kotlin.load.kotlin.toSourceElement +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.calls.callUtil.getValueArgumentsInParentheses +import org.jetbrains.kotlin.resolve.calls.components.isVararg +import org.jetbrains.kotlin.resolve.constants.ConstantValue +import org.jetbrains.kotlin.resolve.constants.KClassValue.Value.LocalClass +import org.jetbrains.kotlin.resolve.constants.KClassValue.Value.NormalClass +import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass +import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny +import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperInterfaces +import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter +import org.jetbrains.kotlin.resolve.scopes.MemberScope +import org.jetbrains.kotlin.resolve.source.KotlinSourceElement +import org.jetbrains.kotlin.resolve.source.PsiSourceElement +import org.jetbrains.kotlin.types.DynamicType +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.TypeProjection +import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull +import org.jetbrains.kotlin.utils.addToStdlib.safeAs +import java.nio.file.Paths +import org.jetbrains.kotlin.resolve.constants.AnnotationValue as ConstantsAnnotationValue +import org.jetbrains.kotlin.resolve.constants.ArrayValue as ConstantsArrayValue +import org.jetbrains.kotlin.resolve.constants.EnumValue as ConstantsEnumValue +import org.jetbrains.kotlin.resolve.constants.KClassValue as ConstantsKtClassValue + +class DefaultDescriptorToDocumentableTranslator( + private val kotlinAnalysis: KotlinAnalysis +) : SourceToDocumentableTranslator { + + override fun invoke(sourceSet: DokkaSourceSet, context: DokkaContext): DModule { + val (environment, facade) = kotlinAnalysis[sourceSet] + val packageFragments = environment.getSourceFiles().asSequence() + .map { it.packageFqName } + .distinct() + .mapNotNull { facade.resolveSession.getPackageFragment(it) } + .toList() + + return DokkaDescriptorVisitor(sourceSet, kotlinAnalysis[sourceSet].facade, context.logger).run { + packageFragments.mapNotNull { it.safeAs<PackageFragmentDescriptor>() }.map { + visitPackageFragmentDescriptor( + it, + DRIWithPlatformInfo(DRI.topLevel, emptyMap()) + ) + } + }.let { DModule(sourceSet.moduleDisplayName, it, emptyMap(), null, setOf(sourceSet)) } + } +} + +data class DRIWithPlatformInfo( + val dri: DRI, + val actual: SourceSetDependent<DocumentableSource> +) + +fun DRI.withEmptyInfo() = DRIWithPlatformInfo(this, emptyMap()) + +private class DokkaDescriptorVisitor( + private val sourceSet: DokkaSourceSet, + private val resolutionFacade: DokkaResolutionFacade, + private val logger: DokkaLogger +) : DeclarationDescriptorVisitorEmptyBodies<Documentable, DRIWithPlatformInfo>() { + override fun visitDeclarationDescriptor(descriptor: DeclarationDescriptor, parent: DRIWithPlatformInfo): Nothing { + throw IllegalStateException("${javaClass.simpleName} should never enter ${descriptor.javaClass.simpleName}") + } + + private fun Collection<DeclarationDescriptor>.filterDescriptorsInSourceSet() = filter { + it.toSourceElement.containingFile.toString().let { path -> + path.isNotBlank() && sourceSet.sourceRoots.any { root -> + Paths.get(path).startsWith(Paths.get(root.path)) + } + } + } + + private fun <T> T.toSourceSetDependent() = mapOf(sourceSet to this) + + override fun visitPackageFragmentDescriptor( + descriptor: PackageFragmentDescriptor, + parent: DRIWithPlatformInfo + ): DPackage { + val name = descriptor.fqName.asString().takeUnless { it.isBlank() } ?: fallbackPackageName() + val driWithPlatform = DRI(packageName = name).withEmptyInfo() + val scope = descriptor.getMemberScope() + + return DPackage( + dri = driWithPlatform.dri, + functions = scope.functions(driWithPlatform, true), + properties = scope.properties(driWithPlatform, true), + classlikes = scope.classlikes(driWithPlatform, true), + typealiases = scope.typealiases(driWithPlatform, true), + documentation = descriptor.resolveDescriptorData(), + sourceSets = setOf(sourceSet) + ) + } + + override fun visitClassDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): DClasslike = + when (descriptor.kind) { + ClassKind.ENUM_CLASS -> enumDescriptor(descriptor, parent) + ClassKind.OBJECT -> objectDescriptor(descriptor, parent) + ClassKind.INTERFACE -> interfaceDescriptor(descriptor, parent) + ClassKind.ANNOTATION_CLASS -> annotationDescriptor(descriptor, parent) + else -> classDescriptor(descriptor, parent) + } + + private fun interfaceDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): DInterface { + val driWithPlatform = parent.dri.withClass(descriptor.name.asString()).withEmptyInfo() + val scope = descriptor.unsubstitutedMemberScope + val isExpect = descriptor.isExpect + val info = descriptor.resolveClassDescriptionData() + + return DInterface( + dri = driWithPlatform.dri, + name = descriptor.name.asString(), + functions = scope.functions(driWithPlatform), + properties = scope.properties(driWithPlatform), + classlikes = scope.classlikes(driWithPlatform), + sources = descriptor.createSources(), + expectPresentInSet = sourceSet.takeIf { isExpect }, + visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), + supertypes = info.supertypes.toSourceSetDependent(), + documentation = info.docs, + generics = descriptor.declaredTypeParameters.map { it.toTypeParameter() }, + companion = descriptor.companion(driWithPlatform), + sourceSets = setOf(sourceSet), + extra = PropertyContainer.withAll( + descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), + descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), + ImplementedInterfaces(info.allImplementedInterfaces.toSourceSetDependent()) + ) + ) + } + + private fun objectDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): DObject { + val driWithPlatform = parent.dri.withClass(descriptor.name.asString()).withEmptyInfo() + val scope = descriptor.unsubstitutedMemberScope + val isExpect = descriptor.isExpect + val info = descriptor.resolveClassDescriptionData() + + + return DObject( + dri = driWithPlatform.dri, + name = descriptor.name.asString(), + functions = scope.functions(driWithPlatform), + properties = scope.properties(driWithPlatform), + classlikes = scope.classlikes(driWithPlatform), + sources = descriptor.createSources(), + expectPresentInSet = sourceSet.takeIf { isExpect }, + visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), + supertypes = info.supertypes.toSourceSetDependent(), + documentation = info.docs, + sourceSets = setOf(sourceSet), + extra = PropertyContainer.withAll( + descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), + descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), + ImplementedInterfaces(info.allImplementedInterfaces.toSourceSetDependent()) + ) + ) + } + + private fun enumDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): DEnum { + val driWithPlatform = parent.dri.withClass(descriptor.name.asString()).withEmptyInfo() + val scope = descriptor.unsubstitutedMemberScope + val isExpect = descriptor.isExpect + val info = descriptor.resolveClassDescriptionData() + + return DEnum( + dri = driWithPlatform.dri, + name = descriptor.name.asString(), + entries = scope.enumEntries(driWithPlatform), + constructors = descriptor.constructors.map { visitConstructorDescriptor(it, driWithPlatform) }, + functions = scope.functions(driWithPlatform), + properties = scope.properties(driWithPlatform), + classlikes = scope.classlikes(driWithPlatform), + sources = descriptor.createSources(), + expectPresentInSet = sourceSet.takeIf { isExpect }, + visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), + supertypes = info.supertypes.toSourceSetDependent(), + documentation = info.docs, + companion = descriptor.companion(driWithPlatform), + sourceSets = setOf(sourceSet), + extra = PropertyContainer.withAll( + descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), + descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), + ImplementedInterfaces(info.allImplementedInterfaces.toSourceSetDependent()) + ) + ) + } + + private fun enumEntryDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): DEnumEntry { + val driWithPlatform = parent.dri.withClass(descriptor.name.asString()).withEmptyInfo() + val scope = descriptor.unsubstitutedMemberScope + val isExpect = descriptor.isExpect + + return DEnumEntry( + dri = driWithPlatform.dri, + name = descriptor.name.asString(), + documentation = descriptor.resolveDescriptorData(), + classlikes = scope.classlikes(driWithPlatform), + functions = scope.functions(driWithPlatform), + properties = scope.properties(driWithPlatform), + sourceSets = setOf(sourceSet), + expectPresentInSet = sourceSet.takeIf { isExpect }, + extra = PropertyContainer.withAll( + descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), + descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), + ConstructorValues(descriptor.getAppliedConstructorParameters().toSourceSetDependent()) + ) + ) + } + + fun annotationDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): DAnnotation { + val driWithPlatform = parent.dri.withClass(descriptor.name.asString()).withEmptyInfo() + val scope = descriptor.unsubstitutedMemberScope + + return DAnnotation( + dri = driWithPlatform.dri, + name = descriptor.name.asString(), + documentation = descriptor.resolveDescriptorData(), + classlikes = scope.classlikes(driWithPlatform), + functions = scope.functions(driWithPlatform), + properties = scope.properties(driWithPlatform), + expectPresentInSet = null, + sourceSets = setOf(sourceSet), + extra = PropertyContainer.withAll( + descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), + descriptor.getAnnotations().toSourceSetDependent().toAnnotations() + ), + companion = descriptor.companionObjectDescriptor?.let { objectDescriptor(it, driWithPlatform) }, + visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), + generics = descriptor.declaredTypeParameters.map { it.toTypeParameter() }, + constructors = descriptor.constructors.map { visitConstructorDescriptor(it, driWithPlatform) }, + sources = descriptor.createSources() + ) + } + + private fun classDescriptor(descriptor: ClassDescriptor, parent: DRIWithPlatformInfo): DClass { + val driWithPlatform = parent.dri.withClass(descriptor.name.asString()).withEmptyInfo() + val scope = descriptor.unsubstitutedMemberScope + val isExpect = descriptor.isExpect + val info = descriptor.resolveClassDescriptionData() + val actual = descriptor.createSources() + + return DClass( + dri = driWithPlatform.dri, + name = descriptor.name.asString(), + constructors = descriptor.constructors.map { + visitConstructorDescriptor( + it, + if (it.isPrimary) DRIWithPlatformInfo(driWithPlatform.dri, actual) + else DRIWithPlatformInfo(driWithPlatform.dri, emptyMap()) + ) + }, + functions = scope.functions(driWithPlatform), + properties = scope.properties(driWithPlatform), + classlikes = scope.classlikes(driWithPlatform), + sources = actual, + expectPresentInSet = sourceSet.takeIf { isExpect }, + visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), + supertypes = info.supertypes.toSourceSetDependent(), + generics = descriptor.declaredTypeParameters.map { it.toTypeParameter() }, + documentation = info.docs, + modifier = descriptor.modifier().toSourceSetDependent(), + companion = descriptor.companion(driWithPlatform), + sourceSets = setOf(sourceSet), + extra = PropertyContainer.withAll( + descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), + descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), + ImplementedInterfaces(info.allImplementedInterfaces.toSourceSetDependent()) + ) + ) + } + + override fun visitPropertyDescriptor(descriptor: PropertyDescriptor, parent: DRIWithPlatformInfo): DProperty { + val dri = parent.dri.copy(callable = Callable.from(descriptor)) + val isExpect = descriptor.isExpect + + val actual = descriptor.createSources() + return DProperty( + dri = dri, + name = descriptor.name.asString(), + receiver = descriptor.extensionReceiverParameter?.let { + visitReceiverParameterDescriptor(it, DRIWithPlatformInfo(dri, actual)) + }, + sources = actual, + getter = descriptor.accessors.filterIsInstance<PropertyGetterDescriptor>().singleOrNull()?.let { + visitPropertyAccessorDescriptor(it, descriptor, dri) + }, + setter = descriptor.accessors.filterIsInstance<PropertySetterDescriptor>().singleOrNull()?.let { + visitPropertyAccessorDescriptor(it, descriptor, dri) + }, + visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), + documentation = descriptor.resolveDescriptorData(), + modifier = descriptor.modifier().toSourceSetDependent(), + type = descriptor.returnType!!.toBound(), + expectPresentInSet = sourceSet.takeIf { isExpect }, + sourceSets = setOf(sourceSet), + generics = descriptor.typeParameters.map { it.toTypeParameter() }, + extra = PropertyContainer.withAll( + (descriptor.additionalExtras() + descriptor.getAnnotationsWithBackingField() + .toAdditionalExtras()).toSet().toSourceSetDependent().toAdditionalModifiers(), + descriptor.getAnnotationsWithBackingField().toSourceSetDependent().toAnnotations() + ) + ) + } + + fun CallableMemberDescriptor.createDRI(wasOverridenBy: DRI? = null): Pair<DRI, DRI?> = + if (kind == CallableMemberDescriptor.Kind.DECLARATION || overriddenDescriptors.isEmpty()) + Pair(DRI.from(this), wasOverridenBy) + else + overriddenDescriptors.first().createDRI(DRI.from(this)) + + override fun visitFunctionDescriptor(descriptor: FunctionDescriptor, parent: DRIWithPlatformInfo): DFunction { + val (dri, inheritedFrom) = descriptor.createDRI() + val isExpect = descriptor.isExpect + + val actual = descriptor.createSources() + return DFunction( + dri = dri, + name = descriptor.name.asString(), + isConstructor = false, + receiver = descriptor.extensionReceiverParameter?.let { + visitReceiverParameterDescriptor(it, DRIWithPlatformInfo(dri, actual)) + }, + parameters = descriptor.valueParameters.mapIndexed { index, desc -> + parameter(index, desc, DRIWithPlatformInfo(dri, actual)) + }, + expectPresentInSet = sourceSet.takeIf { isExpect }, + sources = actual, + visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), + generics = descriptor.typeParameters.map { it.toTypeParameter() }, + documentation = descriptor.takeIf { it.kind != CallableMemberDescriptor.Kind.SYNTHESIZED }?.resolveDescriptorData() ?: emptyMap(), + modifier = descriptor.modifier().toSourceSetDependent(), + type = descriptor.returnType!!.toBound(), + sourceSets = setOf(sourceSet), + extra = PropertyContainer.withAll( + InheritedFunction(inheritedFrom.toSourceSetDependent()), + descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), + descriptor.getAnnotations().toSourceSetDependent().toAnnotations() + ) + ) + } + + override fun visitConstructorDescriptor(descriptor: ConstructorDescriptor, parent: DRIWithPlatformInfo): DFunction { + val dri = parent.dri.copy(callable = Callable.from(descriptor)) + val actual = descriptor.createSources() + val isExpect = descriptor.isExpect + + return DFunction( + dri = dri, + name = "<init>", + isConstructor = true, + receiver = descriptor.extensionReceiverParameter?.let { + visitReceiverParameterDescriptor(it, DRIWithPlatformInfo(dri, actual)) + }, + parameters = descriptor.valueParameters.mapIndexed { index, desc -> + parameter(index, desc, DRIWithPlatformInfo(dri, actual)) + }, + sources = actual, + expectPresentInSet = sourceSet.takeIf { isExpect }, + visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), + documentation = descriptor.resolveDescriptorData().let { sourceSetDependent -> + if (descriptor.isPrimary) { + sourceSetDependent.map { entry -> + Pair( + entry.key, + entry.value.copy(children = (entry.value.children.find { it is Constructor }?.root?.let { constructor -> + listOf(Description(constructor)) + } ?: emptyList<TagWrapper>()) + entry.value.children.filterIsInstance<Param>())) + }.toMap() + } else { + sourceSetDependent + } + }, + type = descriptor.returnType.toBound(), + modifier = descriptor.modifier().toSourceSetDependent(), + generics = descriptor.typeParameters.map { it.toTypeParameter() }, + sourceSets = setOf(sourceSet), + extra = PropertyContainer.withAll<DFunction>( + descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), + descriptor.getAnnotations().toSourceSetDependent().toAnnotations() + ).let { + if (descriptor.isPrimary) { + it + PrimaryConstructorExtra + } else it + } + ) + } + + override fun visitReceiverParameterDescriptor( + descriptor: ReceiverParameterDescriptor, + parent: DRIWithPlatformInfo + ) = DParameter( + dri = parent.dri.copy(target = PointingToDeclaration), + name = null, + type = descriptor.type.toBound(), + expectPresentInSet = null, + documentation = descriptor.resolveDescriptorData(), + sourceSets = setOf(sourceSet), + extra = PropertyContainer.withAll(descriptor.getAnnotations().toSourceSetDependent().toAnnotations()) + ) + + private fun visitPropertyAccessorDescriptor( + descriptor: PropertyAccessorDescriptor, + propertyDescriptor: PropertyDescriptor, + parent: DRI + ): DFunction { + val dri = parent.copy(callable = Callable.from(descriptor)) + val isGetter = descriptor is PropertyGetterDescriptor + val isExpect = descriptor.isExpect + + fun PropertyDescriptor.asParameter(parent: DRI) = + DParameter( + parent.copy(target = PointingToCallableParameters(parameterIndex = 1)), + this.name.asString(), + type = this.type.toBound(), + expectPresentInSet = sourceSet.takeIf { isExpect }, + documentation = descriptor.resolveDescriptorData(), + sourceSets = setOf(sourceSet), + extra = PropertyContainer.withAll( + descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), + getAnnotationsWithBackingField().toSourceSetDependent().toAnnotations() + ) + ) + + val name = run { + val modifier = if (isGetter) "get" else "set" + val rawName = propertyDescriptor.name.asString() + "$modifier${rawName[0].toUpperCase()}${rawName.drop(1)}" + } + + val parameters = + if (isGetter) { + emptyList() + } else { + listOf(propertyDescriptor.asParameter(dri)) + } + + return DFunction( + dri, + name, + isConstructor = false, + parameters = parameters, + visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), + documentation = descriptor.resolveDescriptorData(), + type = descriptor.returnType!!.toBound(), + generics = descriptor.typeParameters.map { it.toTypeParameter() }, + modifier = descriptor.modifier().toSourceSetDependent(), + expectPresentInSet = sourceSet.takeIf { isExpect }, + receiver = descriptor.extensionReceiverParameter?.let { + visitReceiverParameterDescriptor( + it, + DRIWithPlatformInfo(dri, descriptor.createSources()) + ) + }, + sources = descriptor.createSources(), + sourceSets = setOf(sourceSet), + extra = PropertyContainer.withAll( + descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), + descriptor.getAnnotations().toSourceSetDependent().toAnnotations() + ) + ) + } + + override fun visitTypeAliasDescriptor(descriptor: TypeAliasDescriptor, parent: DRIWithPlatformInfo?) = + with(descriptor) { + DTypeAlias( + dri = DRI.from(this), + name = name.asString(), + type = defaultType.toBound(), + expectPresentInSet = null, + underlyingType = underlyingType.toBound().toSourceSetDependent(), + visibility = visibility.toDokkaVisibility().toSourceSetDependent(), + documentation = resolveDescriptorData(), + sourceSets = setOf(sourceSet) + ) + } + + private fun parameter(index: Int, descriptor: ValueParameterDescriptor, parent: DRIWithPlatformInfo) = + DParameter( + dri = parent.dri.copy(target = PointingToCallableParameters(index)), + name = descriptor.name.asString(), + type = descriptor.type.toBound(), + expectPresentInSet = null, + documentation = descriptor.resolveDescriptorData(), + sourceSets = setOf(sourceSet), + extra = PropertyContainer.withAll(listOfNotNull( + descriptor.additionalExtras().toSourceSetDependent().toAdditionalModifiers(), + descriptor.getAnnotations().toSourceSetDependent().toAnnotations(), + descriptor.getDefaultValue()?.let { DefaultValue(it) } + )) + ) + + private fun MemberScope.getContributedDescriptors(kindFilter: DescriptorKindFilter, shouldFilter: Boolean) = + getContributedDescriptors(kindFilter) { true }.let { + if (shouldFilter) it.filterDescriptorsInSourceSet() else it + } + + private fun MemberScope.functions(parent: DRIWithPlatformInfo, packageLevel: Boolean = false): List<DFunction> = + getContributedDescriptors(DescriptorKindFilter.FUNCTIONS, packageLevel) + .filterIsInstance<FunctionDescriptor>() + .map { visitFunctionDescriptor(it, parent) } + + private fun MemberScope.properties(parent: DRIWithPlatformInfo, packageLevel: Boolean = false): List<DProperty> = + getContributedDescriptors(DescriptorKindFilter.VALUES, packageLevel) + .filterIsInstance<PropertyDescriptor>() + .map { visitPropertyDescriptor(it, parent) } + + private fun MemberScope.classlikes(parent: DRIWithPlatformInfo, packageLevel: Boolean = false): List<DClasslike> = + getContributedDescriptors(DescriptorKindFilter.CLASSIFIERS, packageLevel) + .filter { it is ClassDescriptor && it.kind != ClassKind.ENUM_ENTRY } + .map { visitClassDescriptor(it as ClassDescriptor, parent) } + + private fun MemberScope.typealiases(parent: DRIWithPlatformInfo, packageLevel: Boolean = false): List<DTypeAlias> = + getContributedDescriptors(DescriptorKindFilter.TYPE_ALIASES, packageLevel) + .filterIsInstance<TypeAliasDescriptor>() + .map { visitTypeAliasDescriptor(it, parent) } + + private fun MemberScope.enumEntries(parent: DRIWithPlatformInfo): List<DEnumEntry> = + this.getContributedDescriptors(DescriptorKindFilter.CLASSIFIERS) { true } + .filterIsInstance<ClassDescriptor>() + .filter { it.kind == ClassKind.ENUM_ENTRY } + .map { enumEntryDescriptor(it, parent) } + + + private fun DeclarationDescriptor.resolveDescriptorData(): SourceSetDependent<DocumentationNode> = + getDocumentation()?.toSourceSetDependent() ?: emptyMap() + + private fun ClassDescriptor.resolveClassDescriptionData(): ClassInfo { + tailrec fun buildInheritanceInformation( + inheritorClass: ClassDescriptor?, + interfaces: List<ClassDescriptor>, + level: Int = 0, + inheritanceInformation: Set<InheritanceLevel> = emptySet() + ): Set<InheritanceLevel> { + if (inheritorClass == null && interfaces.isEmpty()) return inheritanceInformation + + val updated = inheritanceInformation + InheritanceLevel( + level, + inheritorClass?.let { DRI.from(it) }, + interfaces.map { DRI.from(it) }) + val superInterfacesFromClass = inheritorClass?.getSuperInterfaces().orEmpty() + return buildInheritanceInformation( + inheritorClass = inheritorClass?.getSuperClassNotAny(), + interfaces = interfaces.flatMap { it.getSuperInterfaces() } + superInterfacesFromClass, + level = level + 1, + inheritanceInformation = updated + ) + } + return ClassInfo( + buildInheritanceInformation(getSuperClassNotAny(), getSuperInterfaces()).sortedBy { it.level }, + resolveDescriptorData() + ) + } + + private fun TypeParameterDescriptor.toTypeParameter() = + DTypeParameter( + DRI.from(this), + name.identifier, + resolveDescriptorData(), + null, + upperBounds.map { it.toBound() }, + setOf(sourceSet), + extra = PropertyContainer.withAll(additionalExtras().toSourceSetDependent().toAdditionalModifiers()) + ) + + private fun KotlinType.toBound(): Bound = when (this) { + is DynamicType -> Dynamic + else -> when (val ctor = constructor.declarationDescriptor) { + is TypeParameterDescriptor -> OtherParameter( + declarationDRI = DRI.from(ctor.containingDeclaration).withPackageFallbackTo(fallbackPackageName()), + name = ctor.name.asString() + ) + else -> TypeConstructor( + DRI.from(constructor.declarationDescriptor!!), // TODO: remove '!!' + arguments.map { it.toProjection() }, + if (isExtensionFunctionType) FunctionModifiers.EXTENSION + else if (isFunctionType) FunctionModifiers.FUNCTION + else FunctionModifiers.NONE + ) + }.let { + if (isMarkedNullable) Nullable(it) else it + } + } + + private fun TypeProjection.toProjection(): Projection = + if (isStarProjection) Star else formPossiblyVariant() + + private fun TypeProjection.formPossiblyVariant(): Projection = type.fromPossiblyNullable().let { + when (projectionKind) { + org.jetbrains.kotlin.types.Variance.INVARIANT -> it + org.jetbrains.kotlin.types.Variance.IN_VARIANCE -> Variance(Variance.Kind.In, it) + org.jetbrains.kotlin.types.Variance.OUT_VARIANCE -> Variance(Variance.Kind.Out, it) + } + } + + private fun KotlinType.fromPossiblyNullable(): Bound = + toBound().let { if (isMarkedNullable) Nullable(it) else it } + + private fun DeclarationDescriptor.getDocumentation() = findKDoc().let { + MarkdownParser(resolutionFacade, this, logger).parseFromKDocTag(it) + }.takeIf { it.children.isNotEmpty() } + + private fun ClassDescriptor.companion(dri: DRIWithPlatformInfo): DObject? = companionObjectDescriptor?.let { + objectDescriptor(it, dri) + } + + private fun MemberDescriptor.modifier() = when (modality) { + Modality.FINAL -> KotlinModifier.Final + Modality.SEALED -> KotlinModifier.Sealed + Modality.OPEN -> KotlinModifier.Open + Modality.ABSTRACT -> KotlinModifier.Abstract + else -> KotlinModifier.Empty + } + + private fun MemberDescriptor.createSources(): SourceSetDependent<DocumentableSource> = + DescriptorDocumentableSource(this).toSourceSetDependent() + + private fun FunctionDescriptor.additionalExtras() = listOfNotNull( + ExtraModifiers.KotlinOnlyModifiers.Infix.takeIf { isInfix }, + ExtraModifiers.KotlinOnlyModifiers.Inline.takeIf { isInline }, + ExtraModifiers.KotlinOnlyModifiers.Suspend.takeIf { isSuspend }, + ExtraModifiers.KotlinOnlyModifiers.Operator.takeIf { isOperator }, + ExtraModifiers.JavaOnlyModifiers.Static.takeIf { isJvmStaticInObjectOrClassOrInterface() }, + ExtraModifiers.KotlinOnlyModifiers.TailRec.takeIf { isTailrec }, + ExtraModifiers.KotlinOnlyModifiers.External.takeIf { isExternal }, + ExtraModifiers.KotlinOnlyModifiers.Override.takeIf { DescriptorUtils.isOverride(this) } + ).toSet() + + private fun ClassDescriptor.additionalExtras() = listOfNotNull( + ExtraModifiers.KotlinOnlyModifiers.Inline.takeIf { isInline }, + ExtraModifiers.KotlinOnlyModifiers.External.takeIf { isExternal }, + ExtraModifiers.KotlinOnlyModifiers.Inner.takeIf { isInner }, + ExtraModifiers.KotlinOnlyModifiers.Data.takeIf { isData }, + ExtraModifiers.KotlinOnlyModifiers.Fun.takeIf { isFun } + ).toSet() + + private fun ValueParameterDescriptor.additionalExtras() = listOfNotNull( + ExtraModifiers.KotlinOnlyModifiers.NoInline.takeIf { isNoinline }, + ExtraModifiers.KotlinOnlyModifiers.CrossInline.takeIf { isCrossinline }, + ExtraModifiers.KotlinOnlyModifiers.Const.takeIf { isConst }, + ExtraModifiers.KotlinOnlyModifiers.LateInit.takeIf { isLateInit }, + ExtraModifiers.KotlinOnlyModifiers.VarArg.takeIf { isVararg } + ).toSet() + + private fun TypeParameterDescriptor.additionalExtras() = listOfNotNull( + ExtraModifiers.KotlinOnlyModifiers.Reified.takeIf { isReified } + ).toSet() + + private fun PropertyDescriptor.additionalExtras() = listOfNotNull( + ExtraModifiers.KotlinOnlyModifiers.Const.takeIf { isConst }, + ExtraModifiers.KotlinOnlyModifiers.LateInit.takeIf { isLateInit }, + ExtraModifiers.JavaOnlyModifiers.Static.takeIf { isJvmStaticInObjectOrClassOrInterface() }, + ExtraModifiers.KotlinOnlyModifiers.External.takeIf { isExternal }, + ExtraModifiers.KotlinOnlyModifiers.Override.takeIf { DescriptorUtils.isOverride(this) } + ) + + private fun Annotated.getAnnotations() = annotations.mapNotNull { it.toAnnotation() } + + private fun ConstantValue<*>.toValue(): AnnotationParameterValue? = when (this) { + is ConstantsAnnotationValue -> value.toAnnotation()?.let { AnnotationValue(it) } + is ConstantsArrayValue -> ArrayValue(value.mapNotNull { it.toValue() }) + is ConstantsEnumValue -> EnumValue( + fullEnumEntryName(), + DRI(enumClassId.packageFqName.asString(), fullEnumEntryName()) + ) + is ConstantsKtClassValue -> when (value) { + is NormalClass -> (value as NormalClass).value.classId.let { + ClassValue( + it.relativeClassName.asString(), + DRI(it.packageFqName.asString(), it.relativeClassName.asString()) + ) + } + is LocalClass -> (value as LocalClass).type.let { + ClassValue( + it.toString(), + DRI.from(it.constructor.declarationDescriptor as DeclarationDescriptor) + ) + } + } + else -> StringValue(toString()) + } + + private fun AnnotationDescriptor.toAnnotation(): Annotations.Annotation { + return Annotations.Annotation( + DRI.from(annotationClass as DeclarationDescriptor), + allValueArguments.map { it.key.asString() to it.value.toValue() }.filter { + it.second != null + }.toMap() as Map<String, AnnotationParameterValue>, + annotationClass!!.annotations.hasAnnotation(FqName("kotlin.annotation.MustBeDocumented")) + ) + } + + private fun PropertyDescriptor.getAnnotationsWithBackingField(): List<Annotations.Annotation> = + getAnnotations() + (backingField?.getAnnotations() ?: emptyList()) + + private fun List<Annotations.Annotation>.toAdditionalExtras() = mapNotNull { + try { + ExtraModifiers.valueOf(it.dri.classNames?.toLowerCase() ?: "") + } catch (e: IllegalArgumentException) { + null + } + } + + + private fun ValueParameterDescriptor.getDefaultValue(): String? = + (source as? KotlinSourceElement)?.psi?.children?.find { it is KtExpression }?.text + + private fun ClassDescriptor.getAppliedConstructorParameters() = + (source as PsiSourceElement).psi?.children?.flatMap { + it.safeAs<KtInitializerList>()?.initializersAsText().orEmpty() + }.orEmpty() + + private fun KtInitializerList.initializersAsText() = + initializers.firstIsInstanceOrNull<KtCallElement>() + ?.getValueArgumentsInParentheses() + ?.flatMap { it.childrenAsText() } + .orEmpty() + + private fun ValueArgument.childrenAsText() = this.safeAs<KtValueArgument>()?.children?.map { it.text }.orEmpty() + + private data class InheritanceLevel(val level: Int, val superclass: DRI?, val interfaces: List<DRI>) + + private data class ClassInfo(val inheritance: List<InheritanceLevel>, val docs: SourceSetDependent<DocumentationNode>){ + val supertypes: List<DriWithKind> + get() = inheritance.firstOrNull { it.level == 0 }?.let { + listOfNotNull(it.superclass?.let { DriWithKind(it, KotlinClassKindTypes.CLASS) }) + it.interfaces.map { DriWithKind(it, KotlinClassKindTypes.INTERFACE) } + }.orEmpty() + + val allImplementedInterfaces: List<DRI> + get() = inheritance.flatMap { it.interfaces }.distinct() + } + + private fun Visibility.toDokkaVisibility(): org.jetbrains.dokka.model.Visibility = when (this) { + Visibilities.PUBLIC -> KotlinVisibility.Public + Visibilities.PROTECTED -> KotlinVisibility.Protected + Visibilities.INTERNAL -> KotlinVisibility.Internal + Visibilities.PRIVATE -> KotlinVisibility.Private + else -> KotlinVisibility.Public + } + + private fun ConstantsEnumValue.fullEnumEntryName() = + "${this.enumClassId.relativeClassName.asString()}.${this.enumEntryName.identifier}" + + private fun fallbackPackageName(): String = + "[${sourceSet.displayName} root]"// TODO: error-prone, find a better way to do it +} + +private fun DRI.withPackageFallbackTo(fallbackPackage: String): DRI { + return if (packageName.isNullOrBlank()) { + copy(packageName = fallbackPackage) + } else { + this + } +} diff --git a/plugins/base/src/main/kotlin/translators/documentables/DefaultDocumentableToPageTranslator.kt b/plugins/base/src/main/kotlin/translators/documentables/DefaultDocumentableToPageTranslator.kt new file mode 100644 index 00000000..04251947 --- /dev/null +++ b/plugins/base/src/main/kotlin/translators/documentables/DefaultDocumentableToPageTranslator.kt @@ -0,0 +1,17 @@ +package org.jetbrains.dokka.base.translators.documentables + +import org.jetbrains.dokka.base.signatures.SignatureProvider +import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter +import org.jetbrains.dokka.model.DModule +import org.jetbrains.dokka.pages.ModulePageNode +import org.jetbrains.dokka.transformers.documentation.DocumentableToPageTranslator +import org.jetbrains.dokka.utilities.DokkaLogger + +class DefaultDocumentableToPageTranslator( + private val commentsToContentConverter: CommentsToContentConverter, + private val signatureProvider: SignatureProvider, + private val logger: DokkaLogger +) : DocumentableToPageTranslator { + override fun invoke(module: DModule): ModulePageNode = + DefaultPageCreator(commentsToContentConverter, signatureProvider, logger).pageForModule(module) +}
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt new file mode 100644 index 00000000..02f4b54e --- /dev/null +++ b/plugins/base/src/main/kotlin/translators/documentables/DefaultPageCreator.kt @@ -0,0 +1,525 @@ +package org.jetbrains.dokka.base.translators.documentables + +import org.jetbrains.dokka.base.signatures.SignatureProvider +import org.jetbrains.dokka.base.transformers.documentables.CallableExtensions +import org.jetbrains.dokka.base.transformers.documentables.InheritorsInfo +import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter +import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder.DocumentableContentBuilder +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.doc.* +import org.jetbrains.dokka.model.properties.PropertyContainer +import org.jetbrains.dokka.model.properties.WithExtraProperties +import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.utilities.DokkaLogger +import org.jetbrains.kotlin.utils.addToStdlib.safeAs +import kotlin.reflect.KClass +import kotlin.reflect.full.isSubclassOf +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet + +private typealias GroupedTags = Map<KClass<out TagWrapper>, List<Pair<DokkaSourceSet?, TagWrapper>>> + +private val specialTags: Set<KClass<out TagWrapper>> = + setOf(Property::class, Description::class, Constructor::class, Receiver::class, Param::class, See::class) + +open class DefaultPageCreator( + commentsToContentConverter: CommentsToContentConverter, + signatureProvider: SignatureProvider, + val logger: DokkaLogger +) { + protected open val contentBuilder = PageContentBuilder(commentsToContentConverter, signatureProvider, logger) + + open fun pageForModule(m: DModule) = + ModulePageNode(m.name.ifEmpty { "<root>" }, contentForModule(m), m, m.packages.map(::pageForPackage)) + + open fun pageForPackage(p: DPackage): PackagePageNode = PackagePageNode( + p.name, contentForPackage(p), setOf(p.dri), p, + p.classlikes.map(::pageForClasslike) + + p.functions.map(::pageForFunction) + ) + + open fun pageForEnumEntry(e: DEnumEntry): ClasslikePageNode = + ClasslikePageNode( + e.name, contentForEnumEntry(e), setOf(e.dri), e, + e.classlikes.map(::pageForClasslike) + + e.filteredFunctions.map(::pageForFunction) + ) + + open fun pageForClasslike(c: DClasslike): ClasslikePageNode { + val constructors = if (c is WithConstructors) c.constructors else emptyList() + + return ClasslikePageNode( + c.name.orEmpty(), contentForClasslike(c), setOf(c.dri), c, + constructors.map(::pageForFunction) + + c.classlikes.map(::pageForClasslike) + + c.filteredFunctions.map(::pageForFunction) + + if (c is DEnum) c.entries.map(::pageForEnumEntry) else emptyList() + ) + } + + open fun pageForFunction(f: DFunction) = MemberPageNode(f.name, contentForFunction(f), setOf(f.dri), f) + + open fun pageForTypeAlias(t: DTypeAlias) = MemberPageNode(t.name, contentForTypeAlias(t), setOf(t.dri), t) + + private val WithScope.filteredFunctions: List<DFunction> + get() = functions.mapNotNull { function -> + function.takeIf { + it.sourceSets.any { sourceSet -> it.extra[InheritedFunction]?.isInherited(sourceSet) != true } + } + } + + protected open fun contentForModule(m: DModule) = contentBuilder.contentFor(m) { + group(kind = ContentKind.Cover) { + cover(m.name) + if (contentForDescription(m).isNotEmpty()) { + sourceSetDependentHint( + m.dri, + m.sourceSets.toSet(), + kind = ContentKind.SourceSetDependentHint, + styles = setOf(TextStyle.UnderCoverText) + ) { + +contentForDescription(m) + } + } + } + +contentForComments(m) + block("Packages", 2, ContentKind.Packages, m.packages, m.sourceSets.toSet()) { + link(it.name, it.dri) + } +// text("Index\n") TODO +// text("Link to allpage here") + } + + protected open fun contentForPackage(p: DPackage) = contentBuilder.contentFor(p) { + group(kind = ContentKind.Cover) { + cover("Package ${p.name}") + if (contentForDescription(p).isNotEmpty()) { + sourceSetDependentHint( + p.dri, + p.sourceSets.toSet(), + kind = ContentKind.SourceSetDependentHint, + styles = setOf(TextStyle.UnderCoverText) + ) { + +contentForDescription(p) + } + } + } + group(styles = setOf(ContentStyle.TabbedContent)) { + +contentForComments(p) + +contentForScope(p, p.dri, p.sourceSets) + } + } + + protected open fun contentForScope( + s: WithScope, + dri: DRI, + sourceSets: Set<DokkaSourceSet> + ) = contentBuilder.contentFor(s as Documentable) { + val types = listOf( + s.classlikes, + (s as? DPackage)?.typealiases ?: emptyList() + ).flatten() + divergentBlock("Types", types, ContentKind.Classlikes, extra = mainExtra + SimpleAttr.header("Types")) + divergentBlock( + "Functions", + s.functions, + ContentKind.Functions, + extra = mainExtra + SimpleAttr.header("Functions") + ) + block( + "Properties", + 2, + ContentKind.Properties, + s.properties, + sourceSets.toSet(), + needsAnchors = true, + extra = mainExtra + SimpleAttr.header("Properties") + ) { + link(it.name, it.dri, kind = ContentKind.Main) + sourceSetDependentHint(it.dri, it.sourceSets.toSet(), kind = ContentKind.SourceSetDependentHint) { + contentForBrief(it) + +buildSignature(it) + } + } + s.safeAs<WithExtraProperties<Documentable>>()?.let { it.extra[InheritorsInfo] }?.let { inheritors -> + val map = inheritors.value.filter { it.value.isNotEmpty() } + if (map.values.any()) { + header(2, "Inheritors") { } + +ContentTable( + listOf(contentBuilder.contentFor(mainDRI, mainSourcesetData){ + text("Name") + }), + map.entries.flatMap { entry -> entry.value.map { Pair(entry.key, it) } } + .groupBy({ it.second }, { it.first }).map { (classlike, platforms) -> + buildGroup(setOf(dri), platforms.toSet(), ContentKind.Inheritors) { + link( + classlike.classNames?.substringBeforeLast(".") ?: classlike.toString() + .also { logger.warn("No class name found for DRI $classlike") }, classlike + ) + } + }, + DCI(setOf(dri), ContentKind.Inheritors), + sourceSets.toSet(), + style = emptySet(), + extra = mainExtra + SimpleAttr.header("Inheritors") + ) + } + } + } + + protected open fun contentForEnumEntry(e: DEnumEntry) = contentBuilder.contentFor(e) { + group(kind = ContentKind.Cover) { + cover(e.name) + sourceSetDependentHint(e.dri, e.sourceSets.toSet()) { + +contentForDescription(e) + +buildSignature(e) + } + } + group(styles = setOf(ContentStyle.TabbedContent)) { + +contentForComments(e) + +contentForScope(e, e.dri, e.sourceSets) + } + } + + protected open fun contentForClasslike(c: DClasslike) = contentBuilder.contentFor(c) { + @Suppress("UNCHECKED_CAST") + val extensions = (c as WithExtraProperties<DClasslike>) + .extra[CallableExtensions]?.extensions + ?.filterIsInstance<Documentable>().orEmpty() + // Extensions are added to sourceSets since they can be placed outside the sourceSets from classlike + // Example would be an Interface in common and extension function in jvm + group(kind = ContentKind.Cover, sourceSets = mainSourcesetData + extensions.sourceSets) { + cover(c.name.orEmpty()) + sourceSetDependentHint(c.dri, c.sourceSets) { + +contentForDescription(c) + +buildSignature(c) + } + } + + group(styles = setOf(ContentStyle.TabbedContent), sourceSets = mainSourcesetData + extensions.sourceSets) { + +contentForComments(c) + if (c is WithConstructors) { + block( + "Constructors", + 2, + ContentKind.Constructors, + c.constructors.filter { it.extra[PrimaryConstructorExtra] == null || it.documentation.isNotEmpty() }, + c.sourceSets, + extra = PropertyContainer.empty<ContentNode>() + SimpleAttr.header("Constructors") + ) { + link(it.name, it.dri, kind = ContentKind.Main) + sourceSetDependentHint( + it.dri, + it.sourceSets.toSet(), + kind = ContentKind.SourceSetDependentHint, + styles = emptySet() + ) { + contentForBrief(it) + +buildSignature(it) + } + } + } + if (c is DEnum) { + block( + "Entries", + 2, + ContentKind.Classlikes, + c.entries, + c.sourceSets.toSet(), + needsSorting = false, + extra = mainExtra + SimpleAttr.header("Entries"), + styles = emptySet() + ) { + link(it.name, it.dri) + sourceSetDependentHint(it.dri, it.sourceSets.toSet(), kind = ContentKind.SourceSetDependentHint) { + contentForBrief(it) + +buildSignature(it) + } + } + } + +contentForScope(c, c.dri, c.sourceSets) + + divergentBlock("Extensions", extensions, ContentKind.Extensions, extra = mainExtra + SimpleAttr.header("Extensions")) + } + } + + @Suppress("UNCHECKED_CAST") + private inline fun <reified T : TagWrapper> GroupedTags.withTypeUnnamed(): SourceSetDependent<T> = + (this[T::class] as List<Pair<DokkaSourceSet, T>>?)?.toMap().orEmpty() + + @Suppress("UNCHECKED_CAST") + private inline fun <reified T : NamedTagWrapper> GroupedTags.withTypeNamed(): Map<String, SourceSetDependent<T>> = + (this[T::class] as List<Pair<DokkaSourceSet, T>>?) + ?.groupBy { it.second.name } + ?.mapValues { (_, v) -> v.toMap() } + ?.toSortedMap(String.CASE_INSENSITIVE_ORDER) + .orEmpty() + + private inline fun <reified T : TagWrapper> GroupedTags.isNotEmptyForTag(): Boolean = + this[T::class]?.isNotEmpty() ?: false + + protected open fun contentForDescription( + d: Documentable + ): List<ContentNode> { + val tags: GroupedTags = d.documentation.flatMap { (pd, doc) -> + doc.children.asSequence().map { pd to it }.toList() + }.groupBy { it.second::class } + + val platforms = d.sourceSets.toSet() + + return contentBuilder.contentFor(d, styles = setOf(TextStyle.Block)) { + val description = tags.withTypeUnnamed<Description>() + if (description.any { it.value.root.children.isNotEmpty() }) { + platforms.forEach { platform -> + description[platform]?.also { + group(sourceSets = setOf(platform)) { + comment(it.root) + } + } + } + } + + val unnamedTags: List<SourceSetDependent<TagWrapper>> = + tags.filterNot { (k, _) -> k.isSubclassOf(NamedTagWrapper::class) || k in specialTags } + .map { (_, v) -> v.mapNotNull { (k, v) -> k?.let { it to v } }.toMap() } + if (unnamedTags.isNotEmpty()) { + platforms.forEach { platform -> + unnamedTags.forEach { pdTag -> + pdTag[platform]?.also { tag -> + group(sourceSets = setOf(platform)) { + header(4, tag.toHeaderString()) + comment(tag.root) + } + } + } + } + } + + contentForSinceKotlin(d) + }.children + } + + private fun Documentable.getPossibleFallbackSourcesets(sourceSet: DokkaSourceSet) = + this.sourceSets.filter { it.sourceSetID in sourceSet.dependentSourceSets } + + private fun <V> Map<DokkaSourceSet, V>.fallback(sourceSets: List<DokkaSourceSet>): V? = + sourceSets.firstOrNull { it in this.keys }.let { this[it] } + + protected open fun contentForComments( + d: Documentable + ): List<ContentNode> { + val tags: GroupedTags = d.documentation.flatMap { (pd, doc) -> + doc.children.asSequence().map { pd to it }.toList() + }.groupBy { it.second::class } + + val platforms = d.sourceSets + + fun DocumentableContentBuilder.contentForParams() { + if (tags.isNotEmptyForTag<Param>()) { + header(2, "Parameters", kind = ContentKind.Parameters) + group( + extra = mainExtra + SimpleAttr.header("Parameters"), + styles = setOf(ContentStyle.WithExtraAttributes) + ) { + sourceSetDependentHint(sourceSets = platforms.toSet(), kind = ContentKind.SourceSetDependentHint) { + val receiver = tags.withTypeUnnamed<Receiver>() + val params = tags.withTypeNamed<Param>() + table(kind = ContentKind.Parameters) { + platforms.flatMap { platform -> + val possibleFallbacks = d.getPossibleFallbackSourcesets(platform) + val receiverRow = (receiver[platform] ?: receiver.fallback(possibleFallbacks))?.let { + buildGroup(sourceSets = setOf(platform), kind = ContentKind.Parameters) { + text("<receiver>", styles = mainStyles + ContentStyle.RowTitle) + comment(it.root) + } + } + + val paramRows = params.mapNotNull { (_, param) -> + (param[platform] ?: param.fallback(possibleFallbacks))?.let { + buildGroup(sourceSets = setOf(platform), kind = ContentKind.Parameters) { + text( + it.name, + kind = ContentKind.Parameters, + styles = mainStyles + ContentStyle.RowTitle + ) + comment(it.root) + } + } + } + + listOfNotNull(receiverRow) + paramRows + } + } + } + } + } + } + + fun DocumentableContentBuilder.contentForSeeAlso() { + if (tags.isNotEmptyForTag<See>()) { + header(2, "See also", kind = ContentKind.Comment) + group( + extra = mainExtra + SimpleAttr.header("See also"), + styles = setOf(ContentStyle.WithExtraAttributes) + ) { + sourceSetDependentHint(sourceSets = platforms.toSet(), kind = ContentKind.SourceSetDependentHint) { + val seeAlsoTags = tags.withTypeNamed<See>() + table(kind = ContentKind.Sample) { + platforms.flatMap { platform -> + val possibleFallbacks = d.getPossibleFallbackSourcesets(platform) + seeAlsoTags.mapNotNull { (_, see) -> + (see[platform] ?: see.fallback(possibleFallbacks))?.let { + buildGroup( + sourceSets = setOf(platform), + kind = ContentKind.Comment, + styles = mainStyles + ContentStyle.RowTitle + ) { + if (it.address != null) link( + it.name, + it.address!!, + kind = ContentKind.Comment + ) + else text(it.name, kind = ContentKind.Comment) + comment(it.root) + } + } + } + } + } + } + } + } + } + + fun DocumentableContentBuilder.contentForSamples() { + val samples = tags.withTypeNamed<Sample>() + if (samples.isNotEmpty()) { + header(2, "Samples", kind = ContentKind.Sample) + group( + extra = mainExtra + SimpleAttr.header("Samples"), + styles = emptySet() + ) { + sourceSetDependentHint(sourceSets = platforms.toSet(), kind = ContentKind.SourceSetDependentHint) { + platforms.map { platformData -> + val content = samples.filter { it.value.isEmpty() || platformData in it.value } + group( + sourceSets = setOf(platformData), + kind = ContentKind.Sample, + styles = setOf(TextStyle.Monospace, ContentStyle.RunnableSample) + ) { + content.forEach { + text(it.key) + } + } + } + } + } + } + } + + return contentBuilder.contentFor(d) { + if (tags.isNotEmpty()) { + contentForSamples() + contentForSeeAlso() + contentForParams() + } + }.children + } + + protected open fun DocumentableContentBuilder.contentForBrief(documentable: Documentable) { + documentable.sourceSets.forEach { sourceSet -> + documentable.documentation[sourceSet]?.children?.firstOrNull()?.root?.let { + group(sourceSets = setOf(sourceSet), kind = ContentKind.BriefComment) { + comment(it) + } + } + } + } + + protected open fun DocumentableContentBuilder.contentForSinceKotlin(documentable: Documentable) { + documentable.documentation.mapValues { + it.value.children.find { it is CustomTagWrapper && it.name == "Since Kotlin" } as CustomTagWrapper? + }.run { + documentable.sourceSets.forEach { sourceSet -> + this[sourceSet]?.also { tag -> + group(sourceSets = setOf(sourceSet), kind = ContentKind.Comment, styles = setOf(TextStyle.Block)) { + header(4, tag.name) + comment(tag.root) + } + } + } + } + } + + protected open fun contentForFunction(f: DFunction) = contentForMember(f) + protected open fun contentForTypeAlias(t: DTypeAlias) = contentForMember(t) + protected open fun contentForMember(d: Documentable) = contentBuilder.contentFor(d) { + group(kind = ContentKind.Cover) { + cover(d.name.orEmpty()) + } + divergentGroup(ContentDivergentGroup.GroupID("member")) { + instance(setOf(d.dri), d.sourceSets.toSet()) { + before { + +contentForDescription(d) + +contentForComments(d) + } + divergent(kind = ContentKind.Symbol) { + +buildSignature(d) + } + } + } + } + + protected open fun DocumentableContentBuilder.divergentBlock( + name: String, + collection: Collection<Documentable>, + kind: ContentKind, + extra: PropertyContainer<ContentNode> = mainExtra + ) { + if (collection.any()) { + header(2, name, kind = kind) + table(kind, extra = extra, styles = emptySet()) { + collection + .groupBy { it.name } + // This hacks displaying actual typealias signatures along classlike ones + .mapValues { if (it.value.any { it is DClasslike }) it.value.filter { it !is DTypeAlias } else it.value } + .toSortedMap(compareBy(nullsLast(String.CASE_INSENSITIVE_ORDER)){it}) + .map { (elementName, elements) -> // This groupBy should probably use LocationProvider + buildGroup( + dri = elements.map { it.dri }.toSet(), + sourceSets = elements.flatMap { it.sourceSets }.toSet(), + kind = kind, + styles = emptySet() + ) { + link(elementName.orEmpty(), elements.first().dri, kind = kind) + divergentGroup( + ContentDivergentGroup.GroupID(name), + elements.map { it.dri }.toSet(), + kind = kind + ) { + elements.map { + instance(setOf(it.dri), it.sourceSets.toSet()) { + before { + contentForBrief(it) + contentForSinceKotlin(it) + } + divergent { + group { + +buildSignature(it) + } + } + } + } + } + } + } + } + } + } + + + protected open fun TagWrapper.toHeaderString() = this.javaClass.toGenericString().split('.').last() + + private val List<Documentable>.sourceSets: Set<DokkaSourceSet> + get() = flatMap { it.sourceSets }.toSet() +} diff --git a/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt b/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt new file mode 100644 index 00000000..b7927076 --- /dev/null +++ b/plugins/base/src/main/kotlin/translators/documentables/PageContentBuilder.kt @@ -0,0 +1,474 @@ +package org.jetbrains.dokka.base.translators.documentables + +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.base.resolvers.anchors.SymbolAnchorHint +import org.jetbrains.dokka.base.signatures.SignatureProvider +import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.Documentable +import org.jetbrains.dokka.model.SourceSetDependent +import org.jetbrains.dokka.model.doc.DocTag +import org.jetbrains.dokka.model.properties.PropertyContainer +import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.utilities.DokkaLogger + +@DslMarker +annotation class ContentBuilderMarker + +open class PageContentBuilder( + val commentsConverter: CommentsToContentConverter, + val signatureProvider: SignatureProvider, + val logger: DokkaLogger +) { + fun contentFor( + dri: DRI, + sourceSets: Set<DokkaSourceSet>, + kind: Kind = ContentKind.Main, + styles: Set<Style> = emptySet(), + extra: PropertyContainer<ContentNode> = PropertyContainer.empty(), + block: DocumentableContentBuilder.() -> Unit + ): ContentGroup = + DocumentableContentBuilder(setOf(dri), sourceSets, styles, extra) + .apply(block) + .build(sourceSets, kind, styles, extra) + + fun contentFor( + dri: Set<DRI>, + sourceSets: Set<DokkaSourceSet>, + kind: Kind = ContentKind.Main, + styles: Set<Style> = emptySet(), + extra: PropertyContainer<ContentNode> = PropertyContainer.empty(), + block: DocumentableContentBuilder.() -> Unit + ): ContentGroup = + DocumentableContentBuilder(dri, sourceSets, styles, extra) + .apply(block) + .build(sourceSets, kind, styles, extra) + + fun contentFor( + d: Documentable, + kind: Kind = ContentKind.Main, + styles: Set<Style> = emptySet(), + extra: PropertyContainer<ContentNode> = PropertyContainer.empty(), + sourceSets: Set<DokkaSourceSet> = d.sourceSets.toSet(), + block: DocumentableContentBuilder.() -> Unit = {} + ): ContentGroup = + DocumentableContentBuilder(setOf(d.dri), sourceSets, styles, extra) + .apply(block) + .build(sourceSets, kind, styles, extra) + + @ContentBuilderMarker + open inner class DocumentableContentBuilder( + val mainDRI: Set<DRI>, + val mainSourcesetData: Set<DokkaSourceSet>, + val mainStyles: Set<Style>, + val mainExtra: PropertyContainer<ContentNode> + ) { + protected val contents = mutableListOf<ContentNode>() + + fun build( + sourceSets: Set<DokkaSourceSet>, + kind: Kind, + styles: Set<Style>, + extra: PropertyContainer<ContentNode> + ) = ContentGroup( + contents.toList(), + DCI(mainDRI, kind), + sourceSets, + styles, + extra + ) + + operator fun ContentNode.unaryPlus() { + contents += this + } + + operator fun Collection<ContentNode>.unaryPlus() { + contents += this + } + + private val defaultHeaders + get() = listOf( + contentFor(mainDRI, mainSourcesetData){ + text("Name") + }, + contentFor(mainDRI, mainSourcesetData){ + text("Summary") + } + ) + + fun header( + level: Int, + text: String, + kind: Kind = ContentKind.Main, + sourceSets: Set<DokkaSourceSet> = mainSourcesetData, + styles: Set<Style> = mainStyles, + extra: PropertyContainer<ContentNode> = mainExtra, + block: DocumentableContentBuilder.() -> Unit = {} + ) { + contents += ContentHeader( + level, + contentFor( + mainDRI, + sourceSets, + kind, + styles, + extra + SimpleAttr("anchor", text.replace("\\s".toRegex(), "").toLowerCase()) + ) { + text(text, kind = kind) + block() + } + ) + } + + fun cover( + text: String, + sourceSets: Set<DokkaSourceSet> = mainSourcesetData, + styles: Set<Style> = mainStyles + TextStyle.Cover, + extra: PropertyContainer<ContentNode> = mainExtra, + block: DocumentableContentBuilder.() -> Unit = {} + ) { + header(1, text, sourceSets = sourceSets, styles = styles, extra = extra, block = block) + } + + fun text( + text: String, + kind: Kind = ContentKind.Main, + sourceSets: Set<DokkaSourceSet> = mainSourcesetData, + styles: Set<Style> = mainStyles, + extra: PropertyContainer<ContentNode> = mainExtra + ) { + contents += createText(text, kind, sourceSets, styles, extra) + } + + fun buildSignature(d: Documentable) = signatureProvider.signature(d) + + fun table( + kind: Kind = ContentKind.Main, + sourceSets: Set<DokkaSourceSet> = mainSourcesetData, + styles: Set<Style> = mainStyles, + extra: PropertyContainer<ContentNode> = mainExtra, + operation: DocumentableContentBuilder.() -> List<ContentGroup> + ) { + contents += ContentTable( + defaultHeaders, + operation(), + DCI(mainDRI, kind), + sourceSets, styles, extra + ) + } + + fun <T : Documentable> block( + name: String, + level: Int, + kind: Kind = ContentKind.Main, + elements: Iterable<T>, + sourceSets: Set<DokkaSourceSet> = mainSourcesetData, + styles: Set<Style> = mainStyles, + extra: PropertyContainer<ContentNode> = mainExtra, + renderWhenEmpty: Boolean = false, + needsSorting: Boolean = true, + headers: List<ContentGroup>? = null, + needsAnchors: Boolean = false, + operation: DocumentableContentBuilder.(T) -> Unit + ) { + if (renderWhenEmpty || elements.any()) { + header(level, name, kind = kind) { } + contents += ContentTable( + headers ?: defaultHeaders, + elements + .let { + if (needsSorting) + it.sortedWith(compareBy(nullsLast(String.CASE_INSENSITIVE_ORDER)) { it.name }) + else it + } + .map { + val newExtra = if (needsAnchors) extra + SymbolAnchorHint else extra + buildGroup(setOf(it.dri), it.sourceSets.toSet(), kind, styles, newExtra) { + operation(it) + } + }, + DCI(mainDRI, kind), + sourceSets, styles, extra + ) + } + } + + fun <T> list( + elements: List<T>, + prefix: String = "", + suffix: String = "", + separator: String = ", ", + sourceSets: Set<DokkaSourceSet> = mainSourcesetData, // TODO: children should be aware of this platform data + operation: DocumentableContentBuilder.(T) -> Unit + ) { + if (elements.isNotEmpty()) { + if (prefix.isNotEmpty()) text(prefix, sourceSets = sourceSets) + elements.dropLast(1).forEach { + operation(it) + text(separator, sourceSets = sourceSets) + } + operation(elements.last()) + if (suffix.isNotEmpty()) text(suffix, sourceSets = sourceSets) + } + } + + fun link( + text: String, + address: DRI, + kind: Kind = ContentKind.Main, + sourceSets: Set<DokkaSourceSet> = mainSourcesetData, + styles: Set<Style> = mainStyles, + extra: PropertyContainer<ContentNode> = mainExtra + ) { + contents += linkNode(text, address, kind, sourceSets, styles, extra) + } + + fun linkNode( + text: String, + address: DRI, + kind: Kind = ContentKind.Main, + sourceSets: Set<DokkaSourceSet> = mainSourcesetData, + styles: Set<Style> = mainStyles, + extra: PropertyContainer<ContentNode> = mainExtra + ) = ContentDRILink( + listOf(createText(text, kind, sourceSets, styles, extra)), + address, + DCI(mainDRI, kind), + sourceSets + ) + + fun link( + text: String, + address: String, + kind: Kind = ContentKind.Main, + sourceSets: Set<DokkaSourceSet> = mainSourcesetData, + styles: Set<Style> = mainStyles, + extra: PropertyContainer<ContentNode> = mainExtra + ) { + contents += ContentResolvedLink( + children = listOf(createText(text, kind, sourceSets, styles, extra)), + address = address, + extra = PropertyContainer.empty(), + dci = DCI(mainDRI, kind), + sourceSets = sourceSets, + style = emptySet() + ) + } + + fun link( + address: DRI, + kind: Kind = ContentKind.Main, + sourceSets: Set<DokkaSourceSet> = mainSourcesetData, + styles: Set<Style> = mainStyles, + extra: PropertyContainer<ContentNode> = mainExtra, + block: DocumentableContentBuilder.() -> Unit + ) { + contents += ContentDRILink( + contentFor(mainDRI, sourceSets, kind, styles, extra, block).children, + address, + DCI(mainDRI, kind), + sourceSets + ) + } + + fun comment( + docTag: DocTag, + kind: Kind = ContentKind.Comment, + sourceSets: Set<DokkaSourceSet> = mainSourcesetData, + styles: Set<Style> = mainStyles, + extra: PropertyContainer<ContentNode> = mainExtra + ) { + val content = commentsConverter.buildContent( + docTag, + DCI(mainDRI, kind), + sourceSets + ) + contents += ContentGroup(content, DCI(mainDRI, kind), sourceSets, styles, extra) + } + + fun group( + dri: Set<DRI> = mainDRI, + sourceSets: Set<DokkaSourceSet> = mainSourcesetData, + kind: Kind = ContentKind.Main, + styles: Set<Style> = mainStyles, + extra: PropertyContainer<ContentNode> = mainExtra, + block: DocumentableContentBuilder.() -> Unit + ) { + contents += buildGroup(dri, sourceSets, kind, styles, extra, block) + } + + fun divergentGroup( + groupID: ContentDivergentGroup.GroupID, + dri: Set<DRI> = mainDRI, + kind: Kind = ContentKind.Main, + styles: Set<Style> = mainStyles, + extra: PropertyContainer<ContentNode> = mainExtra, + implicitlySourceSetHinted: Boolean = true, + block: DivergentBuilder.() -> Unit + ) { + contents += + DivergentBuilder(dri, kind, styles, extra) + .apply(block) + .build(groupID = groupID, implicitlySourceSetHinted = implicitlySourceSetHinted) + } + + fun buildGroup( + dri: Set<DRI> = mainDRI, + sourceSets: Set<DokkaSourceSet> = mainSourcesetData, + kind: Kind = ContentKind.Main, + styles: Set<Style> = mainStyles, + extra: PropertyContainer<ContentNode> = mainExtra, + block: DocumentableContentBuilder.() -> Unit + ): ContentGroup = contentFor(dri, sourceSets, kind, styles, extra, block) + + fun sourceSetDependentHint( + dri: Set<DRI> = mainDRI, + sourceSets: Set<DokkaSourceSet> = mainSourcesetData, + kind: Kind = ContentKind.Main, + styles: Set<Style> = mainStyles, + extra: PropertyContainer<ContentNode> = mainExtra, + block: DocumentableContentBuilder.() -> Unit + ) { + contents += PlatformHintedContent( + buildGroup(dri, sourceSets, kind, styles, extra, block), + sourceSets + ) + } + + fun sourceSetDependentHint( + dri: DRI, + sourcesetData: Set<DokkaSourceSet> = mainSourcesetData, + kind: Kind = ContentKind.Main, + styles: Set<Style> = mainStyles, + extra: PropertyContainer<ContentNode> = mainExtra, + block: DocumentableContentBuilder.() -> Unit + ) { + contents += PlatformHintedContent( + buildGroup(setOf(dri), sourcesetData, kind, styles, extra, block), + sourcesetData + ) + } + + protected fun createText( + text: String, + kind: Kind, + sourceSets: Set<DokkaSourceSet>, + styles: Set<Style>, + extra: PropertyContainer<ContentNode> + ) = + ContentText(text, DCI(mainDRI, kind), sourceSets, styles, extra) + + fun <T> sourceSetDependentText( + value: SourceSetDependent<T>, + sourceSets: Set<DokkaSourceSet> = value.keys, + transform: (T) -> String + ) = value.entries.filter { it.key in sourceSets }.mapNotNull { (p, v) -> + transform(v).takeIf { it.isNotBlank() }?.let { it to p } + }.groupBy({ it.first }) { it.second }.forEach { + text(it.key, sourceSets = it.value.toSet()) + } + } + + @ContentBuilderMarker + open inner class DivergentBuilder( + private val mainDRI: Set<DRI>, + private val mainKind: Kind, + private val mainStyles: Set<Style>, + private val mainExtra: PropertyContainer<ContentNode> + ) { + private val instances: MutableList<ContentDivergentInstance> = mutableListOf() + fun instance( + dri: Set<DRI>, + sourceSets: Set<DokkaSourceSet>, // Having correct sourcesetData is crucial here, that's why there's no default + kind: Kind = mainKind, + styles: Set<Style> = mainStyles, + extra: PropertyContainer<ContentNode> = mainExtra, + block: DivergentInstanceBuilder.() -> Unit + ) { + instances += DivergentInstanceBuilder(dri, sourceSets, styles, extra) + .apply(block) + .build(kind) + } + + fun build( + groupID: ContentDivergentGroup.GroupID, + implicitlySourceSetHinted: Boolean, + kind: Kind = mainKind, + styles: Set<Style> = mainStyles, + extra: PropertyContainer<ContentNode> = mainExtra + ) = ContentDivergentGroup( + instances.toList(), + DCI(mainDRI, kind), + styles, + extra, + groupID, + implicitlySourceSetHinted + ) + } + + @ContentBuilderMarker + open inner class DivergentInstanceBuilder( + private val mainDRI: Set<DRI>, + private val mainSourceSets: Set<DokkaSourceSet>, + private val mainStyles: Set<Style>, + private val mainExtra: PropertyContainer<ContentNode> + ) { + private var before: ContentNode? = null + private var divergent: ContentNode? = null + private var after: ContentNode? = null + + fun before( + dri: Set<DRI> = mainDRI, + sourceSets: Set<DokkaSourceSet> = mainSourceSets, + kind: Kind = ContentKind.Main, + styles: Set<Style> = mainStyles, + extra: PropertyContainer<ContentNode> = mainExtra, + block: DocumentableContentBuilder.() -> Unit + ) { + contentFor(dri, sourceSets, kind, styles, extra, block) + .takeIf { it.hasAnyContent() } + .also { before = it } + } + + fun divergent( + dri: Set<DRI> = mainDRI, + sourceSets: Set<DokkaSourceSet> = mainSourceSets, + kind: Kind = ContentKind.Main, + styles: Set<Style> = mainStyles, + extra: PropertyContainer<ContentNode> = mainExtra, + block: DocumentableContentBuilder.() -> Unit + ) { + divergent = contentFor(dri, sourceSets, kind, styles, extra, block) + } + + fun after( + dri: Set<DRI> = mainDRI, + sourceSets: Set<DokkaSourceSet> = mainSourceSets, + kind: Kind = ContentKind.Main, + styles: Set<Style> = mainStyles, + extra: PropertyContainer<ContentNode> = mainExtra, + block: DocumentableContentBuilder.() -> Unit + ) { + contentFor(dri, sourceSets, kind, styles, extra, block) + .takeIf { it.hasAnyContent() } + .also { after = it } + } + + + fun build( + kind: Kind, + sourceSets: Set<DokkaSourceSet> = mainSourceSets, + styles: Set<Style> = mainStyles, + extra: PropertyContainer<ContentNode> = mainExtra + ) = + ContentDivergentInstance( + before, + divergent ?: throw IllegalStateException("Divergent block needs divergent part"), + after, + DCI(mainDRI, kind), + sourceSets, + styles, + extra + ) + } +}
\ No newline at end of file diff --git a/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt b/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt new file mode 100644 index 00000000..6f980383 --- /dev/null +++ b/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt @@ -0,0 +1,480 @@ +package org.jetbrains.dokka.base.translators.psi + +import com.intellij.lang.jvm.JvmModifier +import com.intellij.lang.jvm.annotation.JvmAnnotationAttribute +import com.intellij.lang.jvm.types.JvmReferenceType +import com.intellij.openapi.vfs.VirtualFileManager +import com.intellij.psi.* +import com.intellij.psi.impl.source.PsiClassReferenceType +import com.intellij.psi.impl.source.PsiImmediateClassType +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.analysis.KotlinAnalysis +import org.jetbrains.dokka.analysis.PsiDocumentableSource +import org.jetbrains.dokka.analysis.from +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.links.DriWithKind +import org.jetbrains.dokka.links.nextTarget +import org.jetbrains.dokka.links.withClass +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.doc.DocumentationLink +import org.jetbrains.dokka.model.doc.DocumentationNode +import org.jetbrains.dokka.model.doc.Param +import org.jetbrains.dokka.model.doc.Text +import org.jetbrains.dokka.model.properties.PropertyContainer +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.transformers.sources.SourceToDocumentableTranslator +import org.jetbrains.dokka.utilities.DokkaLogger +import org.jetbrains.kotlin.asJava.elements.KtLightAbstractAnnotation +import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys +import org.jetbrains.kotlin.cli.jvm.config.JavaSourceRoot +import org.jetbrains.kotlin.descriptors.Visibilities +import org.jetbrains.kotlin.load.java.JvmAbi +import org.jetbrains.kotlin.load.java.propertyNameByGetMethodName +import org.jetbrains.kotlin.load.java.propertyNamesBySetMethodName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.psiUtil.getChildOfType +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.utils.addToStdlib.safeAs +import java.io.File + +class DefaultPsiToDocumentableTranslator( + private val kotlinAnalysis: KotlinAnalysis +) : SourceToDocumentableTranslator { + + override fun invoke(sourceSet: DokkaSourceSet, context: DokkaContext): DModule { + + fun isFileInSourceRoots(file: File) : Boolean { + return sourceSet.sourceRoots.any { root -> file.path.startsWith(File(root.path).absolutePath) } + } + + val (environment, _) = kotlinAnalysis[sourceSet] + + val sourceRoots = environment.configuration.get(CLIConfigurationKeys.CONTENT_ROOTS) + ?.filterIsInstance<JavaSourceRoot>() + ?.mapNotNull { it.file.takeIf(::isFileInSourceRoots) } + ?: listOf() + val localFileSystem = VirtualFileManager.getInstance().getFileSystem("file") + + val psiFiles = sourceRoots.map { sourceRoot -> + sourceRoot.absoluteFile.walkTopDown().mapNotNull { + localFileSystem.findFileByPath(it.path)?.let { vFile -> + PsiManager.getInstance(environment.project).findFile(vFile) as? PsiJavaFile + } + }.toList() + }.flatten() + + val docParser = + DokkaPsiParser( + sourceSet, + context.logger + ) + return DModule( + sourceSet.moduleDisplayName, + psiFiles.mapNotNull { it.safeAs<PsiJavaFile>() }.groupBy { it.packageName }.map { (packageName, psiFiles) -> + val dri = DRI(packageName = packageName) + DPackage( + dri, + emptyList(), + emptyList(), + psiFiles.flatMap { psiFile -> + psiFile.classes.map { docParser.parseClasslike(it, dri) } + }, + emptyList(), + emptyMap(), + null, + setOf(sourceSet) + ) + }, + emptyMap(), + null, + setOf(sourceSet) + ) + } + + class DokkaPsiParser( + private val sourceSetData: DokkaSourceSet, + private val logger: DokkaLogger + ) { + + private val javadocParser: JavaDocumentationParser = JavadocParser(logger) + + private val cachedBounds = hashMapOf<String, Bound>() + + private fun PsiModifierListOwner.getVisibility() = modifierList?.children?.toList()?.let { ml -> + when { + ml.any { it.text == PsiKeyword.PUBLIC } -> JavaVisibility.Public + ml.any { it.text == PsiKeyword.PROTECTED } -> JavaVisibility.Protected + ml.any { it.text == PsiKeyword.PRIVATE } -> JavaVisibility.Private + else -> JavaVisibility.Default + } + } ?: JavaVisibility.Default + + private val PsiMethod.hash: Int + get() = "$returnType $name$parameterList".hashCode() + + private val PsiClassType.shouldBeIgnored: Boolean + get() = isClass("java.lang.Enum") || isClass("java.lang.Object") + + private fun PsiClassType.isClass(qName: String): Boolean { + val shortName = qName.substringAfterLast('.') + if (className == shortName) { + val psiClass = resolve() + return psiClass?.qualifiedName == qName + } + return false + } + + private fun <T> T.toSourceSetDependent() = mapOf(sourceSetData to this) + + fun parseClasslike(psi: PsiClass, parent: DRI): DClasslike = with(psi) { + val dri = parent.withClass(name.toString()) + val inheritanceTree = mutableListOf<AncestorLevel>() + val superMethodsKeys = hashSetOf<Int>() + val superMethods = mutableListOf<Pair<PsiMethod, DRI>>() + methods.forEach { superMethodsKeys.add(it.hash) } + fun parseSupertypes(superTypes: Array<PsiClassType>, level: Int = 0) { + if(superTypes.isEmpty()) return + val parsedClasses = superTypes.filter { !it.shouldBeIgnored }.mapNotNull { + it.resolve()?.let { + when { + it.isInterface -> DRI.from(it) to JavaClassKindTypes.INTERFACE + else -> DRI.from(it) to JavaClassKindTypes.CLASS + } + } + } + val (classes, interfaces) = parsedClasses.partition { it.second == JavaClassKindTypes.CLASS } + inheritanceTree.add(AncestorLevel(level, classes.firstOrNull()?.first, interfaces.map { it.first })) + + superTypes.forEach { type -> + (type as? PsiClassType)?.takeUnless { type.shouldBeIgnored }?.resolve()?.let { + val definedAt = DRI.from(it) + it.methods.forEach { method -> + val hash = method.hash + if (!method.isConstructor && !superMethodsKeys.contains(hash) && + method.getVisibility() != Visibilities.PRIVATE + ) { + superMethodsKeys.add(hash) + superMethods.add(Pair(method, definedAt)) + } + } + parseSupertypes(it.superTypes, level + 1) + } + } + } + parseSupertypes(superTypes) + val (regularFunctions, accessors) = splitFunctionsAndAccessors() + val documentation = javadocParser.parseDocumentation(this).toSourceSetDependent() + val allFunctions = regularFunctions.mapNotNull { if (!it.isConstructor) parseFunction(it) else null } + + superMethods.map { parseFunction(it.first, inheritedFrom = it.second) } + val source = PsiDocumentableSource(this).toSourceSetDependent() + val classlikes = innerClasses.map { parseClasslike(it, dri) } + val visibility = getVisibility().toSourceSetDependent() + val ancestors = inheritanceTree.filter { it.level == 0 }.flatMap { + listOfNotNull(it.superclass?.let { + DriWithKind( + dri = it, + kind = JavaClassKindTypes.CLASS + ) + }) + it.interfaces.map { DriWithKind(dri = it, kind = JavaClassKindTypes.INTERFACE) } + }.toSourceSetDependent() + val modifiers = getModifier().toSourceSetDependent() + val implementedInterfacesExtra = ImplementedInterfaces(inheritanceTree.flatMap { it.interfaces }.distinct().toSourceSetDependent()) + return when { + isAnnotationType -> + DAnnotation( + name.orEmpty(), + dri, + documentation, + null, + source, + allFunctions, + fields.mapNotNull { parseField(it, accessors[it].orEmpty()) }, + classlikes, + visibility, + null, + constructors.map { parseFunction(it, true) }, + mapTypeParameters(dri), + setOf(sourceSetData), + PropertyContainer.withAll(implementedInterfacesExtra, annotations.toList().toListOfAnnotations().toSourceSetDependent() + .toAnnotations()) + ) + isEnum -> DEnum( + dri, + name.orEmpty(), + fields.filterIsInstance<PsiEnumConstant>().map { entry -> + DEnumEntry( + dri.withClass("${entry.name}"), + entry.name, + javadocParser.parseDocumentation(entry).toSourceSetDependent(), + null, + emptyList(), + emptyList(), + emptyList(), + setOf(sourceSetData), + PropertyContainer.withAll(implementedInterfacesExtra, annotations.toList().toListOfAnnotations().toSourceSetDependent() + .toAnnotations()) + ) + }, + documentation, + null, + source, + allFunctions, + fields.filter { it !is PsiEnumConstant }.map { parseField(it, accessors[it].orEmpty()) }, + classlikes, + visibility, + null, + constructors.map { parseFunction(it, true) }, + ancestors, + setOf(sourceSetData), + PropertyContainer.withAll(implementedInterfacesExtra, annotations.toList().toListOfAnnotations().toSourceSetDependent() + .toAnnotations()) + ) + isInterface -> DInterface( + dri, + name.orEmpty(), + documentation, + null, + source, + allFunctions, + fields.mapNotNull { parseField(it, accessors[it].orEmpty()) }, + classlikes, + visibility, + null, + mapTypeParameters(dri), + ancestors, + setOf(sourceSetData), + PropertyContainer.withAll(implementedInterfacesExtra, annotations.toList().toListOfAnnotations().toSourceSetDependent() + .toAnnotations()) + ) + else -> DClass( + dri, + name.orEmpty(), + constructors.map { parseFunction(it, true) }, + allFunctions, + fields.mapNotNull { parseField(it, accessors[it].orEmpty()) }, + classlikes, + source, + visibility, + null, + mapTypeParameters(dri), + ancestors, + documentation, + null, + modifiers, + setOf(sourceSetData), + PropertyContainer.withAll(implementedInterfacesExtra, annotations.toList().toListOfAnnotations().toSourceSetDependent() + .toAnnotations()) + ) + } + } + + private fun parseFunction( + psi: PsiMethod, + isConstructor: Boolean = false, + inheritedFrom: DRI? = null + ): DFunction { + val dri = DRI.from(psi) + val docs = javadocParser.parseDocumentation(psi) + return DFunction( + dri, + if (isConstructor) "<init>" else psi.name, + isConstructor, + psi.parameterList.parameters.map { psiParameter -> + DParameter( + dri.copy(target = dri.target.nextTarget()), + psiParameter.name, + DocumentationNode( + listOfNotNull(docs.firstChildOfTypeOrNull<Param> { + it.firstChildOfTypeOrNull<DocumentationLink>() + ?.firstChildOfTypeOrNull<Text>()?.body == psiParameter.name + })).toSourceSetDependent(), + null, + getBound(psiParameter.type), + setOf(sourceSetData) + ) + }, + docs.toSourceSetDependent(), + null, + PsiDocumentableSource(psi).toSourceSetDependent(), + psi.getVisibility().toSourceSetDependent(), + psi.returnType?.let { getBound(type = it) } ?: Void, + psi.mapTypeParameters(dri), + null, + psi.getModifier().toSourceSetDependent(), + setOf(sourceSetData), + psi.additionalExtras().let { + PropertyContainer.withAll( + InheritedFunction(inheritedFrom.toSourceSetDependent()), + it.toSourceSetDependent().toAdditionalModifiers(), + (psi.annotations.toList().toListOfAnnotations() + it.toListOfAnnotations()).toSourceSetDependent() + .toAnnotations() + ) + } + ) + } + + private fun PsiModifierListOwner.additionalExtras() = listOfNotNull( + ExtraModifiers.JavaOnlyModifiers.Static.takeIf { hasModifier(JvmModifier.STATIC) }, + ExtraModifiers.JavaOnlyModifiers.Native.takeIf { hasModifier(JvmModifier.NATIVE) }, + ExtraModifiers.JavaOnlyModifiers.Synchronized.takeIf { hasModifier(JvmModifier.SYNCHRONIZED) }, + ExtraModifiers.JavaOnlyModifiers.StrictFP.takeIf { hasModifier(JvmModifier.STRICTFP) }, + ExtraModifiers.JavaOnlyModifiers.Transient.takeIf { hasModifier(JvmModifier.TRANSIENT) }, + ExtraModifiers.JavaOnlyModifiers.Volatile.takeIf { hasModifier(JvmModifier.VOLATILE) }, + ExtraModifiers.JavaOnlyModifiers.Transitive.takeIf { hasModifier(JvmModifier.TRANSITIVE) } + ).toSet() + + private fun Set<ExtraModifiers>.toListOfAnnotations() = map { + if (it !is ExtraModifiers.JavaOnlyModifiers.Static) + Annotations.Annotation(DRI("kotlin.jvm", it.name.toLowerCase().capitalize()), emptyMap()) + else + Annotations.Annotation(DRI("kotlin.jvm", "JvmStatic"), emptyMap()) + } + + private fun getBound(type: PsiType): Bound = + cachedBounds.getOrPut(type.canonicalText) { + when (type) { + is PsiClassReferenceType -> { + val resolved: PsiClass = type.resolve() + ?: return UnresolvedBound(type.presentableText) + when { + resolved.qualifiedName == "java.lang.Object" -> JavaObject + resolved is PsiTypeParameter && resolved.owner != null -> + OtherParameter( + declarationDRI = DRI.from(resolved.owner!!), + name = resolved.name.orEmpty() + ) + else -> + TypeConstructor(DRI.from(resolved), type.parameters.map { getProjection(it) }) + } + } + is PsiArrayType -> TypeConstructor( + DRI("kotlin", "Array"), + listOf(getProjection(type.componentType)) + ) + is PsiPrimitiveType -> if (type.name == "void") Void else PrimitiveJavaType(type.name) + is PsiImmediateClassType -> JavaObject + else -> throw IllegalStateException("${type.presentableText} is not supported by PSI parser") + } + } + + private fun getVariance(type: PsiWildcardType): Projection = when { + type.extendsBound != PsiType.NULL -> Variance(Variance.Kind.Out, getBound(type.extendsBound)) + type.superBound != PsiType.NULL -> Variance(Variance.Kind.In, getBound(type.superBound)) + else -> throw IllegalStateException("${type.presentableText} has incorrect bounds") + } + + private fun getProjection(type: PsiType): Projection = when (type) { + is PsiEllipsisType -> Star + is PsiWildcardType -> getVariance(type) + else -> getBound(type) + } + + private fun PsiModifierListOwner.getModifier() = when { + hasModifier(JvmModifier.ABSTRACT) -> JavaModifier.Abstract + hasModifier(JvmModifier.FINAL) -> JavaModifier.Final + else -> JavaModifier.Empty + } + + private fun PsiTypeParameterListOwner.mapTypeParameters(dri: DRI): List<DTypeParameter> { + fun mapBounds(bounds: Array<JvmReferenceType>): List<Bound> = + if (bounds.isEmpty()) emptyList() else bounds.mapNotNull { + (it as? PsiClassType)?.let { classType -> Nullable(getBound(classType)) } + } + return typeParameters.map { type -> + DTypeParameter( + dri.copy(target = dri.target.nextTarget()), + type.name.orEmpty(), + javadocParser.parseDocumentation(type).toSourceSetDependent(), + null, + mapBounds(type.bounds), + setOf(sourceSetData) + ) + } + } + + private fun PsiMethod.getPropertyNameForFunction() = + getAnnotation(DescriptorUtils.JVM_NAME.asString())?.findAttributeValue("name")?.text + ?: when { + JvmAbi.isGetterName(name) -> propertyNameByGetMethodName(Name.identifier(name))?.asString() + JvmAbi.isSetterName(name) -> propertyNamesBySetMethodName(Name.identifier(name)).firstOrNull() + ?.asString() + else -> null + } + + private fun PsiClass.splitFunctionsAndAccessors(): Pair<MutableList<PsiMethod>, MutableMap<PsiField, MutableList<PsiMethod>>> { + val fieldNames = fields.map { it.name to it }.toMap() + val accessors = mutableMapOf<PsiField, MutableList<PsiMethod>>() + val regularMethods = mutableListOf<PsiMethod>() + methods.forEach { method -> + val field = method.getPropertyNameForFunction()?.let { name -> fieldNames[name] } + if (field != null) { + accessors.getOrPut(field, ::mutableListOf).add(method) + } else { + regularMethods.add(method) + } + } + return regularMethods to accessors + } + + private fun parseField(psi: PsiField, accessors: List<PsiMethod>): DProperty { + val dri = DRI.from(psi) + return DProperty( + dri, + psi.name, + javadocParser.parseDocumentation(psi).toSourceSetDependent(), + null, + PsiDocumentableSource(psi).toSourceSetDependent(), + psi.getVisibility().toSourceSetDependent(), + getBound(psi.type), + null, + accessors.firstOrNull { it.hasParameters() }?.let { parseFunction(it) }, + accessors.firstOrNull { it.returnType == psi.type }?.let { parseFunction(it) }, + psi.getModifier().toSourceSetDependent(), + setOf(sourceSetData), + emptyList(), + psi.additionalExtras().let { + PropertyContainer.withAll<DProperty>( + it.toSourceSetDependent().toAdditionalModifiers(), + (psi.annotations.toList().toListOfAnnotations() + it.toListOfAnnotations()).toSourceSetDependent() + .toAnnotations() + ) + } + ) + } + + private fun Collection<PsiAnnotation>.toListOfAnnotations() = + filter { it !is KtLightAbstractAnnotation }.mapNotNull { it.toAnnotation() } + + private fun JvmAnnotationAttribute.toValue(): AnnotationParameterValue = when (this) { + is PsiNameValuePair -> value?.toValue() ?: StringValue("") + else -> StringValue(this.attributeName) + } + + private fun PsiAnnotationMemberValue.toValue(): AnnotationParameterValue? = when (this) { + is PsiAnnotation -> toAnnotation()?.let { AnnotationValue(it) } + is PsiArrayInitializerMemberValue -> ArrayValue(initializers.mapNotNull { it.toValue() }) + is PsiReferenceExpression -> psiReference?.let { EnumValue(text ?: "", DRI.from(it)) } + is PsiClassObjectAccessExpression -> { + val psiClass = ((type as PsiImmediateClassType).parameters.single() as PsiClassReferenceType).resolve() + psiClass?.let { ClassValue(text ?: "", DRI.from(psiClass)) } + } + else -> StringValue(text ?: "") + } + + private fun PsiAnnotation.toAnnotation() = psiReference?.let { psiElement -> + Annotations.Annotation( + DRI.from(psiElement), + attributes.filter { it !is KtLightAbstractAnnotation }.mapNotNull { it.attributeName to it.toValue() } + .toMap(), + (psiElement as PsiClass).annotations.any { + it.hasQualifiedName("java.lang.annotation.Documented") + } + ) + } + + private val PsiElement.psiReference + get() = getChildOfType<PsiJavaCodeReferenceElement>()?.resolve() + } + + private data class AncestorLevel(val level: Int, val superclass: DRI?, val interfaces: List<DRI>) +} diff --git a/plugins/base/src/main/kotlin/translators/psi/JavadocParser.kt b/plugins/base/src/main/kotlin/translators/psi/JavadocParser.kt new file mode 100644 index 00000000..81955fde --- /dev/null +++ b/plugins/base/src/main/kotlin/translators/psi/JavadocParser.kt @@ -0,0 +1,216 @@ +package org.jetbrains.dokka.base.translators.psi + +import com.intellij.psi.* +import com.intellij.psi.impl.source.javadoc.PsiDocParamRef +import com.intellij.psi.impl.source.tree.JavaDocElementType +import com.intellij.psi.impl.source.tree.LeafPsiElement +import com.intellij.psi.javadoc.* +import com.intellij.psi.util.PsiTreeUtil +import org.jetbrains.dokka.analysis.from +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.doc.* +import org.jetbrains.dokka.model.doc.Deprecated +import org.jetbrains.dokka.utilities.DokkaLogger +import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull +import org.jetbrains.kotlin.utils.addToStdlib.safeAs +import org.jsoup.Jsoup +import org.jsoup.nodes.Element +import org.jsoup.nodes.Node +import org.jsoup.nodes.TextNode + +interface JavaDocumentationParser { + fun parseDocumentation(element: PsiNamedElement): DocumentationNode +} + +class JavadocParser( + private val logger: DokkaLogger // TODO: Add logging +) : JavaDocumentationParser { + + override fun parseDocumentation(element: PsiNamedElement): DocumentationNode { + val docComment = findClosestDocComment(element) ?: return DocumentationNode(emptyList()) + val nodes = mutableListOf<TagWrapper>() + docComment.getDescription()?.let { nodes.add(it) } + nodes.addAll(docComment.tags.mapNotNull { tag -> + when (tag.name) { + "param" -> Param(P(convertJavadocElements(tag.dataElements.toList())), tag.text) + "throws" -> Throws(P(convertJavadocElements(tag.dataElements.toList())), tag.text) + "return" -> Return(P(convertJavadocElements(tag.dataElements.toList()))) + "author" -> Author(P(convertJavadocElements(tag.dataElements.toList()))) + "see" -> See(P(getSeeTagElementContent(tag)), tag.referenceElement()?.text.orEmpty(), null) + "deprecated" -> Deprecated(P(convertJavadocElements(tag.dataElements.toList()))) + else -> null + } + }) + return DocumentationNode(nodes) + } + + private fun findClosestDocComment(element: PsiNamedElement): PsiDocComment? { + (element as? PsiDocCommentOwner)?.docComment?.run { return this } + if (element is PsiMethod) { + val superMethods = element.findSuperMethodsOrEmptyArray() + if (superMethods.isEmpty()) return null + + if (superMethods.size == 1) { + return findClosestDocComment(superMethods.single()) + } + + val superMethodDocumentation = superMethods.map(::findClosestDocComment) + if (superMethodDocumentation.size == 1) { + return superMethodDocumentation.single() + } + + logger.warn( + "Conflicting documentation for ${DRI.from(element)}" + + "${superMethods.map { DRI.from(it) }}" + ) + + /* Prioritize super class over interface */ + val indexOfSuperClass = superMethods.indexOfFirst { method -> + val parent = method.parent + if (parent is PsiClass) !parent.isInterface + else false + } + + return if (indexOfSuperClass >= 0) superMethodDocumentation[indexOfSuperClass] + else superMethodDocumentation.first() + } + + return null + } + + /** + * Workaround for failing [PsiMethod.findSuperMethods]. + * This might be resolved once ultra light classes are enabled for dokka + * See [KT-39518](https://youtrack.jetbrains.com/issue/KT-39518) + */ + private fun PsiMethod.findSuperMethodsOrEmptyArray(): Array<PsiMethod> { + return try { + /* + We are not even attempting to call "findSuperMethods" on all methods called "getGetter" or "getSetter" + on any object implementing "kotlin.reflect.KProperty", since we know that those methods will fail + (KT-39518). Just catching the exception is not good enough, since "findSuperMethods" will + print the whole exception to stderr internally and then spoil the console. + */ + val kPropertyFqName = FqName("kotlin.reflect.KProperty") + if ( + this.parent?.safeAs<PsiClass>()?.implementsInterface(kPropertyFqName) == true && + (this.name == "getSetter" || this.name == "getGetter") + ) { + logger.warn("Skipped lookup of super methods for ${getKotlinFqName()} (KT-39518)") + return emptyArray() + } + findSuperMethods() + } catch (exception: Throwable) { + logger.warn("Failed to lookup of super methods for ${getKotlinFqName()} (KT-39518)") + emptyArray() + } + } + + private fun PsiClass.implementsInterface(fqName: FqName): Boolean { + return allInterfaces().any { it.getKotlinFqName() == fqName } + } + + private fun PsiClass.allInterfaces(): Sequence<PsiClass> { + return sequence { + this.yieldAll(interfaces.toList()) + interfaces.forEach { yieldAll(it.allInterfaces()) } + } + } + + private fun getSeeTagElementContent(tag: PsiDocTag): List<DocTag> = + listOfNotNull(tag.referenceElement()?.toDocumentationLink()) + + private fun PsiDocComment.getDescription(): Description? { + val nonEmptyDescriptionElements = descriptionElements.filter { it.text.trim().isNotEmpty() } + val convertedDescriptionElements = convertJavadocElements(nonEmptyDescriptionElements) + if (convertedDescriptionElements.isNotEmpty()) { + return Description(P(convertedDescriptionElements)) + } + + return null + } + + private fun convertJavadocElements(elements: Iterable<PsiElement>): List<DocTag> = + elements.mapNotNull { + when (it) { + is PsiReference -> convertJavadocElements(it.children.toList()) + is PsiInlineDocTag -> listOfNotNull(convertInlineDocTag(it)) + is PsiDocParamRef -> listOfNotNull(it.toDocumentationLink()) + is PsiDocTagValue, + is LeafPsiElement -> Jsoup.parse(it.text.trim()).body().childNodes().mapNotNull(::convertHtmlNode) + else -> null + } + }.flatten() + + private fun convertHtmlNode(node: Node, insidePre: Boolean = false): DocTag? = when (node) { + is TextNode -> Text(body = if (insidePre) node.wholeText else node.text()) + is Element -> createBlock(node) + else -> null + } + + private fun createBlock(element: Element): DocTag { + val children = element.childNodes().mapNotNull { convertHtmlNode(it) } + return when (element.tagName()) { + "p" -> P(listOf(Br, Br) + children) + "b" -> B(children) + "strong" -> Strong(children) + "i" -> I(children) + "em" -> Em(children) + "code" -> CodeBlock(children) + "pre" -> Pre(children) + "ul" -> Ul(children) + "ol" -> Ol(children) + "li" -> Li(children) + "a" -> createLink(element, children) + else -> Text(body = element.ownText()) + } + } + + private fun createLink(element: Element, children: List<DocTag>): DocTag { + return when { + element.hasAttr("docref") -> { + A(children, params = mapOf("docref" to element.attr("docref"))) + } + element.hasAttr("href") -> { + A(children, params = mapOf("href" to element.attr("href"))) + } + else -> Text(children = children) + } + } + + private fun PsiDocToken.isSharpToken() = tokenType.toString() == "DOC_TAG_VALUE_SHARP_TOKEN" + + private fun PsiElement.toDocumentationLink(labelElement: PsiElement? = null) = + reference?.resolve()?.let { + val dri = DRI.from(it) + val label = labelElement ?: children.firstOrNull { + it is PsiDocToken && it.text.isNotBlank() && !it.isSharpToken() + } ?: this + DocumentationLink(dri, convertJavadocElements(listOfNotNull(label))) + } + + private fun convertInlineDocTag(tag: PsiInlineDocTag) = when (tag.name) { + "link", "linkplain" -> { + tag.referenceElement()?.toDocumentationLink(tag.dataElements.firstIsInstanceOrNull<PsiDocToken>()) + } + "code", "literal" -> { + CodeInline(listOf(Text(tag.text))) + } + "index" -> Index(tag.children.filterIsInstance<PsiDocTagValue>().map { Text(it.text) }) + else -> Text(tag.text) + } + + private fun PsiDocTag.referenceElement(): PsiElement? = + linkElement()?.let { + if (it.node.elementType == JavaDocElementType.DOC_REFERENCE_HOLDER) { + PsiTreeUtil.findChildOfType(it, PsiJavaCodeReferenceElement::class.java) + } else { + it + } + } + + private fun PsiDocTag.linkElement(): PsiElement? = + valueElement ?: dataElements.firstOrNull { it !is PsiWhiteSpace } +} diff --git a/plugins/base/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin b/plugins/base/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin new file mode 100644 index 00000000..bc8de448 --- /dev/null +++ b/plugins/base/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin @@ -0,0 +1 @@ +org.jetbrains.dokka.base.DokkaBase diff --git a/core/src/main/resources/dokka/format/gfm.properties b/plugins/base/src/main/resources/dokka/format/gfm.properties index 5e8f7aa8..5e8f7aa8 100644 --- a/core/src/main/resources/dokka/format/gfm.properties +++ b/plugins/base/src/main/resources/dokka/format/gfm.properties diff --git a/core/src/main/resources/dokka/format/html-as-java.properties b/plugins/base/src/main/resources/dokka/format/html-as-java.properties index f598f377..f598f377 100644 --- a/core/src/main/resources/dokka/format/html-as-java.properties +++ b/plugins/base/src/main/resources/dokka/format/html-as-java.properties diff --git a/core/src/main/resources/dokka/format/html.properties b/plugins/base/src/main/resources/dokka/format/html.properties index 7881dfae..7881dfae 100644 --- a/core/src/main/resources/dokka/format/html.properties +++ b/plugins/base/src/main/resources/dokka/format/html.properties diff --git a/core/src/main/resources/dokka/format/java-layout-html.properties b/plugins/base/src/main/resources/dokka/format/java-layout-html.properties index fbb2bbed..fbb2bbed 100644 --- a/core/src/main/resources/dokka/format/java-layout-html.properties +++ b/plugins/base/src/main/resources/dokka/format/java-layout-html.properties diff --git a/core/src/main/resources/dokka/format/jekyll.properties b/plugins/base/src/main/resources/dokka/format/jekyll.properties index b11401a4..b11401a4 100644 --- a/core/src/main/resources/dokka/format/jekyll.properties +++ b/plugins/base/src/main/resources/dokka/format/jekyll.properties diff --git a/core/src/main/resources/dokka/format/kotlin-website-html.properties b/plugins/base/src/main/resources/dokka/format/kotlin-website-html.properties index f4c320b9..f4c320b9 100644 --- a/core/src/main/resources/dokka/format/kotlin-website-html.properties +++ b/plugins/base/src/main/resources/dokka/format/kotlin-website-html.properties diff --git a/core/src/main/resources/dokka/format/markdown.properties b/plugins/base/src/main/resources/dokka/format/markdown.properties index 6217a6df..6217a6df 100644 --- a/core/src/main/resources/dokka/format/markdown.properties +++ b/plugins/base/src/main/resources/dokka/format/markdown.properties diff --git a/plugins/base/src/main/resources/dokka/images/arrow_down.svg b/plugins/base/src/main/resources/dokka/images/arrow_down.svg new file mode 100755 index 00000000..89e7df47 --- /dev/null +++ b/plugins/base/src/main/resources/dokka/images/arrow_down.svg @@ -0,0 +1,3 @@ +<svg width="10" height="7" viewBox="0 0 10 7" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M9.71824 1.66658L9.01113 0.959473L5.00497 4.96447L1.00008 0.959473L0.292969 1.66658L5.01113 6.38474L9.71824 1.66658Z" fill="#A1AAB4"/> +</svg> diff --git a/plugins/base/src/main/resources/dokka/images/docs_logo.svg b/plugins/base/src/main/resources/dokka/images/docs_logo.svg new file mode 100644 index 00000000..7c1e3ae8 --- /dev/null +++ b/plugins/base/src/main/resources/dokka/images/docs_logo.svg @@ -0,0 +1,7 @@ +<svg width="125" height="27" viewBox="0 0 125 27" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M89.1611 7.6297V25.6345V25.6867H103.843V21.8039H93.3589V10.3852H103.843V6.50244H89.1611V7.6297Z" fill="#27282C"/> +<path d="M124.989 21.8039L114.778 10.3852H124.905V6.50244H109.059V10.3852L119.459 21.8039H109.059V25.6867H125V21.8039H124.989Z" fill="#27282C"/> +<path d="M58.2978 7.76556C56.5872 6.46086 54.4463 5.67804 52.1271 5.67804C46.5336 5.67804 42 10.1871 42 15.7503C42 21.3135 46.5336 25.8226 52.1271 25.8226C54.4463 25.8226 56.5872 25.0502 58.2978 23.735V25.7182H62.4955V0H58.2978V7.76556ZM52.1271 21.8041C48.7584 21.8041 46.0298 19.0903 46.0298 15.7399C46.0298 12.3894 48.7584 9.67563 52.1271 9.67563C55.4958 9.67563 58.2243 12.3894 58.2243 15.7399C58.2138 19.0903 55.4853 21.8041 52.1271 21.8041Z" fill="#27282C"/> +<path d="M75.9698 5.8656C70.3763 5.8656 65.8428 10.3746 65.8428 15.9379C65.8428 21.5011 70.3763 26.0101 75.9698 26.0101C81.5633 26.0101 86.0969 21.5011 86.0969 15.9379C86.0969 10.3746 81.5633 5.8656 75.9698 5.8656ZM75.9698 21.9916C72.6012 21.9916 69.8726 19.2779 69.8726 15.9274C69.8726 12.577 72.6012 9.86319 75.9698 9.86319C79.3385 9.86319 82.0671 12.577 82.0671 15.9274C82.0671 19.2779 79.3385 21.9916 75.9698 21.9916Z" fill="#27282C"/> +<path d="M26 26H0V0H26L12.9243 12.9747L26 26Z" fill="#F8873C"/> +</svg> diff --git a/plugins/base/src/main/resources/dokka/images/logo-icon.svg b/plugins/base/src/main/resources/dokka/images/logo-icon.svg new file mode 100755 index 00000000..1b3b3670 --- /dev/null +++ b/plugins/base/src/main/resources/dokka/images/logo-icon.svg @@ -0,0 +1,3 @@ +<svg width="26" height="26" viewBox="0 0 26 26" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M26 26H0V0H26L12.9243 12.9747L26 26Z" fill="#F8873C"/> +</svg> diff --git a/plugins/base/src/main/resources/dokka/images/logo-text.svg b/plugins/base/src/main/resources/dokka/images/logo-text.svg new file mode 100755 index 00000000..7bf3e6c5 --- /dev/null +++ b/plugins/base/src/main/resources/dokka/images/logo-text.svg @@ -0,0 +1,6 @@ +<svg width="83" height="27" viewBox="0 0 83 27" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M47.1611 7.6297V25.6345V25.6867H61.8428V21.8039H51.3589V10.3852H61.8428V6.50244H47.1611V7.6297Z" fill="#27282C"/> +<path d="M82.9891 21.8039L72.778 10.3852H82.9051V6.50244H67.0586V10.3852L77.4585 21.8039H67.0586V25.6867H82.9996V21.8039H82.9891Z" fill="#27282C"/> +<path d="M16.2978 7.76556C14.5872 6.46086 12.4463 5.67804 10.1271 5.67804C4.53357 5.67804 0 10.1871 0 15.7503C0 21.3135 4.53357 25.8226 10.1271 25.8226C12.4463 25.8226 14.5872 25.0502 16.2978 23.735V25.7182H20.4955V0H16.2978V7.76556ZM10.1271 21.8041C6.75838 21.8041 4.02984 19.0903 4.02984 15.7399C4.02984 12.3894 6.75838 9.67563 10.1271 9.67563C13.4958 9.67563 16.2243 12.3894 16.2243 15.7399C16.2138 19.0903 13.4853 21.8041 10.1271 21.8041Z" fill="#27282C"/> +<path d="M33.9703 5.86566C28.3768 5.86566 23.8433 10.3747 23.8433 15.9379C23.8433 21.5011 28.3768 26.0102 33.9703 26.0102C39.5638 26.0102 44.0974 21.5011 44.0974 15.9379C44.0974 10.3747 39.5638 5.86566 33.9703 5.86566ZM33.9703 21.9917C30.6016 21.9917 27.8731 19.2779 27.8731 15.9275C27.8731 12.577 30.6016 9.86325 33.9703 9.86325C37.339 9.86325 40.0676 12.577 40.0676 15.9275C40.0676 19.2779 37.339 21.9917 33.9703 21.9917Z" fill="#27282C"/> +</svg> diff --git a/core/src/main/resources/dokka/inbound-link-resolver/dokka-default.properties b/plugins/base/src/main/resources/dokka/inbound-link-resolver/dokka-default.properties index c484a920..c484a920 100644 --- a/core/src/main/resources/dokka/inbound-link-resolver/dokka-default.properties +++ b/plugins/base/src/main/resources/dokka/inbound-link-resolver/dokka-default.properties diff --git a/core/src/main/resources/dokka/inbound-link-resolver/java-layout-html.properties b/plugins/base/src/main/resources/dokka/inbound-link-resolver/java-layout-html.properties index 3b61eabe..3b61eabe 100644 --- a/core/src/main/resources/dokka/inbound-link-resolver/java-layout-html.properties +++ b/plugins/base/src/main/resources/dokka/inbound-link-resolver/java-layout-html.properties diff --git a/core/src/main/resources/dokka/inbound-link-resolver/javadoc.properties b/plugins/base/src/main/resources/dokka/inbound-link-resolver/javadoc.properties index 0d5d7d17..0d5d7d17 100644 --- a/core/src/main/resources/dokka/inbound-link-resolver/javadoc.properties +++ b/plugins/base/src/main/resources/dokka/inbound-link-resolver/javadoc.properties diff --git a/plugins/base/src/main/resources/dokka/scripts/clipboard.js b/plugins/base/src/main/resources/dokka/scripts/clipboard.js new file mode 100644 index 00000000..b00ce246 --- /dev/null +++ b/plugins/base/src/main/resources/dokka/scripts/clipboard.js @@ -0,0 +1,52 @@ +window.addEventListener('load', () => { + document.querySelectorAll('span.copy-icon').forEach(element => { + element.addEventListener('click', (el) => copyElementsContentToClipboard(element)); + }) + + document.querySelectorAll('span.anchor-icon').forEach(element => { + element.addEventListener('click', (el) => { + if(element.hasAttribute('pointing-to')){ + const location = hrefWithoutCurrentlyUsedAnchor() + '#' + element.getAttribute('pointing-to') + copyTextToClipboard(element, location) + } + }); + }) +}) + +const copyElementsContentToClipboard = (element) => { + const selection = window.getSelection(); + const range = document.createRange(); + range.selectNodeContents(element.parentNode.parentNode); + selection.removeAllRanges(); + selection.addRange(range); + + copyAndShowPopup(element, () => selection.removeAllRanges()) +} + +const copyTextToClipboard = (element, text) => { + var textarea = document.createElement("textarea"); + textarea.textContent = text; + textarea.style.position = "fixed"; + document.body.appendChild(textarea); + textarea.select(); + + copyAndShowPopup(element, () => document.body.removeChild(textarea)) +} + +const copyAndShowPopup = (element, after) => { + try { + document.execCommand('copy'); + element.nextElementSibling.classList.add('active-popup'); + setTimeout(() => { + element.nextElementSibling.classList.remove('active-popup'); + }, 1200); + } catch (e) { + console.error('Failed to write to clipboard:', e) + } + finally { + if(after) after() + } +} + +const hrefWithoutCurrentlyUsedAnchor = () => window.location.href.split('#')[0] + diff --git a/plugins/base/src/main/resources/dokka/scripts/navigationLoader.js b/plugins/base/src/main/resources/dokka/scripts/navigationLoader.js new file mode 100644 index 00000000..c2f60ec5 --- /dev/null +++ b/plugins/base/src/main/resources/dokka/scripts/navigationLoader.js @@ -0,0 +1,54 @@ +window.addEventListener('load', () => { + fetch(pathToRoot + "navigation.html") + .then(response => response.text()) + .then(data => { + document.getElementById("sideMenu").innerHTML = data; + }).then(() => { + document.querySelectorAll(".overview > a").forEach(link => { + link.setAttribute("href", pathToRoot + link.getAttribute("href")); + }) + }).then(() => { + document.querySelectorAll(".sideMenuPart").forEach(nav => { + if (!nav.classList.contains("hidden")) nav.classList.add("hidden") + }) + }).then(() => { + revealNavigationForCurrentPage() + }) + + /* Smooth scrolling support for going to the top of the page */ + document.querySelectorAll('a[href^="#"]').forEach(anchor => { + anchor.addEventListener('click', function (e) { + e.preventDefault(); + + document.querySelector(this.getAttribute('href')).scrollIntoView({ + behavior: 'smooth' + }); + }); + }); +}) + +revealNavigationForCurrentPage = () => { + let pageId = document.getElementById("content").attributes["pageIds"].value.toString(); + let parts = document.querySelectorAll(".sideMenuPart"); + let found = 0; + do { + parts.forEach(part => { + if (part.attributes['pageId'].value.indexOf(pageId) !== -1 && found === 0) { + found = 1; + if (part.classList.contains("hidden")){ + part.classList.remove("hidden"); + part.setAttribute('data-active',""); + } + revealParents(part) + } + }); + pageId = pageId.substring(0, pageId.lastIndexOf("/")) + } while (pageId.indexOf("/") !== -1 && found === 0) +}; + +revealParents = (part) => { + if (part.classList.contains("sideMenuPart")) { + if (part.classList.contains("hidden")) part.classList.remove("hidden"); + revealParents(part.parentNode) + } +};
\ No newline at end of file diff --git a/plugins/base/src/main/resources/dokka/scripts/platformContentHandler.js b/plugins/base/src/main/resources/dokka/scripts/platformContentHandler.js new file mode 100644 index 00000000..6f10b08a --- /dev/null +++ b/plugins/base/src/main/resources/dokka/scripts/platformContentHandler.js @@ -0,0 +1,226 @@ +filteringContext = { + dependencies: {}, + restrictedDependencies: [], + activeFilters: [] +} +window.addEventListener('load', () => { + document.querySelectorAll("div[data-platform-hinted]") + .forEach(elem => elem.addEventListener('click', (event) => togglePlatformDependent(event,elem))) + document.querySelectorAll("div[tabs-section]") + .forEach(elem => elem.addEventListener('click', (event) => toggleSectionsEventHandler(event))) + const filterSection = document.getElementById('filter-section') + if (filterSection) { + filterSection.addEventListener('click', (event) => filterButtonHandler(event)) + initializeFiltering() + } + initTabs() + handleAnchor() +}) + +function handleAnchor() { + let searchForTab = function(element) { + if(element && element.hasAttribute) { + if(element.hasAttribute("data-togglable")) return element; + else return searchForTab(element.parentNode) + } else return null + } + let anchor = window.location.hash + if (anchor != "") { + anchor = anchor.substring(1) + let element = document.querySelector('a[data-name="' + anchor+'"]') + if (element) { + let tab = searchForTab(element) + if (tab) { + let found = document.querySelector('.tabs-section > .section-tab[data-togglable="' + tab.getAttribute("data-togglable") + '"]') + toggleSections(tab) + element.scrollIntoView({behavior: "smooth"}) + } + } + } +} + +function initTabs(){ + document.querySelectorAll("div[tabs-section]") + .forEach(element => { + showCorrespondingTabBody(element) + element.addEventListener('click', (event) => toggleSectionsEventHandler(event)) + }) + let cached = localStorage.getItem("active-tab") + if (cached) { + let parsed = JSON.parse(cached) + let tab = document.querySelector('div[tabs-section] > button[data-togglable="' + parsed + '"]') + if(tab) { + toggleSections(tab) + } + } +} + +function showCorrespondingTabBody(element){ + const key = element.querySelector("button[data-active]").getAttribute("data-togglable") + document.querySelector(".tabs-section-body") + .querySelector("div[data-togglable='" + key + "']") + .setAttribute("data-active", "") +} + +function filterButtonHandler(event) { + if(event.target.tagName == "BUTTON" && event.target.hasAttribute("data-filter")) { + let sourceset = event.target.getAttribute("data-filter") + if(filteringContext.activeFilters.indexOf(sourceset) != -1) { + filterSourceset(sourceset) + } else { + unfilterSourceset(sourceset) + } + } +} + +function initializeFiltering() { + filteringContext.dependencies = JSON.parse(sourceset_dependencies) + document.querySelectorAll("#filter-section > button") + .forEach(p => filteringContext.restrictedDependencies.push(p.getAttribute("data-filter"))) + Object.keys(filteringContext.dependencies).forEach(p => { + filteringContext.dependencies[p] = filteringContext.dependencies[p] + .filter(q => -1 !== filteringContext.restrictedDependencies.indexOf(q)) + }) + let cached = window.localStorage.getItem('inactive-filters') + if (cached) { + let parsed = JSON.parse(cached) + filteringContext.activeFilters = filteringContext.restrictedDependencies + .filter(q => parsed.indexOf(q) == -1 ) + } else { + filteringContext.activeFilters = filteringContext.restrictedDependencies + } + refreshFiltering() +} + +function filterSourceset(sourceset) { + filteringContext.activeFilters = filteringContext.activeFilters.filter(p => p != sourceset) + refreshFiltering() + addSourcesetFilterToCache(sourceset) +} + +function unfilterSourceset(sourceset) { + if(filteringContext.activeFilters.length == 0) { + filteringContext.activeFilters = filteringContext.dependencies[sourceset].concat([sourceset]) + refreshFiltering() + filteringContext.dependencies[sourceset].concat([sourceset]).forEach(p => removeSourcesetFilterFromCache(p)) + } else { + filteringContext.activeFilters.push(sourceset) + refreshFiltering() + removeSourcesetFilterFromCache(sourceset) + } + +} + +function addSourcesetFilterToCache(sourceset) { + let cached = localStorage.getItem('inactive-filters') + if (cached) { + let parsed = JSON.parse(cached) + localStorage.setItem('inactive-filters', JSON.stringify(parsed.concat([sourceset]))) + } else { + localStorage.setItem('inactive-filters', JSON.stringify([sourceset])) + } +} + +function removeSourcesetFilterFromCache(sourceset) { + let cached = localStorage.getItem('inactive-filters') + if (cached) { + let parsed = JSON.parse(cached) + localStorage.setItem('inactive-filters', JSON.stringify(parsed.filter(p => p != sourceset))) + } +} + +function toggleSections(target) { + localStorage.setItem('active-tab', JSON.stringify(target.getAttribute("data-togglable"))) + const activateTabs = (containerClass) => { + for(const element of document.getElementsByClassName(containerClass)){ + for(const child of element.children){ + if(child.getAttribute("data-togglable") === target.getAttribute("data-togglable")){ + child.setAttribute("data-active", "") + } else { + child.removeAttribute("data-active") + } + } + } + } + + activateTabs("tabs-section") + activateTabs("tabs-section-body") +} + +function toggleSectionsEventHandler(evt){ + if(!evt.target.getAttribute("data-togglable")) return + toggleSections(evt.target) +} + +function togglePlatformDependent(e, container) { + let target = e.target + if (target.tagName != 'BUTTON') return; + let index = target.getAttribute('data-toggle') + + for(let child of container.children){ + if(child.hasAttribute('data-toggle-list')){ + for(let bm of child.children){ + if(bm == target){ + bm.setAttribute('data-active',"") + } else if(bm != target) { + bm.removeAttribute('data-active') + } + } + } + else if(child.getAttribute('data-togglable') == index) { + child.setAttribute('data-active',"") + } + else { + child.removeAttribute('data-active') + } + } +} + +function refreshFiltering() { + let sourcesetList = filteringContext.activeFilters + document.querySelectorAll("[data-filterable-set]") + .forEach( + elem => { + let platformList = elem.getAttribute("data-filterable-set").split(' ').filter(v => -1 !== sourcesetList.indexOf(v)) + elem.setAttribute("data-filterable-current", platformList.join(' ')) + } + ) + refreshFilterButtons() + refreshPlatformTabs() +} + +function refreshPlatformTabs() { + document.querySelectorAll(".platform-hinted > .platform-bookmarks-row").forEach( + p => { + let active = false; + let firstAvailable = null + p.childNodes.forEach( + element => { + if(element.getAttribute("data-filterable-current") != ''){ + if( firstAvailable == null) { + firstAvailable = element + } + if(element.hasAttribute("data-active")) { + active = true; + } + } + } + ) + if( active == false && firstAvailable) { + firstAvailable.click() + } + } + ) +} + +function refreshFilterButtons() { + document.querySelectorAll("#filter-section > button") + .forEach(f => { + if(filteringContext.activeFilters.indexOf(f.getAttribute("data-filter")) != -1){ + f.setAttribute("data-active","") + } else { + f.removeAttribute("data-active") + } + }) +} + diff --git a/plugins/base/src/main/resources/dokka/scripts/scripts.js b/plugins/base/src/main/resources/dokka/scripts/scripts.js new file mode 100644 index 00000000..c2e29b9f --- /dev/null +++ b/plugins/base/src/main/resources/dokka/scripts/scripts.js @@ -0,0 +1,11 @@ +document.getElementById("navigationFilter").oninput = function (e) { + var input = e.target.value; + var menuParts = document.getElementsByClassName("sideMenuPart") + for (let part of menuParts) { + if(part.querySelector("a").textContent.startsWith(input)) { + part.classList.remove("filtered"); + } else { + part.classList.add("filtered"); + } + } +}
\ No newline at end of file diff --git a/plugins/base/src/main/resources/dokka/scripts/search.js b/plugins/base/src/main/resources/dokka/scripts/search.js new file mode 100644 index 00000000..04d88ab5 --- /dev/null +++ b/plugins/base/src/main/resources/dokka/scripts/search.js @@ -0,0 +1,7 @@ +let query = new URLSearchParams(window.location.search).get("query"); +document.getElementById("searchTitle").innerHTML += '"' + query + '":'; +document.getElementById("searchTable").innerHTML = pages + .filter(el => el.name.toLowerCase().startsWith(query.toLowerCase())) + .reduce((acc, element) => { + return acc + '<tr><td><a href="' + element.location + '">' + element.name + '</a></td></tr>' + }, "");
\ No newline at end of file diff --git a/plugins/base/src/main/resources/dokka/styles/jetbrains-mono.css b/plugins/base/src/main/resources/dokka/styles/jetbrains-mono.css new file mode 100644 index 00000000..2af32a92 --- /dev/null +++ b/plugins/base/src/main/resources/dokka/styles/jetbrains-mono.css @@ -0,0 +1,13 @@ +@font-face{ + font-family: 'JetBrains Mono'; + src: url('https://raw.githubusercontent.com/JetBrains/JetBrainsMono/master/web/woff2/JetBrainsMono-Regular.woff2') format('woff2'); + font-weight: normal; + font-style: normal; +} + +@font-face{ + font-family: 'JetBrains Mono'; + src: url('https://raw.githubusercontent.com/JetBrains/JetBrainsMono/master/web/woff2/JetBrainsMono-Bold.woff2') format('woff2'); + font-weight: bold; + font-style: normal; +}
\ No newline at end of file diff --git a/plugins/base/src/main/resources/dokka/styles/style.css b/plugins/base/src/main/resources/dokka/styles/style.css new file mode 100644 index 00000000..57ffb8a5 --- /dev/null +++ b/plugins/base/src/main/resources/dokka/styles/style.css @@ -0,0 +1,1048 @@ +@import url(https://fonts.googleapis.com/css?family=Open+Sans:300i,400,700); +@import url('https://rsms.me/inter/inter.css'); +@import url('jetbrains-mono.css'); + +:root { + --breadcrumb-font-color: #A6AFBA; + --hover-link-color: #5B5DEF; + --footer-height: 64px; + --footer-padding-top: 48px; + --horizontal-spacing-for-content: 42px; +} + +#content { + padding: 0 var(--horizontal-spacing-for-content); + height: calc(100% - var(--footer-height) - var(--footer-padding-top)); +} + +.breadcrumbs { + padding: 24px 0; + color: var(--breadcrumb-font-color); +} + +.breadcrumbs a { + color: var(--breadcrumb-font-color) +} + +.breadcrumbs a:hover { + color: var(--hover-link-color) +} + +.tabs-section > .section-tab:first-child { + margin-left: 0; +} + +.section-tab { + border: 0; + cursor: pointer; + background-color: transparent; + border-bottom: 1px solid #DADFE6; + padding: 11px 3px; + font-size: 14px; + color: #637282; + outline: none; + margin: 0 8px; +} + +.section-tab:hover { + color: #282E34; + border-bottom: 2px solid var(--hover-link-color); +} + +.section-tab[data-active=''] { + color: #282E34; + border-bottom: 2px solid var(--hover-link-color); +} + +.tabs-section-body { + margin: 12px 0; + background-color: white; +} + +.tabs-section-body > .table { + margin: 12px 0; +} + +.tabs-section-body .with-platform-tabs > div { + margin: 0 12px; +} + +.tabs-section-body .table .with-platform-tabs > div { + margin: 0; +} + +.tabs-section-body .with-platform-tabs { + padding-top: 12px; + padding-bottom: 12px; +} + +.tabs-section-body .with-platform-tabs .sourceset-depenent-content .table-row { + background-color: #f4f4f4; + border-bottom: 2px solid white; +} + +.cover > .platform-hinted { + padding-top: 24px; + margin-top: 24px; + padding-bottom: 16px; +} + +.cover { + display: flex; + flex-direction: column; + width: 100%; + padding-bottom: 48px; +} + +.tabbedcontent { + padding: 14px 0; +} + +.cover .platform-hinted .sourceset-depenent-content > .symbol, +.cover > .symbol { + background-color: white; +} + +.cover .platform-hinted.with-platform-tabs .sourceset-depenent-content > .symbol { + background-color: #f4f4f4; +} + +.cover .platform-hinted.with-platform-tabs .sourceset-depenent-content > .block ~ .symbol { + padding-top: 16px; + padding-left: 0; +} + +.cover .sourceset-depenent-content > .block { + padding: 16px 0; + font-size: 18px; + line-height: 28px; +} + +.cover .platform-hinted.with-platform-tabs .sourceset-depenent-content > .block { + padding: 0; + font-size: 14px; +} + +.cover ~ .divergent-group { + margin-top: 24px; + padding: 24px 8px 8px 8px; +} + +.cover ~ .divergent-group .main-subrow .symbol { + width: 100%; +} + +.divergent-group { + background-color: white; + padding: 16px 8px; + margin-bottom: 2px; +} + +.divergent-group .table-row { + background-color: #F4F4F4; + border-bottom: 2px solid white; +} + +.title > .divergent-group:first-of-type { + padding-top: 0; +} + +#container { + display: flex; + flex-direction: row; + min-height: 100%; +} + +#main { + width: 100%; + max-width: calc(100% - 280px); +} + +#leftColumn { + width: 280px; + min-height: 100%; + border-right: 1px solid #DADFE6; + flex: 0 0 auto; +} + +@media screen and (max-width: 600px) { + #container { + flex-direction: column; + } + + #leftColumn { + border-right: none; + } +} + +#sideMenu { + max-height: calc(100% - 90px); + padding-top: 16px; + position: relative; +} + +#sideMenu img { + margin: 1em 0.25em; +} + +#sideMenu hr { + background: #DADFE6; +} + +#searchBar { + float: right; +} + +#logo { + background-size: 125px 26px; + border-bottom: 1px solid #DADFE6; + background-repeat: no-repeat; + background-image: url(../images/docs_logo.svg); + background-origin: content-box; + padding-left: 24px; + padding-top: 24px; + height: 48px; +} + +.monospace, +.code { + font-family: monospace; +} + +.sample-container, .code-area { + display: flex; + flex-direction: column; +} + +code.paragraph { + display: block; +} + +.overview > .navButton { + height: 100%; + align-items: center; + display: flex; + justify-content: flex-end; + padding-right: 24px; +} + +.strikethrough { + text-decoration: line-through; +} + +.symbol:empty { + padding: 0; +} + +.symbol { + background-color: #F4F4F4; + align-items: center; + display: block; + padding: 8px 8px 8px 16px; + box-sizing: border-box; + white-space: pre-wrap; + font-weight: bold; + position: relative; + line-height: 24px; +} + +.symbol span.copy-icon path { + fill: #637282; +} + +.symbol span.copy-icon:hover path { + fill: black; +} + +.copy-popup-wrapper { + display: none; + align-items: center; + position: absolute; + z-index: 1000; + background: white; + font-weight: normal; + font-family: 'Inter', "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; + width: max-content; + font-size: 14px; + cursor: default; + border: 1px solid #D8DCE1; + box-sizing: border-box; + box-shadow: 0px 5px 10px var(--ring-popup-shadow-color); + border-radius: 3px; +} + +.copy-popup-wrapper.popup-to-left { + /* since it is in position absolute we can just move it to the left to make it always appear on the left side of the icon */ + left: -15em; +} + +.copy-popup-wrapper.active-popup { + display: flex !important; +} + +.copy-popup-wrapper:hover { + font-weight: normal; +} + +.copy-popup-wrapper svg { + padding: 8px; +} + +.copy-popup-wrapper > span:last-child { + padding-right: 14px; +} + +.symbol .top-right-position { + /* it is important for a parent to have a position: relative */ + position: absolute; + top: 8px; + right: 8px; +} + +.sideMenuPart > .overview { + height: 40px; + display: flex; + align-items: center; + position: relative; + user-select: none; /* there's a weird bug with text selection */ +} + +.sideMenuPart a { + display: flex; + align-items: center; + flex: 1; + height: 100%; + color: #637282; + overflow: hidden; +} + +.sideMenuPart > .overview:before { + box-sizing: border-box; + content: ''; + top: 0; + width: 280px; + right: 0; + bottom: 0; + position: absolute; + z-index: -1; +} + +.overview:hover:before { + background-color: #DADFE5; +} + +#nav-submenu { + padding-left: 24px; +} + +.sideMenuPart { + padding-left: 12px; + box-sizing: border-box; +} + +.sideMenuPart .hidden > .overview .navButtonContent::before { + transform: rotate(0deg); +} + +.sideMenuPart > .overview .navButtonContent::before { + content: url("../images/arrow_down.svg"); + height: 100%; + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + transform: rotate(180deg); +} + +.sideMenuPart.hidden > .navButton .navButtonContent::after { + content: '\02192'; +} + +.sideMenuPart.hidden > .sideMenuPart { + height: 0; + visibility: hidden; +} + +.filtered > a, .filtered > .navButton { + display: none; +} + +body, table { + font-family: 'Inter', "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; + background: #F4F4F4; + font-style: normal; + font-weight: normal; + font-size: 14px; + line-height: 24px; + margin: 0; + height: 100%; + /*max-width: 1440px; TODO: This results in worse experience on ultrawide, but on 16:9/16:10 looks better.*/ +} + +table { + width: 100%; + border-collapse: collapse; + background-color: #ffffff; + padding: 5px; +} + +tbody > tr { + border-bottom: 2px solid #F4F4F4; + min-height: 56px; +} + +td:first-child { + width: 20vw; +} + +.keyword { + color: black; + font-family: JetBrains Mono, Monaco, Bitstream Vera Sans Mono, Lucida Console, Terminal; + font-size: 12px; +} + +.symbol { + font-family: JetBrains Mono, Monaco, Bitstream Vera Sans Mono, Lucida Console, Terminal; + font-size: 12px; + min-height: 43px; +} + +.symbol > a { + color: var(--hover-link-color); +} + +.identifier { + color: darkblue; + font-size: 12px; + font-family: JetBrains Mono, Monaco, Bitstream Vera Sans Mono, Lucida Console, Terminal; +} + +.brief { + white-space: pre-wrap; + overflow: hidden; + padding-top: 8px; +} + +h1, h2, h3, h4, h5, h6 { + color: #222; + font-weight: bold; +} + +p, ul, ol, table, pre, dl { + margin: 0 0 20px; +} + +h1 { + font-weight: bold; + font-size: 40px; + line-height: 48px; + letter-spacing: -1px; +} + + +h1.cover { + font-size: 60px; + line-height: 64px; + letter-spacing: -1.5px; + + margin-left: calc(-1 * var(--horizontal-spacing-for-content)); + margin-right: calc(-1 * var(--horizontal-spacing-for-content)); + padding-left: var(--horizontal-spacing-for-content); + padding-right: var(--horizontal-spacing-for-content); + border-bottom: 1px solid #DADFE6; + + margin-bottom: 0; + padding-bottom: 32px; +} + +h2 { + color: #393939; + font-size: 31px; + line-height: 40px; + letter-spacing: -0.5px; +} + +h3 { + font-size: 20px; + line-height: 28px; + letter-spacing: -0.2px; +} + +h4 { + margin: 0; +} + +h3, h4, h5, h6 { + color: #494949; +} + +.UnderCoverText { + font-size: 18px; + line-height: 28px; +} + +a { + color: #5B5DEF; + font-weight: 400; + text-decoration: none; +} + +a:hover { + color: #5B5DEF; + text-decoration: underline; +} + +a small { + font-size: 11px; + color: #555; + margin-top: -0.6em; + display: block; +} + +.wrapper { + width: 860px; + margin: 0 auto; +} + +blockquote { + border-left: 1px solid #e5e5e5; + margin: 0; + padding: 0 0 0 20px; + font-style: italic; +} + +code, pre { + font-family: Monaco, Bitstream Vera Sans Mono, Lucida Console, Terminal; + color: #333; + font-size: 12px; +} + +pre { + display: block; + overflow-x: auto; +} + +th, td { + text-align: left; + vertical-align: top; + padding: 5px 10px; +} + +dt { + color: #444; + font-weight: 700; +} + +th { + color: #444; +} + +img { + max-width: 100%; +} + +header { + width: 270px; + float: left; + position: fixed; +} + +header ul { + list-style: none; + height: 40px; + + padding: 0; + + background: #eee; + background: -moz-linear-gradient(top, #f8f8f8 0%, #dddddd 100%); + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #f8f8f8), color-stop(100%, #dddddd)); + background: -webkit-linear-gradient(top, #f8f8f8 0%, #dddddd 100%); + background: -o-linear-gradient(top, #f8f8f8 0%, #dddddd 100%); + background: -ms-linear-gradient(top, #f8f8f8 0%, #dddddd 100%); + background: linear-gradient(top, #f8f8f8 0%, #dddddd 100%); + + border-radius: 5px; + border: 1px solid #d2d2d2; + box-shadow: inset #fff 0 1px 0, inset rgba(0, 0, 0, 0.03) 0 -1px 0; + width: 270px; +} + +header li { + width: 89px; + float: left; + border-right: 1px solid #d2d2d2; + height: 40px; +} + +header ul a { + line-height: 1; + font-size: 11px; + color: #999; + display: block; + text-align: center; + padding-top: 6px; + height: 40px; +} + +strong { + color: #222; + font-weight: 700; +} + +header ul li + li { + width: 88px; + border-left: 1px solid #fff; +} + +header ul li + li + li { + border-right: none; + width: 89px; +} + +header ul a strong { + font-size: 14px; + display: block; + color: #222; +} + +section { + width: 500px; + float: right; + padding-bottom: 50px; +} + +small { + font-size: 11px; +} + +hr { + border: 0; + background: #e5e5e5; + height: 1px; + margin: 0 0 20px; +} + +footer { + width: 270px; + float: left; + position: fixed; + bottom: 50px; +} + +.platform-tag { + display: flex; + flex-direction: row; + padding: 4px 8px; + height: 24px; + border-radius: 100px; + box-sizing: border-box; + border: 1px solid transparent; + margin: 2px; + font-family: Inter, Arial, sans-serif; + font-size: 12px; + font-weight: 400; + font-style: normal; + font-stretch: normal; + line-height: normal; + letter-spacing: normal; + text-align: center; + outline: none; + + color: #fff + +} + +.platform-tags { + flex: 0 0 auto; + display: flex; +} + +.platform-tags > .platform-tag { + align-self: center; +} + +.platform-tag.jvm-like { + background-color: #4DBB5F; + color: white; +} + +.platform-tag.js-like { + background-color: #FED236; + color: white; +} + +.platform-tag.native-like { + background-color: #CD74F6; + color: white; +} + +.platform-tag.common-like { + background-color: #A6AFBA; + color: white; +} + +.filter-section { + display: flex; + flex-direction: row; + align-self: flex-end; + min-height: 30px; + position: absolute; + top: 20px; + right: 88px; + z-index: 0; +} + +.platform-selector:hover { + border: 1px solid #A6AFBA !important; +} + +[data-filterable-current=''] { + display: none !important; +} + +.platform-selector:not([data-active]) { + border: 1px solid #DADFE6; + background-color: transparent; + color: #637282; +} + +td.content { + padding-left: 24px; + padding-top: 16px; + display: flex; + flex-direction: column; +} + +.main-subrow { + display: flex; + flex-direction: row; + padding: 0; + justify-content: space-between; +} + +.main-subrow > span { + display: flex; + position: relative; +} + +.main-subrow > span > a { + text-decoration: none; + font-style: normal; + font-weight: 600; + font-size: 14px; + color: #282E34; +} + +.main-subrow > span > a:hover { + color: var(--hover-link-color); +} + +.main-subrow:hover .anchor-icon { + opacity: 1; + transition: 0.2s; +} + +.main-subrow .anchor-icon { + padding: 0 8px; + opacity: 0; + transition: 0.2s 0.5s; +} + +.main-subrow .anchor-icon > svg path { + fill: #637282; +} + +.main-subrow .anchor-icon:hover { + cursor: pointer; +} + +.main-subrow .anchor-icon:hover > svg path { + fill: var(--hover-link-color); +} + +.main-subrow .anchor-wrapper { + position: relative; +} + +.platform-hinted { + flex: auto; + display: block; + margin-bottom: 5px; +} + +.platform-hinted > .platform-bookmarks-row > .platform-bookmark { + min-width: 64px; + height: 36px; + border: 2px solid white; + background: white; + outline: none; + flex: none; + order: 5; + align-self: flex-start; + margin: 0; +} + +.platform-hinted > .platform-bookmarks-row > .platform-bookmark.jvm-like:hover { + border-top: 2px solid rgba(77, 187, 95, 0.3); +} + +.platform-hinted > .platform-bookmarks-row > .platform-bookmark.js-like:hover { + border-top: 2px solid rgba(254, 175, 54, 0.3); +} + +.platform-hinted > .platform-bookmarks-row > .platform-bookmark.native-like:hover { + border-top: 2px solid rgba(105, 118, 249, 0.3); +} + +.platform-hinted > .platform-bookmarks-row > .platform-bookmark.common-like:hover { + border-top: 2px solid rgba(161, 170, 180, 0.3); +} + +.platform-hinted > .platform-bookmarks-row > .platform-bookmark.jvm-like[data-active=''] { + border: 2px solid #F4F4F4; + border-top: 2px solid #4DBB5F; + + background: #F4F4F4; +} + +.platform-hinted > .platform-bookmarks-row > .platform-bookmark.js-like[data-active=''] { + border: 2px solid #F4F4F4; + border-top: 2px solid #FED236; + + background: #F4F4F4; +} + +.platform-hinted > .platform-bookmarks-row > .platform-bookmark.native-like[data-active=''] { + border: 2px solid #F4F4F4; + border-top: 2px solid #CD74F6; + + background: #F4F4F4; +} + +.platform-hinted > .platform-bookmarks-row > .platform-bookmark.common-like[data-active=''] { + border: 2px solid #F4F4F4; + border-top: 2px solid #A6AFBA; + + background: #F4F4F4; +} + +.platform-hinted > .content:not([data-active]), +.tabs-section-body > *:not([data-active]) { + visibility: hidden; + height: 0; + position: fixed; + top: 0; +} + +.inner-brief-with-platform-tags { + display: block; + width: 100% +} + +.brief-with-platform-tags { + display: flex; +} + +.brief-with-platform-tags ~ .main-subrow { + padding-top: 16px; +} + +.cover .with-platform-tabs { + background-color: white; +} + +.cover > .with-platform-tabs .platform-bookmarks-row { + margin: 0 16px; +} + +.cover > .with-platform-tabs > .content { + margin: 0 16px; + background-color: #f4f4f4; + padding: 8px 16px; +} + +.cover > .block { + padding-top: 48px; + padding-bottom: 24px; + font-size: 18px; + line-height: 28px; +} + +.cover > .block:empty { + padding-bottom: 0; +} + +.table-row .with-platform-tabs .sourceset-depenent-content .brief { + padding: 16px; + background-color: #f4f4f4; +} + +.sideMenuPart[data-active] > .overview:before { + border-left: 4px solid var(--hover-link-color); + background: rgba(91, 93, 239, 0.15); +} + +.table { + display: flex; + flex-direction: column; +} + +.table-row { + display: flex; + flex-direction: column; + background: white; + border-bottom: 2px solid #f4f4f4; + padding: 16px 24px 16px 24px; +} + +.platform-dependent-row { + display: grid; + padding-top: 8px; +} + +.title-row { + display: grid; + grid-template-columns: auto auto 7em; + width: 100%; +} + +.keyValue { + display: grid; +} + +@media print, screen and (min-width: 960px) { + .keyValue { + grid-template-columns: 20% 80%; + } + + .title-row { + grid-template-columns: 20% auto 7em; + } +} + +@media print, screen and (max-width: 960px) { + + div.wrapper { + width: auto; + margin: 0; + } + + header, section, footer { + float: none; + position: static; + width: auto; + } + + header { + padding-right: 320px; + } + + section { + border: 1px solid #e5e5e5; + border-width: 1px 0; + padding: 20px 0; + margin: 0 0 20px; + } + + header a small { + display: inline; + } + + header ul { + position: absolute; + right: 50px; + top: 52px; + } +} + +@media print, screen and (max-width: 720px) { + body { + word-wrap: break-word; + } + + header { + padding: 0; + } + + header ul, header p.view { + position: static; + } + + pre, code { + word-wrap: normal; + } +} + +@media print, screen and (max-width: 480px) { + body { + padding-right: 15px; + } + + header ul { + display: none; + } +} + +@media print { + body { + padding: 0.4in; + font-size: 12pt; + color: #444; + } +} + +.footer { + clear: both; + display: flex; + align-items: center; + position: relative; + height: var(--footer-height); + border-top: 1px solid #DADFE6; + font-size: 12px; + line-height: 16px; + letter-spacing: 0.2px; + color: var(--breadcrumb-font-color); + margin-top: var(--footer-padding-top); +} + +.footer span.go-to-top-icon { + border-radius: 2em; + padding: 11px 10px !important; + background-color: white; +} + +.footer span.go-to-top-icon path { + fill: #637282; +} + +.footer > span:first-child { + margin-left: var(--horizontal-spacing-for-content); + padding-left: 0; +} + +.footer > span:last-child { + margin-right: var(--horizontal-spacing-for-content); + padding-right: 0; +} + +.footer > span { + padding: 0 16px; +} + +.footer .padded-icon { + padding-left: 0.5em; +} + +/*For svg*/ +.footer path { + fill: var(--breadcrumb-font-color); +} + +.pull-right { + float: right; + margin-left: auto +} + +div.runnablesample { + height: fit-content; +}
\ No newline at end of file diff --git a/plugins/base/src/test/kotlin/basic/DRITest.kt b/plugins/base/src/test/kotlin/basic/DRITest.kt new file mode 100644 index 00000000..559a2dbf --- /dev/null +++ b/plugins/base/src/test/kotlin/basic/DRITest.kt @@ -0,0 +1,317 @@ +package basic + +import org.jetbrains.dokka.links.* +import org.jetbrains.dokka.links.Callable +import org.jetbrains.dokka.links.Nullable +import org.jetbrains.dokka.links.TypeConstructor +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.pages.ClasslikePageNode +import org.jetbrains.dokka.pages.ContentPage +import org.jetbrains.dokka.pages.MemberPageNode +import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test + +class DRITest : AbstractCoreTest() { + @Test + fun issue634() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/") + } + } + } + + testInline( + """ + |/src/main/kotlin/basic/Test.kt + |package toplevel + | + |inline fun <T, R : Comparable<R>> Array<out T>.mySortBy( + | crossinline selector: (T) -> R?): Array<out T> = TODO() + |} + """.trimMargin(), + configuration + ) { + documentablesMergingStage = { module -> + val expected = TypeConstructor( + "kotlin.Function1", listOf( + TypeParam(listOf(Nullable(TypeConstructor("kotlin.Any", emptyList())))), + Nullable(TypeParam(listOf(TypeConstructor("kotlin.Comparable", listOf(SelfType))))) + ) + ) + val actual = module.packages.single() + .functions.single() + .dri.callable?.params?.single() + assertEquals(expected, actual) + } + } + } + + @Test + fun issue634WithImmediateNullableSelf() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/") + } + } + } + + testInline( + """ + |/src/main/kotlin/basic/Test.kt + |package toplevel + | + |fun <T : Comparable<T>> Array<T>.doSomething(t: T?): Array<T> = TODO() + |} + """.trimMargin(), + configuration + ) { + documentablesMergingStage = { module -> + val expected = Nullable(TypeParam(listOf(TypeConstructor("kotlin.Comparable", listOf(SelfType))))) + val actual = module.packages.single() + .functions.single() + .dri.callable?.params?.single() + assertEquals(expected, actual) + } + } + } + + @Test + fun issue634WithGenericNullableReceiver() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/") + } + } + } + + testInline( + """ + |/src/main/kotlin/basic/Test.kt + |package toplevel + | + |fun <T : Comparable<T>> T?.doSomethingWithNullable() = TODO() + |} + """.trimMargin(), + configuration + ) { + documentablesMergingStage = { module -> + val expected = Nullable(TypeParam(listOf(TypeConstructor("kotlin.Comparable", listOf(SelfType))))) + val actual = module.packages.single() + .functions.single() + .dri.callable?.receiver + assertEquals(expected, actual) + } + } + } + + @Test + fun issue642WithStarAndAny() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + analysisPlatform = "js" + sourceRoots = listOf("src/") + } + } + } + + testInline( + """ + |/src/main/kotlin/Test.kt + | + |open class Bar<Z> + |class ReBarBar : Bar<StringBuilder>() + |class Foo<out T : Comparable<*>, R : List<Bar<*>>> + | + |fun <T : Comparable<Any?>> Foo<T, *>.qux(): String = TODO() + |fun <T : Comparable<*>> Foo<T, *>.qux(): String = TODO() + | + """.trimMargin(), + configuration + ) { + pagesGenerationStage = { module -> + // DRI(//qux/Foo[TypeParam(bounds=[kotlin.Comparable[kotlin.Any?]]),*]#/PointingToFunctionOrClasslike/) + val expectedDRI = DRI( + "", + null, + Callable( + "qux", TypeConstructor( + "Foo", listOf( + TypeParam( + listOf( + TypeConstructor( + "kotlin.Comparable", listOf( + Nullable(TypeConstructor("kotlin.Any", emptyList())) + ) + ) + ) + ), + StarProjection + ) + ), + emptyList() + ) + ) + + val driCount = module + .withDescendants() + .filterIsInstance<ContentPage>() + .sumBy { it.dri.count { dri -> dri == expectedDRI } } + + assertEquals(1, driCount) + } + } + } + + @Test + fun driForGenericClass(){ + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/") + } + } + } + testInline( + """ + |/src/main/kotlin/Test.kt + |package example + | + |class Sample<S>(first: S){ } + | + | + """.trimMargin(), + configuration + ) { + pagesGenerationStage = { module -> + val sampleClass = module.dfs { it.name == "Sample" } as ClasslikePageNode + val classDocumentable = sampleClass.documentable as DClass + + assertEquals( "example/Sample///PointingToDeclaration/", sampleClass.dri.first().toString()) + assertEquals("example/Sample///PointingToGenericParameters(0)/", classDocumentable.generics.first().dri.toString()) + } + } + } + + @Test + fun driForGenericFunction(){ + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/") + classpath = listOfNotNull(jvmStdlibPath) + } + } + } + testInline( + """ + |/src/main/kotlin/Test.kt + |package example + | + |class Sample<S>(first: S){ + | fun <T> genericFun(param1: String): Tuple<S,T> = TODO() + |} + | + | + """.trimMargin(), + configuration + ) { + pagesGenerationStage = { module -> + val sampleClass = module.dfs { it.name == "Sample" } as ClasslikePageNode + val functionNode = sampleClass.children.first { it.name == "genericFun" } as MemberPageNode + val functionDocumentable = functionNode.documentable as DFunction + val parameter = functionDocumentable.parameters.first() + + assertEquals("example/Sample/genericFun/#kotlin.String/PointingToDeclaration/", functionNode.dri.first().toString()) + + assertEquals(1, functionDocumentable.parameters.size) + assertEquals("example/Sample/genericFun/#kotlin.String/PointingToCallableParameters(0)/", parameter.dri.toString()) + //1 since from the function's perspective there is only 1 new generic declared + //The other one is 'inherited' from class + assertEquals( 1, functionDocumentable.generics.size) + assertEquals( "T", functionDocumentable.generics.first().name) + assertEquals( "example/Sample/genericFun/#kotlin.String/PointingToGenericParameters(0)/", functionDocumentable.generics.first().dri.toString()) + } + } + } + + @Test + fun driForFunctionNestedInsideInnerClass() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/") + classpath = listOfNotNull(jvmStdlibPath) + } + } + } + testInline( + """ + |/src/main/kotlin/Test.kt + |package example + | + |class Sample<S>(first: S){ + | inner class SampleInner { + | fun foo(): S = TODO() + | } + |} + | + | + """.trimMargin(), + configuration + ) { + pagesGenerationStage = { module -> + val sampleClass = module.dfs { it.name == "Sample" } as ClasslikePageNode + val sampleInner = sampleClass.children.first { it.name == "SampleInner" } as ClasslikePageNode + val foo = sampleInner.children.first { it.name == "foo" } as MemberPageNode + val documentable = foo.documentable as DFunction + + assertEquals(sampleClass.dri.first().toString(), (documentable.type as OtherParameter).declarationDRI.toString()) + assertEquals(0, documentable.generics.size) + } + } + } + + @Test + fun driForGenericExtensionFunction(){ + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/") + } + } + } + testInline( + """ + |/src/main/kotlin/Test.kt + |package example + | + | fun <T> List<T>.extensionFunction(): String = "" + | + """.trimMargin(), + configuration + ) { + pagesGenerationStage = { module -> + val extensionFunction = module.dfs { it.name == "extensionFunction" } as MemberPageNode + val documentable = extensionFunction.documentable as DFunction + + assertEquals( + "example//extensionFunction/kotlin.collections.List[TypeParam(bounds=[kotlin.Any?])]#/PointingToDeclaration/", + extensionFunction.dri.first().toString() + ) + assertEquals(1, documentable.generics.size) + assertEquals("T", documentable.generics.first().name) + assertEquals( + "example//extensionFunction/kotlin.collections.List[TypeParam(bounds=[kotlin.Any?])]#/PointingToGenericParameters(0)/", + documentable.generics.first().dri.toString() + ) + + } + } + } +} diff --git a/plugins/base/src/test/kotlin/basic/DokkaBasicTests.kt b/plugins/base/src/test/kotlin/basic/DokkaBasicTests.kt new file mode 100644 index 00000000..bceb79ae --- /dev/null +++ b/plugins/base/src/test/kotlin/basic/DokkaBasicTests.kt @@ -0,0 +1,42 @@ +package basic + +import org.jetbrains.dokka.pages.ClasslikePageNode +import org.jetbrains.dokka.pages.ModulePageNode +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test +import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest + +class DokkaBasicTests : AbstractCoreTest() { + + @Test + fun basic1() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin/basic/Test.kt") + } + } + } + + testInline( + """ + |/src/main/kotlin/basic/Test.kt + |package basic + | + |class Test { + | val tI = 1 + | fun tF() = 2 + |} + """.trimMargin(), + configuration + ) { + pagesGenerationStage = { + val root = it as ModulePageNode + assertTrue(root.getClasslikeToMemberMap().filterKeys { it.name == "Test" }.entries.firstOrNull()?.value?.size == 2) + } + } + } + + private fun ModulePageNode.getClasslikeToMemberMap() = + this.parentMap.filterValues { it is ClasslikePageNode }.entries.groupBy({ it.value }) { it.key } +} diff --git a/plugins/base/src/test/kotlin/basic/FailOnWarningTest.kt b/plugins/base/src/test/kotlin/basic/FailOnWarningTest.kt new file mode 100644 index 00000000..9d2c5825 --- /dev/null +++ b/plugins/base/src/test/kotlin/basic/FailOnWarningTest.kt @@ -0,0 +1,118 @@ +package basic + +import org.jetbrains.dokka.DokkaException +import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest +import org.jetbrains.dokka.utilities.DokkaConsoleLogger +import org.jetbrains.dokka.utilities.DokkaLogger +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import testApi.logger.TestLogger + +class FailOnWarningTest : AbstractCoreTest() { + + @Test + fun `throws exception if one or more warnings were emitted`() { + val configuration = dokkaConfiguration { + failOnWarning = true + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin") + } + } + } + + assertThrows<DokkaException> { + testInline( + """ + |/src/main/kotlin + |package sample + """.trimIndent(), configuration + ) { + pluginsSetupStage = { + logger.warn("Warning!") + } + } + } + } + + @Test + fun `throws exception if one or more error were emitted`() { + val configuration = dokkaConfiguration { + failOnWarning = true + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin") + } + } + } + + assertThrows<DokkaException> { + testInline( + """ + |/src/main/kotlin + |package sample + """.trimIndent(), configuration + ) { + pluginsSetupStage = { + logger.error("Error!") + } + } + } + } + + @Test + fun `does not throw if now warning or error was emitted`() { + logger = TestLogger(ZeroErrorOrWarningCountDokkaLogger()) + + val configuration = dokkaConfiguration { + failOnWarning = true + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin") + } + } + } + + + testInline( + """ + |/src/main/kotlin + |package sample + """.trimIndent(), configuration + ) { + /* We expect no Exception */ + } + } + + @Test + fun `does not throw if disabled`() { + val configuration = dokkaConfiguration { + failOnWarning = false + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin") + } + } + } + + + testInline( + """ + |/src/main/kotlin + |package sample + """.trimIndent(), configuration + ) { + pluginsSetupStage = { + logger.warn("Error!") + logger.error("Error!") + } + } + } +} + +private class ZeroErrorOrWarningCountDokkaLogger( + logger: DokkaLogger = DokkaConsoleLogger +) : DokkaLogger by logger { + override var warningsCount: Int = 0 + override var errorsCount: Int = 0 +} diff --git a/plugins/base/src/test/kotlin/content/annotations/ContentForAnnotationsTest.kt b/plugins/base/src/test/kotlin/content/annotations/ContentForAnnotationsTest.kt new file mode 100644 index 00000000..bf78b847 --- /dev/null +++ b/plugins/base/src/test/kotlin/content/annotations/ContentForAnnotationsTest.kt @@ -0,0 +1,221 @@ +package content.annotations + +import matchers.content.* +import org.jetbrains.dokka.pages.ContentPage +import org.jetbrains.dokka.pages.PackagePageNode +import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest +import org.junit.jupiter.api.Test +import utils.ParamAttributes +import utils.bareSignature +import utils.propertySignature + + +class ContentForAnnotationsTest : AbstractCoreTest() { + + + private val testConfiguration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/") + analysisPlatform = "jvm" + } + } + } + + @Test + fun `function with documented annotation`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | + |@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, + | AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.EXPRESSION, AnnotationTarget.CONSTRUCTOR, AnnotationTarget.FIELD + |) + |@Retention(AnnotationRetention.SOURCE) + |@MustBeDocumented + |annotation class Fancy + | + | + |@Fancy + |fun function(@Fancy abc: String): String { + | return "Hello, " + abc + |} + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } + .children.single { it.name == "function" } as ContentPage + page.content.assertNode { + group { + header(1) { +"function" } + } + divergentGroup { + divergentInstance { + divergent { + bareSignature( + mapOf("Fancy" to emptySet()), + "", + "", + emptySet(), + "function", + "String", + "abc" to ParamAttributes(mapOf("Fancy" to emptySet()), emptySet(), "String") + ) + } + } + } + + } + } + } + } + + @Test + fun `function with undocumented annotation`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | + |@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, + | AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.EXPRESSION, AnnotationTarget.CONSTRUCTOR, AnnotationTarget.FIELD + |) + |@Retention(AnnotationRetention.SOURCE) + |annotation class Fancy + | + |@Fancy + |fun function(@Fancy abc: String): String { + | return "Hello, " + abc + |} + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } + .children.single { it.name == "function" } as ContentPage + page.content.assertNode { + group { + header(1) { +"function" } + } + divergentGroup { + divergentInstance { + divergent { + bareSignature( + emptyMap(), + "", + "", + emptySet(), + "function", + "String", + "abc" to ParamAttributes(emptyMap(), emptySet(), "String") + ) + } + } + } + + } + } + } + } + + @Test + fun `property with undocumented annotation`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | + |@Suppress + |val property: Int = 6 + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } as PackagePageNode + page.content.assertNode { + propertySignature(emptyMap(), "", "", emptySet(), "val", "property", "Int") + } + } + } + } + + @Test + fun `property with documented annotation`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | + |@MustBeDocumented + |annotation class Fancy + | + |@Fancy + |val property: Int = 6 + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } as PackagePageNode + page.content.assertNode { + propertySignature(mapOf("Fancy" to emptySet()), "", "", emptySet(), "val", "property", "Int") + } + } + } + } + + + @Test + fun `rich documented annotation`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | + |@MustBeDocumented + |@Retention(AnnotationRetention.SOURCE) + |@Target(AnnotationTarget.FIELD) + |annotation class BugReport( + | val assignedTo: String = "[none]", + | val testCase: KClass<ABC> = ABC::class, + | val status: Status = Status.UNCONFIRMED, + | val ref: Reference = Reference(value = 1), + | val reportedBy: Array<Reference>, + | val showStopper: Boolean = false + |) { + | enum class Status { + | UNCONFIRMED, CONFIRMED, FIXED, NOTABUG + | } + | class ABC + |} + |annotation class Reference(val value: Int) + | + | + |@BugReport( + | assignedTo = "me", + | testCase = BugReport.ABC::class, + | status = BugReport.Status.FIXED, + | ref = Reference(value = 2), + | reportedBy = [Reference(value = 2), Reference(value = 4)], + | showStopper = true + |) + |val ltint: Int = 5 + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } as PackagePageNode + page.content.assertNode { + propertySignature( + mapOf( + "BugReport" to setOf( + "assignedTo", + "testCase", + "status", + "ref", + "reportedBy", + "showStopper" + ) + ), "", "", emptySet(), "val", "ltint", "Int" + ) + } + } + } + } +} diff --git a/plugins/base/src/test/kotlin/content/annotations/DepredatedAndSinceKotlinTest.kt b/plugins/base/src/test/kotlin/content/annotations/DepredatedAndSinceKotlinTest.kt new file mode 100644 index 00000000..69de1bcd --- /dev/null +++ b/plugins/base/src/test/kotlin/content/annotations/DepredatedAndSinceKotlinTest.kt @@ -0,0 +1,103 @@ +package content.annotations + + +import matchers.content.* +import org.jetbrains.dokka.pages.ContentPage +import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest +import org.junit.jupiter.api.Test +import utils.ParamAttributes +import utils.bareSignature + + +class DepredatedAndSinceKotlinTest : AbstractCoreTest() { + + private val testConfiguration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/") + analysisPlatform = "jvm" + } + } + } + + @Test + fun `function with deprecated annotation`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | + |@Deprecated("And some things that should not have been forgotten were lost. History became legend. Legend became myth.") + |fun ring(abc: String): String { + | return "My precious " + abc + |} + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } + .children.single { it.name == "ring" } as ContentPage + page.content.assertNode { + group { + header(1) { +"ring" } + } + divergentGroup { + divergentInstance { + divergent { + bareSignature( + emptyMap(), + "", + "", + emptySet(), + "ring", + "String", + "abc" to ParamAttributes(emptyMap(), emptySet(), "String") + ) + } + } + } + } + } + } + } + + @Test + fun `function with since kotlin annotation`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | + |@SinceKotlin("1.3") + |fun ring(abc: String): String { + | return "My precious " + abc + |} + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } + .children.single { it.name == "ring" } as ContentPage + page.content.assertNode { + group { + header(1) { +"ring" } + } + divergentGroup { + divergentInstance { + divergent { + bareSignature( + emptyMap(), + "", + "", + emptySet(), + "ring", + "String", + "abc" to ParamAttributes(emptyMap(), emptySet(), "String") + ) + } + } + } + + } + } + } + } +} diff --git a/plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt b/plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt new file mode 100644 index 00000000..a9689bc5 --- /dev/null +++ b/plugins/base/src/test/kotlin/content/params/ContentForParamsTest.kt @@ -0,0 +1,611 @@ +package content.params + +import matchers.content.* +import org.jetbrains.dokka.Platform +import org.jetbrains.dokka.model.DFunction +import org.jetbrains.dokka.model.dfs +import org.jetbrains.dokka.model.doc.DocumentationNode +import org.jetbrains.dokka.model.doc.Param +import org.jetbrains.dokka.model.doc.Text +import org.jetbrains.dokka.pages.ContentPage +import org.jetbrains.dokka.pages.MemberPageNode +import org.jetbrains.dokka.pages.dfs +import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest +import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull +import org.junit.jupiter.api.Test +import utils.* + +class ContentForParamsTest : AbstractCoreTest() { + private val testConfiguration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/") + analysisPlatform = "jvm" + } + } + } + + @Test + fun `undocumented function`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | + |fun function(abc: String) { + | println(abc) + |} + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } + .children.single { it.name == "function" } as ContentPage + page.content.assertNode { + group { + header(1) { +"function" } + } + divergentGroup { + divergentInstance { + divergent { + bareSignature( + emptyMap(), "", "", emptySet(), "function", null, "abc" to ParamAttributes( + emptyMap(), + emptySet(), + "String" + ) + ) + } + } + } + } + } + } + } + + @Test + fun `undocumented parameter`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | /** + | * comment to function + | */ + |fun function(abc: String) { + | println(abc) + |} + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } + .children.single { it.name == "function" } as ContentPage + page.content.assertNode { + group { + header(1) { +"function" } + } + divergentGroup { + divergentInstance { + before { + pWrapped("comment to function") + } + divergent { + bareSignature( + emptyMap(), + "", + "", + emptySet(), + "function", + null, + "abc" to ParamAttributes(emptyMap(), emptySet(), "String") + ) + } + } + } + } + } + } + } + + @Test + fun `undocumented parameter and other tags without function comment`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | /** + | * @author Kordyjan + | * @since 0.11 + | */ + |fun function(abc: String) { + | println(abc) + |} + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } + .children.single { it.name == "function" } as ContentPage + page.content.assertNode { + group { + header(1) { +"function" } + } + divergentGroup { + divergentInstance { + before { + unnamedTag("Author") { +"Kordyjan" } + unnamedTag("Since") { +"0.11" } + } + divergent { + bareSignature( + emptyMap(), + "", + "", + emptySet(), + "function", + null, + "abc" to ParamAttributes(emptyMap(), emptySet(), "String") + ) + } + } + } + } + } + } + } + + @Test + fun `undocumented parameter and other tags`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | /** + | * comment to function + | * @author Kordyjan + | * @since 0.11 + | */ + |fun function(abc: String) { + | println(abc) + |} + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } + .children.single { it.name == "function" } as ContentPage + page.content.assertNode { + group { + header(1) { +"function" } + } + divergentGroup { + divergentInstance { + before { + pWrapped("comment to function") + unnamedTag("Author") { +"Kordyjan" } + unnamedTag("Since") { +"0.11" } + } + divergent { + bareSignature( + emptyMap(), + "", + "", + emptySet(), + "function", + null, + "abc" to ParamAttributes(emptyMap(), emptySet(), "String") + ) + } + } + } + } + } + } + } + + @Test + fun `single parameter`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | /** + | * comment to function + | * @param abc comment to param + | */ + |fun function(abc: String) { + | println(abc) + |} + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } + .children.single { it.name == "function" } as ContentPage + page.content.assertNode { + group { + header(1) { +"function" } + } + divergentGroup { + divergentInstance { + before { + pWrapped("comment to function") + header(2) { +"Parameters" } + group { + platformHinted { + table { + group { + +"abc" + group { +"comment to param" } + } + } + } + } + } + divergent { + bareSignature( + emptyMap(), + "", + "", + emptySet(), + "function", + null, + "abc" to ParamAttributes(emptyMap(), emptySet(), "String") + ) + } + } + } + } + } + } + } + + @Test + fun `multiple parameters`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | /** + | * comment to function + | * @param first comment to first param + | * @param second comment to second param + | * @param[third] comment to third param + | */ + |fun function(first: String, second: Int, third: Double) { + | println(abc) + |} + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } + .children.single { it.name == "function" } as ContentPage + page.content.assertNode { + group { + header(1) { +"function" } + } + divergentGroup { + divergentInstance { + before { + pWrapped("comment to function") + header(2) { +"Parameters" } + group { + platformHinted { + table { + group { + +"first" + group { +"comment to first param" } + } + group { + +"second" + group { +"comment to second param" } + } + group { + +"third" + group { +"comment to third param" } + } + } + } + } + } + divergent { + bareSignature( + emptyMap(), "", "", emptySet(), "function", null, + "first" to ParamAttributes(emptyMap(), emptySet(), "String"), + "second" to ParamAttributes(emptyMap(), emptySet(), "Int"), + "third" to ParamAttributes(emptyMap(), emptySet(), "Double") + ) + } + } + } + } + } + } + } + + @Test + fun `multiple parameters without function description`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | /** + | * @param first comment to first param + | * @param second comment to second param + | * @param[third] comment to third param + | */ + |fun function(first: String, second: Int, third: Double) { + | println(abc) + |} + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } + .children.single { it.name == "function" } as ContentPage + page.content.assertNode { + group { + header(1) { +"function" } + } + divergentGroup { + divergentInstance { + before { + header(2) { +"Parameters" } + group { + platformHinted { + table { + group { + +"first" + group { +"comment to first param" } + } + group { + +"second" + group { +"comment to second param" } + } + group { + +"third" + group { +"comment to third param" } + } + } + } + } + } + divergent { + bareSignature( + emptyMap(), "", "", emptySet(), "function", null, + "first" to ParamAttributes(emptyMap(), emptySet(), "String"), + "second" to ParamAttributes(emptyMap(), emptySet(), "Int"), + "third" to ParamAttributes(emptyMap(), emptySet(), "Double") + ) + } + } + } + } + } + } + } + + @Test + fun `function with receiver`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | /** + | * comment to function + | * @param abc comment to param + | * @receiver comment to receiver + | */ + |fun String.function(abc: String) { + | println(abc) + |} + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } + .children.single { it.name == "function" } as ContentPage + page.content.assertNode { + group { + header(1) { +"function" } + } + divergentGroup { + divergentInstance { + before { + pWrapped("comment to function") + header(2) { +"Parameters" } + group { + platformHinted { + table { + group { + +"<receiver>" + group { +"comment to receiver" } + } + group { + +"abc" + group { +"comment to param" } + } + } + } + } + } + divergent { + bareSignatureWithReceiver( + emptyMap(), + "", + "", + emptySet(), + "String", + "function", + null, + "abc" to ParamAttributes(emptyMap(), emptySet(), "String") + ) + } + } + } + } + } + } + } + + @Test + fun `missing parameter documentation`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | /** + | * comment to function + | * @param first comment to first param + | * @param[third] comment to third param + | */ + |fun function(first: String, second: Int, third: Double) { + | println(abc) + |} + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } + .children.single { it.name == "function" } as ContentPage + page.content.assertNode { + group { + header(1) { +"function" } + } + divergentGroup { + divergentInstance { + before { + pWrapped("comment to function") + header(2) { +"Parameters" } + group { + platformHinted { + table { + group { + +"first" + group { +"comment to first param" } + } + group { + +"third" + group { +"comment to third param" } + } + } + } + } + } + divergent { + bareSignature( + emptyMap(), "", "", emptySet(), "function", null, + "first" to ParamAttributes(emptyMap(), emptySet(), "String"), + "second" to ParamAttributes(emptyMap(), emptySet(), "Int"), + "third" to ParamAttributes(emptyMap(), emptySet(), "Double") + ) + } + } + } + } + } + } + } + + @Test + fun `parameters mixed with other tags`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | /** + | * comment to function + | * @param first comment to first param + | * @author Kordyjan + | * @param second comment to second param + | * @since 0.11 + | * @param[third] comment to third param + | */ + |fun function(first: String, second: Int, third: Double) { + | println(abc) + |} + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } + .children.single { it.name == "function" } as ContentPage + page.content.assertNode { + group { + header(1) { +"function" } + } + divergentGroup { + divergentInstance { + before { + pWrapped("comment to function") + unnamedTag("Author") { +"Kordyjan" } + unnamedTag("Since") { +"0.11" } + header(2) { +"Parameters" } + + group { + platformHinted { + table { + group { + +"first" + group { +"comment to first param" } + } + group { + +"second" + group { +"comment to second param" } + } + group { + +"third" + group { +"comment to third param" } + } + } + } + } + } + divergent { + bareSignature( + emptyMap(), "", "", emptySet(), "function", null, + "first" to ParamAttributes(emptyMap(), emptySet(), "String"), + "second" to ParamAttributes(emptyMap(), emptySet(), "Int"), + "third" to ParamAttributes(emptyMap(), emptySet(), "Double") + ) + } + } + } + } + } + } + } + + @Test + fun javaDocCommentWithDocumentedParameters() { + testInline( + """ + |/src/main/java/test/Main.java + |package test + | public class Main { + | + | /** + | * comment to function + | * @param first comment to first param + | * @param second comment to second param + | */ + | public void sample(String first, String second) { + | + | } + | } + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val sampleFunction = module.dfs { + it is MemberPageNode && it.dri.first() + .toString() == "test/Main/sample/#java.lang.String#java.lang.String/PointingToDeclaration/" + } as MemberPageNode + val forJvm = (sampleFunction.documentable as DFunction).parameters.mapNotNull { + val jvm = it.documentation.keys.first { it.analysisPlatform == Platform.jvm } + it.documentation[jvm] + } + + assert(forJvm.size == 2) + val (first, second) = forJvm.map { it.paramsDescription() } + assert(first == "comment to first param") + assert(second == "comment to second param") + } + } + } + + private fun DocumentationNode.paramsDescription(): String = + children.firstIsInstanceOrNull<Param>()?.root?.children?.firstIsInstanceOrNull<Text>()?.body.orEmpty() + +} diff --git a/plugins/base/src/test/kotlin/content/seealso/ContentForSeeAlsoTest.kt b/plugins/base/src/test/kotlin/content/seealso/ContentForSeeAlsoTest.kt new file mode 100644 index 00000000..24970660 --- /dev/null +++ b/plugins/base/src/test/kotlin/content/seealso/ContentForSeeAlsoTest.kt @@ -0,0 +1,459 @@ +package content.seealso + +import matchers.content.* +import org.jetbrains.dokka.pages.ContentPage +import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest +import org.junit.jupiter.api.Test +import utils.ParamAttributes +import utils.bareSignature +import utils.pWrapped +import utils.unnamedTag + +class ContentForSeeAlsoTest : AbstractCoreTest() { + private val testConfiguration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/") + analysisPlatform = "jvm" + } + } + } + + @Test + fun `undocumented function`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | + |fun function(abc: String) { + | println(abc) + |} + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } + .children.single { it.name == "function" } as ContentPage + page.content.assertNode { + group { + header(1) { +"function" } + } + divergentGroup { + divergentInstance { + divergent { + bareSignature( + emptyMap(), + "", + "", + emptySet(), + "function", + null, + "abc" to ParamAttributes(emptyMap(), emptySet(), "String") + ) + } + } + } + } + } + } + } + + @Test + fun `undocumented seealso`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | /** + | * @see abc + | */ + |fun function(abc: String) { + | println(abc) + |} + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } + .children.single { it.name == "function" } as ContentPage + page.content.assertNode { + group { + header(1) { +"function" } + } + divergentGroup { + divergentInstance { + before { + header(2) { +"See also" } + group { + platformHinted { + table { + group { + //DRI should be "test//abc/#/-1/" + link { +"abc" } + group { } + } + } + } + } + } + divergent { + bareSignature( + emptyMap(), + "", + "", + emptySet(), + "function", + null, + "abc" to ParamAttributes(emptyMap(), emptySet(), "String") + ) + } + } + } + } + } + } + } + + @Test + fun `documented seealso`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | /** + | * @see abc Comment to abc + | */ + |fun function(abc: String) { + | println(abc) + |} + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } + .children.single { it.name == "function" } as ContentPage + page.content.assertNode { + group { + header(1) { +"function" } + } + divergentGroup { + divergentInstance { + before { + header(2) { +"See also" } + group { + platformHinted { + table { + group { + //DRI should be "test//abc/#/-1/" + link { +"abc" } + group { +"Comment to abc" } + } + } + } + } + } + divergent { + bareSignature( + emptyMap(), + "", + "", + emptySet(), + "function", + null, + "abc" to ParamAttributes(emptyMap(), emptySet(), "String") + ) + } + } + } + } + } + } + } + + @Test + fun `undocumented seealso with stdlib link`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | /** + | * @see Collection + | */ + |fun function(abc: String) { + | println(abc) + |} + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } + .children.single { it.name == "function" } as ContentPage + page.content.assertNode { + group { + header(1) { +"function" } + } + divergentGroup { + divergentInstance { + before { + header(2) { +"See also" } + group { + platformHinted { + table { + group { + //DRI should be "kotlin.collections/Collection////" + link { +"Collection" } + group { } + } + } + } + } + } + divergent { + bareSignature( + emptyMap(), + "", + "", + emptySet(), + "function", + null, + "abc" to ParamAttributes(emptyMap(), emptySet(), "String") + ) + } + } + } + } + } + } + } + + @Test + fun `documented seealso with stdlib link`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | /** + | * @see Collection Comment to stdliblink + | */ + |fun function(abc: String) { + | println(abc) + |} + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } + .children.single { it.name == "function" } as ContentPage + page.content.assertNode { + group { + header(1) { +"function" } + } + divergentGroup { + divergentInstance { + before { + header(2) { +"See also" } + group { + platformHinted { + table { + group { + //DRI should be "test//abc/#/-1/" + link { +"Collection" } + group { +"Comment to stdliblink" } + } + } + } + } + } + divergent { + bareSignature( + emptyMap(), + "", + "", + emptySet(), + "function", + null, + "abc" to ParamAttributes(emptyMap(), emptySet(), "String") + ) + } + } + } + } + } + } + } + + @Test + fun `documented seealso with stdlib link with other tags`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | /** + | * random comment + | * @see Collection Comment to stdliblink + | * @author pikinier20 + | * @since 0.11 + | */ + |fun function(abc: String) { + | println(abc) + |} + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } + .children.single { it.name == "function" } as ContentPage + page.content.assertNode { + group { + header(1) { +"function" } + } + divergentGroup { + divergentInstance { + before { + pWrapped("random comment") + unnamedTag("Author") { +"pikinier20" } + unnamedTag("Since") { +"0.11" } + + header(2) { +"See also" } + group { + platformHinted { + table { + group { + //DRI should be "test//abc/#/-1/" + link { +"Collection" } + group { +"Comment to stdliblink" } + } + } + } + } + } + divergent { + bareSignature( + emptyMap(), + "", + "", + emptySet(), + "function", + null, + "abc" to ParamAttributes(emptyMap(), emptySet(), "String") + ) + } + } + } + } + } + } + } + + @Test + fun `documented multiple see also`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | /** + | * @see abc Comment to abc1 + | * @see abc Comment to abc2 + | */ + |fun function(abc: String) { + | println(abc) + |} + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } + .children.single { it.name == "function" } as ContentPage + page.content.assertNode { + group { + header(1) { +"function" } + } + divergentGroup { + divergentInstance { + before { + header(2) { +"See also" } + group { + platformHinted { + table { + group { + //DRI should be "test//abc/#/-1/" + link { +"abc" } + group { +"Comment to abc2" } + } + } + } + } + } + divergent { + bareSignature( + emptyMap(), + "", + "", + emptySet(), + "function", + null, + "abc" to ParamAttributes(emptyMap(), emptySet(), "String") + ) + } + } + } + } + } + } + } + + @Test + fun `documented multiple see also mixed source`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | /** + | * @see abc Comment to abc1 + | * @see[Collection] Comment to collection + | */ + |fun function(abc: String) { + | println(abc) + |} + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } + .children.single { it.name == "function" } as ContentPage + page.content.assertNode { + group { + header(1) { +"function" } + } + divergentGroup { + divergentInstance { + before { + header(2) { +"See also" } + group { + platformHinted { + table { + group { + //DRI should be "test//abc/#/-1/" + link { +"abc" } + group { +"Comment to abc1" } + } + group { + //DRI should be "test//abc/#/-1/" + link { +"Collection" } + group { +"Comment to collection" } + } + } + } + } + } + divergent { + bareSignature( + emptyMap(), + "", + "", + emptySet(), + "function", + null, + "abc" to ParamAttributes(emptyMap(), emptySet(), "String") + ) + } + } + } + } + } + } + } +} diff --git a/plugins/base/src/test/kotlin/content/signatures/ContentForSignaturesTest.kt b/plugins/base/src/test/kotlin/content/signatures/ContentForSignaturesTest.kt new file mode 100644 index 00000000..cabe822d --- /dev/null +++ b/plugins/base/src/test/kotlin/content/signatures/ContentForSignaturesTest.kt @@ -0,0 +1,456 @@ +package content.signatures + +import matchers.content.* +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.doc.Text +import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest +import org.junit.jupiter.api.Test +import utils.ParamAttributes +import utils.bareSignature +import utils.propertySignature +import utils.typealiasSignature + +class ContentForSignaturesTest : AbstractCoreTest() { + + private val testConfiguration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/") + analysisPlatform = "jvm" + includeNonPublic = true + } + } + } + + @Test + fun `function`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | + |fun function(abc: String): String { + | return "Hello, " + abc + |} + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } + .children.single { it.name == "function" } as ContentPage + page.content.assertNode { + group { + header(1) { +"function" } + } + divergentGroup { + divergentInstance { + divergent { + bareSignature( + emptyMap(), + "", + "", + emptySet(), + "function", + "String", + "abc" to ParamAttributes(emptyMap(), emptySet(), "String") + ) + } + } + } + } + } + } + } + + @Test + fun `private function`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | + |private fun function(abc: String): String { + | return "Hello, " + abc + |} + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } + .children.single { it.name == "function" } as ContentPage + page.content.assertNode { + group { + header(1) { +"function" } + } + divergentGroup { + divergentInstance { + divergent { + bareSignature( + emptyMap(), + "private", + "", + emptySet(), + "function", + "String", + "abc" to ParamAttributes(emptyMap(), emptySet(), "String") + ) + } + } + } + } + } + } + } + + @Test + fun `open function`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | + |open fun function(abc: String): String { + | return "Hello, " + abc + |} + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } + .children.single { it.name == "function" } as ContentPage + page.content.assertNode { + group { + header(1) { +"function" } + } + divergentGroup { + divergentInstance { + divergent { + bareSignature( + emptyMap(), + "", + "open", + emptySet(), + "function", + "String", + "abc" to ParamAttributes(emptyMap(), emptySet(), "String") + ) + } + } + } + } + } + } + } + + @Test + fun `function without parameters`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | + |fun function(): String { + | return "Hello" + |} + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } + .children.single { it.name == "function" } as ContentPage + page.content.assertNode { + group { + header(1) { +"function" } + } + divergentGroup { + divergentInstance { + divergent { + bareSignature( + annotations = emptyMap(), + visibility = "", + modifier = "", + keywords = emptySet(), + name = "function", + returnType = "String", + ) + } + } + } + + } + } + } + } + + + @Test + fun `suspend function`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | + |suspend fun function(abc: String): String { + | return "Hello, " + abc + |} + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } + .children.single { it.name == "function" } as ContentPage + page.content.assertNode { + group { + header(1) { +"function" } + } + divergentGroup { + divergentInstance { + divergent { + bareSignature( + emptyMap(), + "", + "", + setOf("suspend"), + "function", + "String", + "abc" to ParamAttributes(emptyMap(), emptySet(), "String") + ) + } + } + } + } + } + } + } + + @Test + fun `protected open suspend function`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | + |protected open suspend fun function(abc: String): String { + | return "Hello, " + abc + |} + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } + .children.single { it.name == "function" } as ContentPage + page.content.assertNode { + group { + header(1) { +"function" } + } + divergentGroup { + divergentInstance { + divergent { + bareSignature( + emptyMap(), + "protected", + "open", + setOf("suspend"), + "function", + "String", + "abc" to ParamAttributes(emptyMap(), emptySet(), "String") + ) + } + } + } + } + } + } + } + + @Test + fun `protected open suspend inline function`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | + |protected open suspend inline fun function(abc: String): String { + | return "Hello, " + abc + |} + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } + .children.single { it.name == "function" } as ContentPage + page.content.assertNode { + group { + header(1) { +"function" } + } + divergentGroup { + divergentInstance { + divergent { + bareSignature( + emptyMap(), + "protected", + "open", + setOf("inline", "suspend"), + "function", + "String", + "abc" to ParamAttributes(emptyMap(), emptySet(), "String") + ) + } + } + } + } + } + } + } + + @Test + fun `property`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | + |val property: Int = 6 + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } as PackagePageNode + page.content.assertNode { + propertySignature(emptyMap(), "", "", emptySet(), "val", "property", "Int") + } + } + } + } + + @Test + fun `const property`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | + |const val property: Int = 6 + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } as PackagePageNode + page.content.assertNode { + propertySignature(emptyMap(), "", "", setOf("const"), "val", "property", "Int") + } + } + } + } + + @Test + fun `protected property`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | + |protected val property: Int = 6 + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } as PackagePageNode + page.content.assertNode { + propertySignature(emptyMap(), "protected", "", emptySet(), "val", "property", "Int") + } + } + } + } + + @Test + fun `protected lateinit property`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | + |protected lateinit var property: Int = 6 + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } as PackagePageNode + page.content.assertNode { + propertySignature(emptyMap(), "protected", "", setOf("lateinit"), "var", "property", "Int") + } + } + } + } + + @Test + fun `typealias to String`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | + |typealias Alias = String + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } as PackagePageNode + page.content.assertNode { + typealiasSignature("Alias", "String") + } + } + } + } + + @Test + fun `typealias to Int`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | + |typealias Alias = Int + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } as PackagePageNode + page.content.assertNode { + typealiasSignature("Alias", "Int") + } + } + } + } + + @Test + fun `typealias to type in same package`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | + |typealias Alias = X + |class X + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } as PackagePageNode + page.content.assertNode { + typealiasSignature("Alias", "X") + } + } + } + } + + @Test + fun `typealias to type in different package`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + |import other.X + |typealias Alias = X + | + |/src/main/kotlin/test/source2.kt + |package other + |class X + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } as PackagePageNode + page.content.assertNode { + typealiasSignature("Alias", "other.X") + } + } + } + } +} diff --git a/plugins/base/src/test/kotlin/content/signatures/SkippingParenthesisForConstructorsTest.kt b/plugins/base/src/test/kotlin/content/signatures/SkippingParenthesisForConstructorsTest.kt new file mode 100644 index 00000000..7de48664 --- /dev/null +++ b/plugins/base/src/test/kotlin/content/signatures/SkippingParenthesisForConstructorsTest.kt @@ -0,0 +1,254 @@ +package content.signatures + +import matchers.content.* +import org.jetbrains.dokka.pages.ContentPage +import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest +import org.junit.jupiter.api.Test +import utils.functionSignature + +class ConstructorsSignaturesTest : AbstractCoreTest() { + private val testConfiguration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/") + analysisPlatform = "jvm" + } + } + } + + @Test + fun `class name without parenthesis`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | + |class SomeClass + | + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } + .children.single { it.name == "SomeClass" } as ContentPage + page.content.assertNode { + group { + header(1) { +"SomeClass" } + platformHinted { + group { + +"class" + link { +"SomeClass" } + } + } + } + skipAllNotMatching() + } + } + } + } + + @Test + fun `class name with empty parenthesis`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | + |class SomeClass() + | + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } + .children.single { it.name == "SomeClass" } as ContentPage + page.content.assertNode { + group { + header(1) { +"SomeClass" } + platformHinted { + group { + +"class" + link { +"SomeClass" } + } + } + } + skipAllNotMatching() + } + } + } + } + + @Test + fun `class with a parameter`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | + |class SomeClass(a: String) + | + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } + .children.single { it.name == "SomeClass" } as ContentPage + page.content.assertNode { + group { + header(1) { +"SomeClass" } + platformHinted { + group { + +"class" + link { +"SomeClass" } + +"(a:" + group { link { +"String" } } + +")" + } + } + } + skipAllNotMatching() + } + } + } + } + + @Test + fun `class with a val parameter`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | + |class SomeClass(val a: String) + | + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } + .children.single { it.name == "SomeClass" } as ContentPage + page.content.assertNode { + group { + header(1) { +"SomeClass" } + platformHinted { + group { + +"class" + link { +"SomeClass" } + +"(a:" // TODO: Make sure if we still do not want to have "val" here + group { link { +"String" } } + +")" + } + } + } + skipAllNotMatching() + } + } + } + } + + @Test + fun `class with a parameterless secondary constructor`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | + |class SomeClass(a: String) { + | constructor() + |} + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } + .children.single { it.name == "SomeClass" } as ContentPage + page.content.assertNode { + group { + header(1) { +"SomeClass" } + platformHinted { + group { + +"class" + link { +"SomeClass" } + +"(a:" + group { link { +"String" } } + +")" + } + } + } + group { + header { +"Constructors" } + table { + group { + link { +"<init>" } + functionSignature( + annotations = emptyMap(), + visibility = "", + modifier = "", + keywords = emptySet(), + name = "<init>" + ) + } + } + skipAllNotMatching() + } + } + } + } + } + + @Test + fun `class with explicitly documented constructor`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | + | /** + | * some comment + | * @constructor ctor comment + | **/ + |class SomeClass(a: String) + """.trimIndent(), testConfiguration + ) { + pagesTransformationStage = { module -> + val page = module.children.single { it.name == "test" } + .children.single { it.name == "SomeClass" } as ContentPage + page.content.assertNode { + group { + header(1) { +"SomeClass" } + platformHinted { + skipAllNotMatching() + group { + +"class" + link { +"SomeClass" } + +"(a:" + group { link { +"String" } } + +")" + } + } + } + group { + header { +"Constructors" } + table { + group { + link { +"<init>" } + platformHinted { + group { + group { + +"ctor comment" + } + } + group { + +"fun" + link { +"<init>" } + +"(a:" + group { + link { +"String" } + } + +")" + } + } + } + } + skipAllNotMatching() + } + } + } + } + } +} diff --git a/plugins/base/src/test/kotlin/enums/EnumsTest.kt b/plugins/base/src/test/kotlin/enums/EnumsTest.kt new file mode 100644 index 00000000..6a973f8e --- /dev/null +++ b/plugins/base/src/test/kotlin/enums/EnumsTest.kt @@ -0,0 +1,234 @@ +package enums + +import matchers.content.* +import org.jetbrains.dokka.model.ConstructorValues +import org.jetbrains.dokka.model.DEnum +import org.jetbrains.dokka.model.dfs +import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test + +class EnumsTest : AbstractCoreTest() { + + @Test + fun basicEnum() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/") + } + } + } + + testInline( + """ + |/src/main/kotlin/basic/Test.kt + |package enums + | + |enum class Test { + | E1, + | E2 + |} + """.trimMargin(), + configuration + ) { + pagesGenerationStage = { + val map = it.getClasslikeToMemberMap() + val test = map.filterKeys { it.name == "Test" }.values.firstOrNull() + assertTrue(test != null) { "Test not found" } + assertTrue(test!!.any { it.name == "E1" } && test.any { it.name == "E2" }) { "Enum entries missing in parent" } + } + } + } + + @Test + fun enumWithCompanion() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/") + } + } + } + + testInline( + """ + |/src/main/kotlin/basic/Test.kt + |package enums + | + |enum class Test { + | E1, + | E2; + | companion object {} + |} + """.trimMargin(), + configuration + ) { + documentablesTransformationStage = { m -> + m.packages.let { p -> + assertTrue(p.isNotEmpty(), "Package list cannot be empty") + p.first().classlikes.let { c -> + assertTrue(c.isNotEmpty(), "Classlikes list cannot be empty") + + val enum = c.first() as DEnum + assertEquals(enum.name, "Test") + assertEquals(enum.entries.count(), 2) + assertNotNull(enum.companion) + } + } + } + pagesGenerationStage = { module -> + val map = module.getClasslikeToMemberMap() + val test = map.filterKeys { it.name == "Test" }.values.firstOrNull() + assertNotNull(test, "Test not found") + assertTrue(test!!.any { it.name == "E1" } && test.any { it.name == "E2" }) { "Enum entries missing in parent" } + } + } + } + + @Test + fun enumWithConstructor() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/") + } + } + } + + testInline( + """ + |/src/main/kotlin/basic/Test.kt + |package enums + | + | + |enum class Test(name: String, index: Int, excluded: Boolean) { + | E1("e1", 1, true), + | E2("e2", 2, false); + |} + """.trimMargin(), + configuration + ) { + documentablesTransformationStage = { m -> + m.packages.let { p -> + p.first().classlikes.let { c -> + val enum = c.first() as DEnum + val (first, second) = enum.entries + + assertEquals(1, first.extra.allOfType<ConstructorValues>().size) + assertEquals(1, second.extra.allOfType<ConstructorValues>().size) + assertEquals(listOf("\"e1\"", "1", "true"), first.extra.allOfType<ConstructorValues>().first().values.values.first()) + assertEquals(listOf("\"e2\"", "2", "false"), second.extra.allOfType<ConstructorValues>().first().values.values.first()) + } + } + } + pagesGenerationStage = { module -> + val entryPage = module.dfs { it.name == "E1" } as ClasslikePageNode + val signaturePart = (entryPage.content.dfs { + it is ContentGroup && it.dci.toString() == "[enums/Test.E1///PointingToDeclaration/][Symbol]" + } as ContentGroup) + assertEquals("(\"e1\", 1, true)", signaturePart.constructorSignature()) + } + } + } + + @Test + fun enumWithMethods() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/") + } + } + } + + testInline( + """ + |/src/main/kotlin/basic/Test.kt + |package enums + | + | + |interface Sample { + | fun toBeImplemented(): String + |} + | + |enum class Test: Sample { + | E1 { + | override fun toBeImplemented(): String = "e1" + | } + |} + """.trimMargin(), + configuration + ) { + documentablesTransformationStage = { m -> + m.packages.let { p -> + p.first().classlikes.let { c -> + val enum = c.first { it is DEnum } as DEnum + val first = enum.entries.first() + + assertEquals(1, first.extra.allOfType<ConstructorValues>().size) + assertEquals(emptyList<String>(), first.extra.allOfType<ConstructorValues>().first().values.values.first()) + assertNotNull(first.functions.find { it.name == "toBeImplemented" }) + } + } + } + } + } + + @Test + fun enumWithAnnotationsOnEntries(){ + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/") + } + } + } + + testInline( + """ + |/src/main/kotlin/basic/Test.kt + |package enums + | + |enum class Test { + | /** + | Sample docs for E1 + | **/ + | @SinceKotlin("1.3") // This annotation is transparent due to lack of @MustBeDocumented annotation + | E1 + |} + """.trimMargin(), + configuration + ) { + pagesTransformationStage = { m -> + val entryNode = m.children.first { it.name == "enums" }.children.first { it.name == "Test" }.children.first() as ClasslikePageNode + val signature = (entryNode.content as ContentGroup).dfs { it is ContentGroup && it.dci.toString() == "[enums/Test.E1///PointingToDeclaration/][Cover]" } as ContentGroup + + signature.assertNode { + header(1) { +"E1" } + platformHinted { + group { + group { + + "Sample docs for E1" + } + } + group { + group { + link { +"E1" } + +"()" + } + } + } + } + } + } + } + + + fun RootPageNode.getClasslikeToMemberMap() = + this.parentMap.filterValues { it is ClasslikePageNode }.entries.groupBy({ it.value }) { it.key } + + private fun ContentGroup.constructorSignature(): String = + (children.single() as ContentGroup).children.drop(1).joinToString(separator = "") { (it as ContentText).text } +} diff --git a/plugins/base/src/test/kotlin/expect/AbstractExpectTest.kt b/plugins/base/src/test/kotlin/expect/AbstractExpectTest.kt new file mode 100644 index 00000000..4dfdc410 --- /dev/null +++ b/plugins/base/src/test/kotlin/expect/AbstractExpectTest.kt @@ -0,0 +1,104 @@ +package expect + +import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest +import org.junit.jupiter.api.Assertions.assertTrue +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.Paths +import java.util.concurrent.TimeUnit + +abstract class AbstractExpectTest( + val testDir: Path? = Paths.get("src/test", "resources", "expect"), + val formats: List<String> = listOf("html") +) : AbstractCoreTest() { + + protected fun generateOutput(path: Path, outFormat: String): Path? { + val config = dokkaConfiguration { + format = outFormat + sourceSets { + sourceSet { + sourceRoots = listOf(path.toAbsolutePath().asString()) + } + } + } + + var result: Path? = null + testFromData(config, cleanupOutput = false) { + renderingStage = { _, context -> result = Paths.get(context.configuration.outputDir) } + } + return result + } + + protected fun compareOutput(expected: Path, obtained: Path?, gitTimeout: Long = 500) { + obtained?.let { path -> + val gitCompare = ProcessBuilder( + "git", + "--no-pager", + "diff", + expected.asString(), + path.asString() + ).also { logger.info("git diff command: ${it.command().joinToString(" ")}") } + .also { it.redirectErrorStream() }.start() + + assertTrue(gitCompare.waitFor(gitTimeout, TimeUnit.MILLISECONDS)) { "Git timed out after $gitTimeout" } + gitCompare.inputStream.bufferedReader().lines().forEach { logger.info(it) } + assertTrue(gitCompare.exitValue() == 0) { "${path.fileName}: outputs don't match" } + } ?: throw AssertionError("obtained path is null") + } + + protected fun compareOutputWithExcludes( + expected: Path, + obtained: Path?, + excludes: List<String>, + timeout: Long = 500 + ) { + obtained?.let { path -> + val (res, out, err) = runDiff(expected, obtained, excludes, timeout) + assertTrue(res == 0, "Outputs differ:\nstdout - $out\n\nstderr - ${err ?: ""}") + } ?: throw AssertionError("obtained path is null") + } + + protected fun runDiff(exp: Path, obt: Path, excludes: List<String>, timeout: Long): ProcessResult = + ProcessBuilder().command( + listOf("diff", "-ru") + excludes.flatMap { listOf("-x", it) } + listOf("--", exp.asString(), obt.asString()) + ).also { + it.redirectErrorStream() + }.start().also { assertTrue(it.waitFor(timeout, TimeUnit.MILLISECONDS), "diff timed out") }.let { + ProcessResult(it.exitValue(), it.inputStream.bufferResult()) + } + + + protected fun testOutput(p: Path, outFormat: String) { + val expectOut = p.resolve("out/$outFormat") + val testOut = generateOutput(p.resolve("src"), outFormat) + .also { logger.info("Test out: ${it?.asString()}") } + + compareOutput(expectOut.toAbsolutePath(), testOut?.toAbsolutePath()) + testOut?.deleteRecursively() + } + + protected fun testOutputWithExcludes( + p: Path, + outFormat: String, + ignores: List<String> = emptyList(), + timeout: Long = 500 + ) { + val expected = p.resolve("out/$outFormat") + generateOutput(p.resolve("src"), outFormat) + ?.let { obtained -> + compareOutputWithExcludes(expected, obtained, ignores, timeout) + + obtained.deleteRecursively() + } ?: throw AssertionError("Output not generated for ${p.fileName}") + } + + protected fun generateExpect(p: Path, outFormat: String) { + val out = p.resolve("out/$outFormat/") + Files.createDirectories(out) + + val ret = generateOutput(p.resolve("src"), outFormat) + Files.list(out).forEach { it.deleteRecursively() } + ret?.let { Files.list(it).forEach { f -> f.copyRecursively(out.resolve(f.fileName)) } } + } + +} diff --git a/plugins/base/src/test/kotlin/expect/ExpectGenerator.kt b/plugins/base/src/test/kotlin/expect/ExpectGenerator.kt new file mode 100644 index 00000000..cb3313f9 --- /dev/null +++ b/plugins/base/src/test/kotlin/expect/ExpectGenerator.kt @@ -0,0 +1,13 @@ +package expect + +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test + +class ExpectGenerator : AbstractExpectTest() { + + @Disabled + @Test + fun generateAll() = testDir?.dirsWithFormats(formats).orEmpty().forEach { (p, f) -> + generateExpect(p, f) + } +}
\ No newline at end of file diff --git a/plugins/base/src/test/kotlin/expect/ExpectTest.kt b/plugins/base/src/test/kotlin/expect/ExpectTest.kt new file mode 100644 index 00000000..bed841a6 --- /dev/null +++ b/plugins/base/src/test/kotlin/expect/ExpectTest.kt @@ -0,0 +1,24 @@ +package expect + +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.DynamicTest.dynamicTest +import org.junit.jupiter.api.TestFactory + +class ExpectTest : AbstractExpectTest() { + private val ignores: List<String> = listOf( + "images", + "scripts", + "images", + "styles", + "*.js", + "*.css", + "*.svg", + "*.map" + ) + + @Disabled + @TestFactory + fun expectTest() = testDir?.dirsWithFormats(formats).orEmpty().map { (p, f) -> + dynamicTest("${p.fileName}-$f") { testOutputWithExcludes(p, f, ignores) } + } +}
\ No newline at end of file diff --git a/plugins/base/src/test/kotlin/expect/ExpectUtils.kt b/plugins/base/src/test/kotlin/expect/ExpectUtils.kt new file mode 100644 index 00000000..1f54c20e --- /dev/null +++ b/plugins/base/src/test/kotlin/expect/ExpectUtils.kt @@ -0,0 +1,28 @@ +package expect + +import java.io.InputStream +import java.nio.file.Files +import java.nio.file.Path +import kotlin.streams.toList + +data class ProcessResult(val code: Int, val out: String, val err: String? = null) + +internal fun Path.dirsWithFormats(formats: List<String>): List<Pair<Path, String>> = + Files.list(this).toList().filter { Files.isDirectory(it) }.flatMap { p -> formats.map { p to it } } + +internal fun Path.asString() = normalize().toString() +internal fun Path.deleteRecursively() = toFile().deleteRecursively() + +internal fun Path.copyRecursively(target: Path) = toFile().copyRecursively(target.toFile()) + +internal fun Path.listRecursively(filter: (Path) -> Boolean): List<Path> = when { + Files.isDirectory(this) -> listOfNotNull(takeIf(filter)) + Files.list(this).toList().flatMap { + it.listRecursively( + filter + ) + } + Files.isRegularFile(this) -> listOfNotNull(this.takeIf(filter)) + else -> emptyList() + } + +internal fun InputStream.bufferResult(): String = this.bufferedReader().lines().toList().joinToString("\n")
\ No newline at end of file diff --git a/plugins/base/src/test/kotlin/filter/DeprecationFilterTest.kt b/plugins/base/src/test/kotlin/filter/DeprecationFilterTest.kt new file mode 100644 index 00000000..c8b9f2d4 --- /dev/null +++ b/plugins/base/src/test/kotlin/filter/DeprecationFilterTest.kt @@ -0,0 +1,173 @@ +package filter + +import org.jetbrains.dokka.PackageOptionsImpl +import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test + +class DeprecationFilterTest : AbstractCoreTest() { + @Test + fun `function with false global skipDeprecated`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + skipDeprecated = false + sourceRoots = listOf("src/main/kotlin/basic/Test.kt") + } + } + } + + testInline( + """ + |/src/main/kotlin/basic/Test.kt + |package example + | + |fun testFunction() { } + | + | + | + """.trimMargin(), + configuration + ) { + documentablesFirstTransformationStep = { + Assertions.assertTrue( + it.component2().packages.first().functions.size == 1 + ) + } + } + } + @Test + fun `deprecated function with false global skipDeprecated`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + skipDeprecated = false + sourceRoots = listOf("src/main/kotlin/basic/Test.kt") + } + } + } + + testInline( + """ + |/src/main/kotlin/basic/Test.kt + |package example + | + |@Deprecated("dep") + |fun testFunction() { } + | + | + """.trimMargin(), + configuration + ) { + documentablesFirstTransformationStep = { + Assertions.assertTrue( + it.component2().packages.first().functions.size == 1 + ) + } + } + } + @Test + fun `deprecated function with true global skipDeprecated`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin/basic/Test.kt") + skipDeprecated = true + } + } + } + + testInline( + """ + |/src/main/kotlin/basic/Test.kt + |package example + | + |@Deprecated("dep") + |fun testFunction() { } + | + | + """.trimMargin(), + configuration + ) { + documentablesFirstTransformationStep = { + Assertions.assertTrue( + it.component2().packages.first().functions.size == 0 + ) + } + } + } + @Test + fun `deprecated function with false global true package skipDeprecated`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin/basic/Test.kt") + skipDeprecated = false + perPackageOptions = mutableListOf( + PackageOptionsImpl("example", + true, + false, + true, + false) + ) + } + } + } + + testInline( + """ + |/src/main/kotlin/basic/Test.kt + |package example + | + |@Deprecated("dep") + |fun testFunction() { } + | + | + """.trimMargin(), + configuration + ) { + documentablesFirstTransformationStep = { + Assertions.assertTrue( + it.component2().packages.first().functions.size == 0 + ) + } + } + } + @Test + fun `deprecated function with true global false package skipDeprecated`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin/basic/Test.kt") + skipDeprecated = true + perPackageOptions = mutableListOf( + PackageOptionsImpl("example", + false, + false, + false, + false) + ) + } + } + } + + testInline( + """ + |/src/main/kotlin/basic/Test.kt + |package example + | + |@Deprecated("dep") + |fun testFunction() { } + | + | + """.trimMargin(), + configuration + ) { + documentablesFirstTransformationStep = { + Assertions.assertTrue( + it.component2().packages.first().functions.size == 1 + ) + } + } + } +} diff --git a/plugins/base/src/test/kotlin/filter/EmptyPackagesFilterTest.kt b/plugins/base/src/test/kotlin/filter/EmptyPackagesFilterTest.kt new file mode 100644 index 00000000..e5b9e9c2 --- /dev/null +++ b/plugins/base/src/test/kotlin/filter/EmptyPackagesFilterTest.kt @@ -0,0 +1,63 @@ +package filter + +import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test + +class EmptyPackagesFilterTest : AbstractCoreTest() { + @Test + fun `empty package with false skipEmptyPackages`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + skipEmptyPackages = false + sourceRoots = listOf("src/main/kotlin/basic/Test.kt") + } + } + } + + testInline( + """ + |/src/main/kotlin/basic/Test.kt + |package example + | + | + | + """.trimMargin(), + configuration + ) { + documentablesFirstTransformationStep = { + Assertions.assertTrue( + it.component2().packages.isNotEmpty() + ) + } + } + } + @Test + fun `empty package with true skipEmptyPackages`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + skipEmptyPackages = true + sourceRoots = listOf("src/main/kotlin/basic/Test.kt") + } + } + } + + testInline( + """ + |/src/main/kotlin/basic/Test.kt + |package example + | + | + """.trimMargin(), + configuration + ) { + documentablesFirstTransformationStep = { + Assertions.assertTrue( + it.component2().packages.isEmpty() + ) + } + } + } +} diff --git a/plugins/base/src/test/kotlin/filter/VisibilityFilterTest.kt b/plugins/base/src/test/kotlin/filter/VisibilityFilterTest.kt new file mode 100644 index 00000000..192de449 --- /dev/null +++ b/plugins/base/src/test/kotlin/filter/VisibilityFilterTest.kt @@ -0,0 +1,173 @@ +package filter + +import org.jetbrains.dokka.PackageOptionsImpl +import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test + +class VisibilityFilterTest : AbstractCoreTest() { + @Test + fun `public function with false global includeNonPublic`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + includeNonPublic = false + sourceRoots = listOf("src/main/kotlin/basic/Test.kt") + } + } + } + + testInline( + """ + |/src/main/kotlin/basic/Test.kt + |package example + | + |fun testFunction() { } + | + | + | + """.trimMargin(), + configuration + ) { + documentablesFirstTransformationStep = { + Assertions.assertTrue( + it.component2().packages.first().functions.size == 1 + ) + } + } + } + @Test + fun `private function with false global includeNonPublic`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + includeNonPublic = false + sourceRoots = listOf("src/main/kotlin/basic/Test.kt") + } + } + } + + testInline( + """ + |/src/main/kotlin/basic/Test.kt + |package example + | + |private fun testFunction() { } + | + | + | + """.trimMargin(), + configuration + ) { + documentablesFirstTransformationStep = { + Assertions.assertTrue( + it.component2().packages.first().functions.size == 0 + ) + } + } + } + @Test + fun `private function with true global includeNonPublic`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin/basic/Test.kt") + includeNonPublic = true + } + } + } + + testInline( + """ + |/src/main/kotlin/basic/Test.kt + |package example + | + |private fun testFunction() { } + | + | + | + """.trimMargin(), + configuration + ) { + documentablesFirstTransformationStep = { + Assertions.assertTrue( + it.component2().packages.first().functions.size == 1 + ) + } + } + } + @Test + fun `private function with false global true package includeNonPublic`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin/basic/Test.kt") + includeNonPublic = false + perPackageOptions = mutableListOf( + PackageOptionsImpl("example", + true, + false, + false, + false) + ) + } + } + } + + testInline( + """ + |/src/main/kotlin/basic/Test.kt + |package example + | + |private fun testFunction() { } + | + | + | + """.trimMargin(), + configuration + ) { + documentablesFirstTransformationStep = { + Assertions.assertTrue( + it.component2().packages.first().functions.size == 1 + ) + } + } + } + @Test + fun `private function with true global false package includeNonPublic`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin/basic/Test.kt") + includeNonPublic = true + perPackageOptions = mutableListOf( + PackageOptionsImpl("example", + false, + false, + false, + false) + ) + } + } + } + + testInline( + """ + |/src/main/kotlin/basic/Test.kt + |package example + | + |private fun testFunction() { } + | + | + | + """.trimMargin(), + configuration + ) { + documentablesFirstTransformationStep = { + Assertions.assertTrue( + it.component2().packages.first().functions.size == 0 + ) + } + } + } +} diff --git a/plugins/base/src/test/kotlin/issues/IssuesTest.kt b/plugins/base/src/test/kotlin/issues/IssuesTest.kt new file mode 100644 index 00000000..7b065349 --- /dev/null +++ b/plugins/base/src/test/kotlin/issues/IssuesTest.kt @@ -0,0 +1,73 @@ +package issues + +import org.jetbrains.dokka.model.DClass +import org.jetbrains.dokka.model.DFunction +import org.junit.jupiter.api.Test +import utils.AbstractModelTest +import utils.name + +class IssuesTest : AbstractModelTest("/src/main/kotlin/issues/Test.kt", "issues") { + + @Test + fun errorClasses() { + inlineModelTest( + """ + |class Test(var value: String) { + | fun test(): List<String> = emptyList() + | fun brokenApply(v: String) = apply { value = v } + | + | fun brokenRun(v: String) = run { + | value = v + | this + | } + | + | fun brokenLet(v: String) = let { + | it.value = v + | it + | } + | + | fun brokenGenerics() = listOf("a", "b", "c") + | + | fun working(v: String) = doSomething() + | + | fun doSomething(): String = "Hello" + |} + """, + configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/") + classpath = listOfNotNull(jvmStdlibPath) + } + } + } + ) { + with((this / "issues" / "Test").cast<DClass>()) { + (this / "working").cast<DFunction>().type.name equals "String" + (this / "doSomething").cast<DFunction>().type.name equals "String" + (this / "brokenGenerics").cast<DFunction>().type.name equals "List" + (this / "brokenApply").cast<DFunction>().type.name equals "Test" + (this / "brokenRun").cast<DFunction>().type.name equals "Test" + (this / "brokenLet").cast<DFunction>().type.name equals "Test" + } + } + } + + //@Test + // fun errorClasses() { + // checkSourceExistsAndVerifyModel("testdata/issues/errorClasses.kt", + // modelConfig = ModelConfig(analysisPlatform = analysisPlatform, withJdk = true, withKotlinRuntime = true)) { model -> + // val cls = model.members.single().members.single() + // + // fun DocumentationNode.returnType() = this.details.find { it.kind == NodeKind.Type }?.name + // assertEquals("Test", cls.members[1].returnType()) + // assertEquals("Test", cls.members[2].returnType()) + // assertEquals("Test", cls.members[3].returnType()) + // assertEquals("List", cls.members[4].returnType()) + // assertEquals("String", cls.members[5].returnType()) + // assertEquals("String", cls.members[6].returnType()) + // assertEquals("String", cls.members[7].returnType()) + // } + // } + +} diff --git a/plugins/base/src/test/kotlin/linkableContent/LinkableContentTest.kt b/plugins/base/src/test/kotlin/linkableContent/LinkableContentTest.kt new file mode 100644 index 00000000..a8fc9f6d --- /dev/null +++ b/plugins/base/src/test/kotlin/linkableContent/LinkableContentTest.kt @@ -0,0 +1,225 @@ +package linkableContent + +import org.jetbrains.dokka.SourceLinkDefinitionImpl +import org.jetbrains.dokka.base.DokkaBase +import org.jetbrains.dokka.base.transformers.pages.samples.DefaultSamplesTransformer +import org.jetbrains.dokka.base.transformers.pages.sourcelinks.SourceLinksTransformer +import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder +import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.plugability.plugin +import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest +import org.jetbrains.kotlin.utils.addToStdlib.cast +import org.jetbrains.kotlin.utils.addToStdlib.safeAs +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test +import java.nio.file.Paths + +class LinkableContentTest : AbstractCoreTest() { + + @Test + fun `Include module and package documentation`() { + + val testDataDir = getTestDataDir("multiplatform/basicMultiplatformTest").toAbsolutePath() + val includesDir = getTestDataDir("linkable/includes").toAbsolutePath() + + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + moduleName = "example" + analysisPlatform = "js" + sourceRoots = listOf("jsMain", "commonMain", "jvmAndJsSecondCommonMain").map { + Paths.get("$testDataDir/$it/kotlin").toString() + } + name = "js" + includes = listOf(Paths.get("$includesDir/include2.md").toString()) + } + sourceSet { + moduleName = "example" + analysisPlatform = "jvm" + sourceRoots = listOf("jvmMain", "commonMain", "jvmAndJsSecondCommonMain").map { + Paths.get("$testDataDir/$it/kotlin").toString() + } + name = "jvm" + includes = listOf(Paths.get("$includesDir/include1.md").toString()) + } + } + } + + testFromData(configuration) { + documentablesMergingStage = { + Assertions.assertEquals(2, it.documentation.size) + Assertions.assertEquals(2, it.packages.size) + Assertions.assertEquals(1, it.packages.first().documentation.size) + Assertions.assertEquals(1, it.packages.last().documentation.size) + } + } + + } + + @Test + fun `Sources multiplatform class documentation`() { + + val testDataDir = getTestDataDir("linkable/sources").toAbsolutePath() + + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + moduleName = "example" + analysisPlatform = "js" + sourceRoots = listOf("$testDataDir/jsMain/kotlin") + sourceLinks = listOf( + SourceLinkDefinitionImpl( + path = "jsMain/kotlin", + url = "https://github.com/user/repo/tree/master/src/jsMain/kotlin", + lineSuffix = "#L" + ) + ) + name = "js" + } + sourceSet { + moduleName = "example" + analysisPlatform = "jvm" + sourceRoots = listOf("$testDataDir/jvmMain/kotlin") + sourceLinks = listOf( + SourceLinkDefinitionImpl( + path = "jvmMain/kotlin", + url = "https://github.com/user/repo/tree/master/src/jvmMain/kotlin", + lineSuffix = "#L" + ) + ) + name = "jvm" + } + } + } + + testFromData(configuration) { + renderingStage = { rootPageNode, dokkaContext -> + val newRoot = SourceLinksTransformer( + dokkaContext, + PageContentBuilder( + dokkaContext.single(dokkaContext.plugin<DokkaBase>().commentsToContentConverter), + dokkaContext.single(dokkaContext.plugin<DokkaBase>().signatureProvider), + dokkaContext.logger + ) + ).invoke(rootPageNode) + val moduleChildren = newRoot.children + Assertions.assertEquals(1, moduleChildren.size) + val packageChildren = moduleChildren.first().children + Assertions.assertEquals(2, packageChildren.size) + packageChildren.forEach { + val name = it.name.substringBefore("Class") + val crl = it.safeAs<ClasslikePageNode>()?.content?.safeAs<ContentGroup>()?.children?.last() + ?.safeAs<ContentGroup>()?.children?.last()?.safeAs<ContentGroup>()?.children?.lastOrNull() + ?.safeAs<ContentTable>()?.children?.singleOrNull() + ?.safeAs<ContentGroup>()?.children?.singleOrNull().safeAs<ContentResolvedLink>() + Assertions.assertEquals( + "https://github.com/user/repo/tree/master/src/${name.toLowerCase()}Main/kotlin/${name}Class.kt#L3", + crl?.address + ) + } + } + } + } + + @Test + fun `Samples multiplatform documentation`() { + + val testDataDir = getTestDataDir("linkable/samples").toAbsolutePath() + + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + moduleName = "example" + analysisPlatform = "js" + sourceRoots = listOf("$testDataDir/jsMain/kotlin") + name = "js" + samples = listOf("$testDataDir/jsMain/resources/Samples.kt") + } + sourceSet { + moduleName = "example" + analysisPlatform = "jvm" + sourceRoots = listOf("$testDataDir/jvmMain/kotlin") + name = "jvm" + samples = listOf("$testDataDir/jvmMain/resources/Samples.kt") + } + } + } + + testFromData(configuration) { + renderingStage = { rootPageNode, dokkaContext -> + val newRoot = DefaultSamplesTransformer(dokkaContext).invoke(rootPageNode) + + val moduleChildren = newRoot.children + Assertions.assertEquals(1, moduleChildren.size) + val packageChildren = moduleChildren.first().children + Assertions.assertEquals(2, packageChildren.size) + packageChildren.forEach { + val name = it.name.substringBefore("Class") + val classChildren = it.children + Assertions.assertEquals(2, classChildren.size) + val function = classChildren.find { it.name == "printWithExclamation" } + val text = function.cast<MemberPageNode>().content.cast<ContentGroup>().children.last() + .cast<ContentDivergentGroup>().children.single() + .cast<ContentDivergentInstance>().before + .cast<ContentGroup>().children.last() + .cast<ContentGroup>().children.last() + .cast<PlatformHintedContent>().children.single() + .cast<ContentGroup>().children.single() + .cast<ContentGroup>().children.single() + .cast<ContentCodeBlock>().children.single().cast<ContentText>().text + Assertions.assertEquals( + """|import p2.${name}Class + |fun main() { + | //sampleStart + | ${name}Class().printWithExclamation("Hi, $name") + | //sampleEnd + |}""".trimMargin(), + text + ) + } + } + } + } + + @Test + fun `Documenting return type for a function in inner class with generic parent`() { + testInline( + """ + |/src/main/kotlin/test/source.kt + |package test + | + |class Sample<S>(first: S){ + | inner class SampleInner { + | fun foo(): S = TODO() + | } + |} + | + """.trimIndent(), + dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/") + analysisPlatform = "jvm" + name = "js" + } + } + } + ) { + renderingStage = { module, _ -> + val sample = module.children.single { it.name == "test" } + .children.single { it.name == "Sample" }.cast<ClasslikePageNode>() + val foo = sample + .children.single { it.name == "SampleInner" }.cast<ClasslikePageNode>() + .children.single { it.name == "foo" }.cast<MemberPageNode>() + + val returnTypeNode = foo.content.dfs { + val link = it.safeAs<ContentDRILink>()?.children + val child = link?.first().safeAs<ContentText>() + child?.text == "S" + }?.safeAs<ContentDRILink>() + + Assertions.assertEquals(sample.dri.first(), returnTypeNode?.address) + } + } + } +} diff --git a/plugins/base/src/test/kotlin/locationProvider/DefaultLocationProviderTest.kt b/plugins/base/src/test/kotlin/locationProvider/DefaultLocationProviderTest.kt new file mode 100644 index 00000000..a219fb04 --- /dev/null +++ b/plugins/base/src/test/kotlin/locationProvider/DefaultLocationProviderTest.kt @@ -0,0 +1,41 @@ +package locationProvider + +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.base.resolvers.local.DefaultLocationProvider +import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest +import org.junit.jupiter.api.Assertions.assertNotEquals +import org.junit.jupiter.api.Test + +class DefaultLocationProviderTest: AbstractCoreTest() { + @Test + fun `#644 same directory for module and package`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/") + } + } + } + + testInline( + """ + |/src/main/kotlin/basic/Test.kt + | + |class Test { + | val x = 1 + |} + """.trimMargin(), + configuration + ) { + var context: DokkaContext? = null + pluginsSetupStage = { + context = it + } + + pagesGenerationStage = { module -> + val lp = DefaultLocationProvider(module, context!!) + assertNotEquals(lp.resolve(module.children.single()).removePrefix("/"), lp.resolve(module)) + } + } + } +} diff --git a/plugins/base/src/test/kotlin/markdown/KDocTest.kt b/plugins/base/src/test/kotlin/markdown/KDocTest.kt new file mode 100644 index 00000000..f5b29322 --- /dev/null +++ b/plugins/base/src/test/kotlin/markdown/KDocTest.kt @@ -0,0 +1,47 @@ +package markdown + +import org.jetbrains.dokka.model.DPackage +import org.jetbrains.dokka.model.doc.DocumentationNode +import org.jetbrains.dokka.pages.ModulePageNode +import org.junit.jupiter.api.Assertions.* +import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest + +abstract class KDocTest : AbstractCoreTest() { + + private val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin/example/Test.kt") + } + } + } + + private fun interpolateKdoc(kdoc: String) = """ + |/src/main/kotlin/example/Test.kt + |package example + | /** + ${kdoc.split("\n").joinToString("") { "| *$it\n" } } + | */ + |class Test + """.trimMargin() + + private fun actualDocumentationNode(modulePageNode: ModulePageNode) = + (modulePageNode.documentable?.children?.first() as DPackage) + .classlikes.single() + .documentation.values.single() + + + protected fun executeTest(kdoc: String, expectedDocumentationNode: DocumentationNode) { + testInline( + interpolateKdoc(kdoc), + configuration + ) { + pagesGenerationStage = { + assertEquals( + expectedDocumentationNode, + actualDocumentationNode(it as ModulePageNode) + ) + } + } + } +} diff --git a/plugins/base/src/test/kotlin/markdown/LinkTest.kt b/plugins/base/src/test/kotlin/markdown/LinkTest.kt new file mode 100644 index 00000000..a6333c5a --- /dev/null +++ b/plugins/base/src/test/kotlin/markdown/LinkTest.kt @@ -0,0 +1,79 @@ +package markdown + +import org.jetbrains.dokka.pages.ClasslikePageNode +import org.jetbrains.dokka.pages.ContentDRILink +import org.jetbrains.dokka.pages.MemberPageNode +import org.jetbrains.dokka.pages.dfs +import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertNotNull +import org.junit.jupiter.api.Test + +class LinkTest : AbstractCoreTest() { + @Test + fun linkToClassLoader() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin/parser") + } + } + } + testInline( + """ + |/src/main/kotlin/parser/Test.kt + |package parser + | + | /** + | * Some docs that link to [ClassLoader.clearAssertionStatus] + | */ + |fun test(x: ClassLoader) = x.clearAssertionStatus() + | + """.trimMargin(), + configuration + ) { + renderingStage = { rootPageNode, _ -> + assertNotNull((rootPageNode.children.single().children.single() as MemberPageNode) + .content + .dfs { node -> + node is ContentDRILink && + node.address.toString() == "parser//test/#java.lang.ClassLoader/PointingToDeclaration/"} + ) + } + } + } + + @Test + fun returnTypeShouldHaveLinkToOuterClassFromInner() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin") + displayName = "JVM" + } + } + } + //This does not contain a package to check for situation when the package has to be artificially generated + testInline( + """ + |/src/main/kotlin/parser/Test.kt + | + |class Outer<OUTER> { + | inner class Inner<INNER> { + | fun foo(): OUTER = TODO() + | } + |} + """.trimMargin(), + configuration + ) { + renderingStage = { rootPageNode, _ -> + val root = rootPageNode.children.single().children.single() as ClasslikePageNode + val innerClass = root.children.first { it is ClasslikePageNode } + val foo = innerClass.children.first { it.name == "foo" } as MemberPageNode + + assertEquals(root.dri.first().toString(), "[JVM root]/Outer///PointingToDeclaration/") + assertNotNull(foo.content.dfs { it is ContentDRILink && it.address.toString() == root.dri.first().toString() } ) + } + } + } +} diff --git a/plugins/base/src/test/kotlin/markdown/ParserTest.kt b/plugins/base/src/test/kotlin/markdown/ParserTest.kt new file mode 100644 index 00000000..ee6170c2 --- /dev/null +++ b/plugins/base/src/test/kotlin/markdown/ParserTest.kt @@ -0,0 +1,1123 @@ +package org.jetbrains.dokka.tests + +import markdown.KDocTest +import org.jetbrains.dokka.model.doc.* +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test + + +class ParserTest : KDocTest() { + + @Test + fun `Simple text`() { + val kdoc = """ + | This is simple test of string + | Next line + """.trimMargin() + val expectedDocumentationNode = DocumentationNode( + listOf( + Description( + P(listOf(Text("This is simple test of string Next line"))) + ) + ) + ) + executeTest(kdoc, expectedDocumentationNode) + } + + @Test + fun `Simple text with new line`() { + val kdoc = """ + | This is simple test of string\ + | Next line + """.trimMargin() + val expectedDocumentationNode = DocumentationNode( + listOf( + Description( + P( + listOf( + Text("This is simple test of string"), + Br, + Text("Next line") + ) + ) + ) + ) + ) + executeTest(kdoc, expectedDocumentationNode) + } + + @Test + fun `Text with Bold and Emphasis decorators`() { + val kdoc = """ + | This is **simple** test of _string_ + | Next **_line_** + """.trimMargin() + val expectedDocumentationNode = DocumentationNode( + listOf( + Description( + P( + listOf( + Text("This is "), + B(listOf(Text("simple"))), + Text(" test of "), + I(listOf(Text("string"))), + Text(" Next "), + B(listOf(I(listOf(Text("line"))))) + ) + ) + ) + ) + ) + executeTest(kdoc, expectedDocumentationNode) + } + + @Test + fun `Text with Colon`() { + val kdoc = """ + | This is simple text with: colon! + """.trimMargin() + val expectedDocumentationNode = DocumentationNode( + listOf( + Description( + P(listOf(Text("This is simple text with: colon!"))) + ) + ) + ) + executeTest(kdoc, expectedDocumentationNode) + } + + @Test + fun `Multilined text`() { + val kdoc = """ + | Text + | and + | String + """.trimMargin() + val expectedDocumentationNode = DocumentationNode( + listOf( + Description( + P(listOf(Text("Text and String"))) + ) + ) + ) + executeTest(kdoc, expectedDocumentationNode) + } + + @Test + fun `Paragraphs`() { + val kdoc = """ + | Paragraph number + | one + | + | Paragraph\ + | number two + """.trimMargin() + val expectedDocumentationNode = DocumentationNode( + listOf( + Description( + P( + listOf( + P(listOf(Text("Paragraph number one"))), + P(listOf(Text("Paragraph"), Br, Text("number two"))) + ) + ) + ) + ) + ) + executeTest(kdoc, expectedDocumentationNode) + } + + @Test + fun `Emphasis with star`() { + val kdoc = " *text*" + val expectedDocumentationNode = DocumentationNode( + listOf( + Description( + P(listOf(I(listOf(Text("text"))))) + ) + ) + ) + executeTest(kdoc, expectedDocumentationNode) + } + + @Test + fun `Underscores that are not Emphasis`() { + val kdoc = "text_with_underscores" + val expectedDocumentationNode = DocumentationNode( + listOf( + Description( + P(listOf(Text("text_with_underscores"))) + ) + ) + ) + executeTest(kdoc, expectedDocumentationNode) + } + + @Test + fun `Emphasis with underscores`() { + val kdoc = "_text_" + val expectedDocumentationNode = DocumentationNode( + listOf( + Description( + P(listOf(I(listOf(Text("text"))))) + ) + ) + ) + executeTest(kdoc, expectedDocumentationNode) + } + + @Test + fun `Embedded star`() { + val kdoc = "Embedded*Star" + val expectedDocumentationNode = DocumentationNode( + listOf( + Description( + P(listOf(Text("Embedded*Star"))) + ) + ) + ) + executeTest(kdoc, expectedDocumentationNode) + } + + + @Test + fun `Unordered list`() { + val kdoc = """ + | * list item 1 + | * list item 2 + """.trimMargin() + val expectedDocumentationNode = DocumentationNode( + listOf( + Description( + Ul( + listOf( + Li(listOf(P(listOf(Text("list item 1"))))), + Li(listOf(P(listOf(Text("list item 2"))))) + ) + ) + ) + ) + ) + executeTest(kdoc, expectedDocumentationNode) + } + + @Test + fun `Unordered list with multilines`() { + val kdoc = """ + | * list item 1 + | continue 1 + | * list item 2\ + | continue 2 + """.trimMargin() + val expectedDocumentationNode = DocumentationNode( + listOf( + Description( + Ul( + listOf( + Li(listOf(P(listOf(Text("list item 1 continue 1"))))), + Li(listOf(P(listOf(Text("list item 2"), Br, Text("continue 2"))))) + ) + ) + ) + ) + ) + executeTest(kdoc, expectedDocumentationNode) + } + + @Test + fun `Unordered list with Bold`() { + val kdoc = """ + | * list **item** 1 + | continue 1 + | * list __item__ 2 + | continue 2 + """.trimMargin() + val expectedDocumentationNode = DocumentationNode( + listOf( + Description( + Ul( + listOf( + Li( + listOf( + P( + listOf( + Text("list "), + B(listOf(Text("item"))), + Text(" 1 continue 1") + ) + ) + ) + ), + Li( + listOf( + P( + listOf( + Text("list "), + B(listOf(Text("item"))), + Text(" 2 continue 2") + ) + ) + ) + ) + ) + ) + ) + ) + ) + executeTest(kdoc, expectedDocumentationNode) + } + + @Test + fun `Unordered list with nested bullets`() { + val kdoc = """ + | * Outer first + | Outer next line + | * Outer second + | - Middle first + | Middle next line + | - Middle second + | + Inner first + | Inner next line + | - Middle third + | * Outer third + | + | New paragraph""".trimMargin() + val expectedDocumentationNode = DocumentationNode( + listOf( + Description( + P( + listOf( + Ul( + listOf( + Li(listOf(P(listOf(Text("Outer first Outer next line"))))), + Li(listOf(P(listOf(Text("Outer second"))))), + Ul( + listOf( + Li(listOf(P(listOf(Text("Middle first Middle next line"))))), + Li(listOf(P(listOf(Text("Middle second"))))), + Ul( + listOf( + Li(listOf(P(listOf(Text("Inner first Inner next line"))))) + ) + ), + Li(listOf(P(listOf(Text("Middle third"))))) + ) + ), + Li(listOf(P(listOf(Text("Outer third"))))) + ) + ), + P(listOf(Text("New paragraph"))) + ) + ) + ) + ) + ) + executeTest(kdoc, expectedDocumentationNode) + } + + @Test + fun `Ordered list`() { + val kdoc = """ + | 1. list item 1 + | 2. list item 2 + """.trimMargin() + val expectedDocumentationNode = DocumentationNode( + listOf( + Description( + Ol( + listOf( + Li(listOf(P(listOf(Text("list item 1"))))), + Li(listOf(P(listOf(Text("list item 2"))))) + ), + mapOf("start" to "1") + ) + ) + ) + ) + executeTest(kdoc, expectedDocumentationNode) + } + + + @Test + fun `Ordered list beginning from other number`() { + val kdoc = """ + | 9. list item 1 + | 12. list item 2 + """.trimMargin() + val expectedDocumentationNode = DocumentationNode( + listOf( + Description( + Ol( + listOf( + Li(listOf(P(listOf(Text("list item 1"))))), + Li(listOf(P(listOf(Text("list item 2"))))) + ), + mapOf("start" to "9") + ) + ) + ) + ) + executeTest(kdoc, expectedDocumentationNode) + } + + @Test + fun `Ordered list with multilines`() { + val kdoc = """ + | 2. list item 1 + | continue 1 + | 3. list item 2 + | continue 2 + """.trimMargin() + val expectedDocumentationNode = DocumentationNode( + listOf( + Description( + Ol( + listOf( + Li(listOf(P(listOf(Text("list item 1 continue 1"))))), + Li(listOf(P(listOf(Text("list item 2 continue 2"))))) + ), + mapOf("start" to "2") + ) + ) + ) + ) + executeTest(kdoc, expectedDocumentationNode) + } + + @Test + fun `Ordered list with Bold`() { + val kdoc = """ + | 1. list **item** 1 + | continue 1 + | 2. list __item__ 2 + | continue 2 + """.trimMargin() + val expectedDocumentationNode = DocumentationNode( + listOf( + Description( + Ol( + listOf( + Li( + listOf( + P( + listOf( + Text("list "), + B(listOf(Text("item"))), + Text(" 1 continue 1") + ) + ) + ) + ), + Li( + listOf( + P( + listOf( + Text("list "), + B(listOf(Text("item"))), + Text(" 2 continue 2") + ) + ) + ) + ) + ), + mapOf("start" to "1") + ) + ) + ) + ) + executeTest(kdoc, expectedDocumentationNode) + } + + @Test + fun `Ordered list with nested bullets`() { + val kdoc = """ + | 1. Outer first + | Outer next line + | 2. Outer second + | 1. Middle first + | Middle next line + | 2. Middle second + | 1. Inner first + | Inner next line + | 5. Middle third + | 4. Outer third + | + | New paragraph""".trimMargin() + val expectedDocumentationNode = DocumentationNode( + listOf( + Description( + P( + listOf( + Ol( + listOf( + Li(listOf(P(listOf(Text("Outer first Outer next line"))))), + Li(listOf(P(listOf(Text("Outer second"))))), + Ol( + listOf( + Li(listOf(P(listOf(Text("Middle first Middle next line"))))), + Li(listOf(P(listOf(Text("Middle second"))))), + Ol( + listOf( + Li(listOf(P(listOf(Text("Inner first Inner next line"))))) + ), + mapOf("start" to "1") + ), + Li(listOf(P(listOf(Text("Middle third"))))) + ), + mapOf("start" to "1") + ), + Li(listOf(P(listOf(Text("Outer third"))))) + ), + mapOf("start" to "1") + ), + P(listOf(Text("New paragraph"))) + ) + ) + ) + ) + ) + executeTest(kdoc, expectedDocumentationNode) + } + + @Test + fun `Ordered nested in Unordered nested in Ordered list`() { + val kdoc = """ + | 1. Outer first + | Outer next line + | 2. Outer second + | + Middle first + | Middle next line + | + Middle second + | 1. Inner first + | Inner next line + | + Middle third + | 4. Outer third + | + | New paragraph""".trimMargin() + val expectedDocumentationNode = DocumentationNode( + listOf( + Description( + P( + listOf( + Ol( + listOf( + Li(listOf(P(listOf(Text("Outer first Outer next line"))))), + Li(listOf(P(listOf(Text("Outer second"))))), + Ul( + listOf( + Li(listOf(P(listOf(Text("Middle first Middle next line"))))), + Li(listOf(P(listOf(Text("Middle second"))))), + Ol( + listOf( + Li(listOf(P(listOf(Text("Inner first Inner next line"))))) + ), + mapOf("start" to "1") + ), + Li(listOf(P(listOf(Text("Middle third"))))) + ) + ), + Li(listOf(P(listOf(Text("Outer third"))))) + ), + mapOf("start" to "1") + ), + P(listOf(Text("New paragraph"))) + ) + ) + ) + ) + ) + executeTest(kdoc, expectedDocumentationNode) + } + + @Test + fun `Header and two paragraphs`() { + val kdoc = """ + | # Header 1 + | Following text + | + | New paragraph + """.trimMargin() + val expectedDocumentationNode = DocumentationNode( + listOf( + Description( + P( + listOf( + H1(listOf(Text("Header 1"))), + P(listOf(Text("Following text"))), + P(listOf(Text("New paragraph"))) + ) + ) + ) + ) + ) + executeTest(kdoc, expectedDocumentationNode) + } + + @Disabled //TODO: ATX_2 to ATX_6 and sometimes ATX_1 from jetbrains parser consumes white space. Need to handle it in their library + @Test + fun `All headers`() { + val kdoc = """ + | # Header 1 + | Text 1 + | ## Header 2 + | Text 2 + | ### Header 3 + | Text 3 + | #### Header 4 + | Text 4 + | ##### Header 5 + | Text 5 + | ###### Header 6 + | Text 6 + """.trimMargin() + val expectedDocumentationNode = DocumentationNode( + listOf( + Description( + P( + listOf( + H1(listOf(Text("Header 1"))), + P(listOf(Text("Text 1"))), + H2(listOf(Text("Header 2"))), + P(listOf(Text("Text 2"))), + H3(listOf(Text("Header 3"))), + P(listOf(Text("Text 3"))), + H4(listOf(Text("Header 4"))), + P(listOf(Text("Text 4"))), + H5(listOf(Text("Header 5"))), + P(listOf(Text("Text 5"))), + H6(listOf(Text("Header 6"))), + P(listOf(Text("Text 6"))) + ) + ) + ) + ) + ) + executeTest(kdoc, expectedDocumentationNode) + } + + @Test + fun `Bold New Line Bold`() { + val kdoc = """ + | **line 1**\ + | **line 2** + """.trimMargin() + val expectedDocumentationNode = DocumentationNode( + listOf( + Description( + P( + listOf( + B(listOf(Text("line 1"))), + Br, + B(listOf(Text("line 2"))) + ) + ) + ) + ) + ) + executeTest(kdoc, expectedDocumentationNode) + } + + @Test + fun `Horizontal rule`() { + val kdoc = """ + | *** + | text 1 + | ___ + | text 2 + | *** + | text 3 + | ___ + | text 4 + | *** + """.trimMargin() + val expectedDocumentationNode = DocumentationNode( + listOf( + Description( + P( + listOf( + HorizontalRule, + P(listOf(Text("text 1"))), + HorizontalRule, + P(listOf(Text("text 2"))), + HorizontalRule, + P(listOf(Text("text 3"))), + HorizontalRule, + P(listOf(Text("text 4"))), + HorizontalRule + ) + ) + ) + ) + ) + executeTest(kdoc, expectedDocumentationNode) + } + + @Test + fun `Blockquote`() { + val kdoc = """ + | > Blockquotes are very handy in email to emulate reply text. + | > This line is part of the same quote. + | + | Quote break. + | + | > Quote + """.trimMargin() + val expectedDocumentationNode = DocumentationNode( + listOf( + Description( + P( + listOf( + BlockQuote( + listOf( + P( + listOf( + Text("Blockquotes are very handy in email to emulate reply text. This line is part of the same quote.") + ) + ) + ) + ), + P(listOf(Text("Quote break."))), + BlockQuote( + listOf( + P(listOf(Text("Quote"))) + ) + ) + ) + ) + ) + ) + ) + executeTest(kdoc, expectedDocumentationNode) + } + + + @Test + fun `Blockquote nested`() { + val kdoc = """ + | > text 1 + | > text 2 + | >> text 3 + | >> text 4 + | > + | > text 5 + | + | Quote break. + | + | > Quote + """.trimMargin() + val expectedDocumentationNode = DocumentationNode( + listOf( + Description( + P( + listOf( + BlockQuote( + listOf( + P(listOf(Text("text 1 text 2"))), + BlockQuote( + listOf( + P(listOf(Text("text 3 text 4"))) + ) + ), + P(listOf(Text("text 5"))) + ) + ), + P(listOf(Text("Quote break."))), + BlockQuote( + listOf( + P(listOf(Text("Quote"))) + ) + ) + ) + ) + ) + ) + ) + executeTest(kdoc, expectedDocumentationNode) + } + + @Disabled //TODO: Again ATX_1 consumes white space + @Test + fun `Blockquote nested with fancy text enhancement`() { + val kdoc = """ + | > text **1** + | > text 2 + | >> # text 3 + | >> * text 4 + | >> * text 5 + | > + | > text 6 + | + | Quote break. + | + | > Quote + """.trimMargin() + val expectedDocumentationNode = DocumentationNode( + listOf( + Description( + P( + listOf( + BlockQuote( + listOf( + P( + listOf( + Text("text "), + B(listOf(Text("1"))), + Text("\ntext 2") + ) + ), + BlockQuote( + listOf( + H1(listOf(Text("text 3"))), + Ul( + listOf( + Li(listOf(P(listOf(Text("text 4"))))), + Ul( + listOf( + Li(listOf(P(listOf(Text("text 5"))))) + ) + ) + ) + ) + ) + ), + P(listOf(Text("text 6"))) + ) + ), + P(listOf(Text("Quote break."))), + BlockQuote( + listOf( + P(listOf(Text("Quote"))) + ) + ) + ) + ) + ) + ) + ) + executeTest(kdoc, expectedDocumentationNode) + } + + @Test + fun `Simple Code Block`() { + val kdoc = """ + | `Some code` + | Sample text + """.trimMargin() + val expectedDocumentationNode = DocumentationNode( + listOf( + Description( + P( + listOf( + CodeInline(listOf(Text("Some code"))), + Text(" Sample text") + ) + ) + ) + ) + ) + executeTest(kdoc, expectedDocumentationNode) + } + + @Test + fun `Multilined Code Block`() { + val kdoc = """ + | ```kotlin + | val x: Int = 0 + | val y: String = "Text" + | + | val z: Boolean = true + | for(i in 0..10) { + | println(i) + | } + | ``` + | Sample text + """.trimMargin() + val expectedDocumentationNode = DocumentationNode( + listOf( + Description( + P( + listOf( + CodeBlock( + listOf( + Text("val x: Int = 0"), Br, + Text("val y: String = \"Text\""), Br, Br, + Text(" val z: Boolean = true"), Br, + Text("for(i in 0..10) {"), Br, + Text(" println(i)"), Br, + Text("}") + ), + mapOf("lang" to "kotlin") + ), + P(listOf(Text("Sample text"))) + ) + ) + ) + ) + ) + executeTest(kdoc, expectedDocumentationNode) + } + + + @Test + fun `Inline link`() { + val kdoc = """ + | [I'm an inline-style link](https://www.google.com) + """.trimMargin() + val expectedDocumentationNode = DocumentationNode( + listOf( + Description( + P( + listOf( + A( + listOf(Text("I'm an inline-style link")), + mapOf("href" to "https://www.google.com") + ) + ) + ) + ) + ) + ) + executeTest(kdoc, expectedDocumentationNode) + } + + @Test + fun `Inline link with title`() { + val kdoc = """ + | [I'm an inline-style link with title](https://www.google.com "Google's Homepage") + """.trimMargin() + val expectedDocumentationNode = DocumentationNode( + listOf( + Description( + P( + listOf( + A( + listOf(Text("I'm an inline-style link with title")), + mapOf("href" to "https://www.google.com", "title" to "Google's Homepage") + ) + ) + ) + ) + ) + ) + executeTest(kdoc, expectedDocumentationNode) + } + + @Test + fun `Full reference link`() { + val kdoc = """ + | [I'm a reference-style link][Arbitrary case-insensitive reference text] + | + | [arbitrary case-insensitive reference text]: https://www.mozilla.org + """.trimMargin() + val expectedDocumentationNode = DocumentationNode( + listOf( + Description( + P( + listOf( + P( + listOf( + A( + listOf(Text("I'm a reference-style link")), + mapOf("href" to "https://www.mozilla.org") + ) + ) + ) + ) + ) + ) + ) + ) + executeTest(kdoc, expectedDocumentationNode) + } + + @Test + fun `Full reference link with number`() { + val kdoc = """ + | [You can use numbers for reference-style link definitions][1] + | + | [1]: http://slashdot.org + """.trimMargin() + val expectedDocumentationNode = DocumentationNode( + listOf( + Description( + P( + listOf( + P( + listOf( + A( + listOf(Text("You can use numbers for reference-style link definitions")), + mapOf("href" to "http://slashdot.org") + ) + ) + ) + ) + ) + ) + ) + ) + executeTest(kdoc, expectedDocumentationNode) + } + + @Test + fun `Short reference link`() { + val kdoc = """ + | Or leave it empty and use the [link text itself]. + | + | [link text itself]: http://www.reddit.com + """.trimMargin() + val expectedDocumentationNode = DocumentationNode( + listOf( + Description( + P( + listOf( + P( + listOf( + Text("Or leave it empty and use the "), + A( + listOf(Text("link text itself")), + mapOf("href" to "http://www.reddit.com") + ), + Text(".") + ) + ) + ) + ) + ) + ) + ) + executeTest(kdoc, expectedDocumentationNode) + } + + @Test + fun `Autolink`() { + val kdoc = """ + | URLs and URLs in angle brackets will automatically get turned into links. + | http://www.example.com or <http://www.example.com> and sometimes + | example.com (but not on Github, for example). + """.trimMargin() + val expectedDocumentationNode = DocumentationNode( + listOf( + Description( + P( + listOf( + Text("URLs and URLs in angle brackets will automatically get turned into links. http://www.example.com or "), + A( + listOf(Text("http://www.example.com")), + mapOf("href" to "http://www.example.com") + ), + Text(" and sometimes example.com (but not on Github, for example).") + ) + ) + ) + ) + ) + executeTest(kdoc, expectedDocumentationNode) + } + + @Test + fun `Various links`() { + val kdoc = """ + | [I'm an inline-style link](https://www.google.com) + | + | [I'm an inline-style link with title](https://www.google.com "Google's Homepage") + | + | [I'm a reference-style link][Arbitrary case-insensitive reference text] + | + | [You can use numbers for reference-style link definitions][1] + | + | Or leave it empty and use the [link text itself]. + | + | URLs and URLs in angle brackets will automatically get turned into links. + | http://www.example.com or <http://www.example.com> and sometimes + | example.com (but not on Github, for example). + | + | Some text to show that the reference links can follow later. + | + | [arbitrary case-insensitive reference text]: https://www.mozilla.org + | [1]: http://slashdot.org + | [link text itself]: http://www.reddit.com + """.trimMargin() + val expectedDocumentationNode = DocumentationNode( + listOf( + Description( + P( + listOf( + P( + listOf( + A( + listOf(Text("I'm an inline-style link")), + mapOf("href" to "https://www.google.com") + ) + ) + ), + P( + listOf( + A( + listOf(Text("I'm an inline-style link with title")), + mapOf("href" to "https://www.google.com", "title" to "Google's Homepage") + ) + ) + ), + P( + listOf( + A( + listOf(Text("I'm a reference-style link")), + mapOf("href" to "https://www.mozilla.org") + ) + ) + ), + P( + listOf( + A( + listOf(Text("You can use numbers for reference-style link definitions")), + mapOf("href" to "http://slashdot.org") + ) + ) + ), + P( + listOf( + Text("Or leave it empty and use the "), + A( + listOf(Text("link text itself")), + mapOf("href" to "http://www.reddit.com") + ), + Text(".") + ) + ), + P( + listOf( + Text("URLs and URLs in angle brackets will automatically get turned into links. http://www.example.com or "), + A( + listOf(Text("http://www.example.com")), + mapOf("href" to "http://www.example.com") + ), + Text(" and sometimes example.com (but not on Github, for example).") + ) + ), + P(listOf(Text("Some text to show that the reference links can follow later."))) + ) + ) + ) + ) + ) + executeTest(kdoc, expectedDocumentationNode) + } + + @Test + fun `Windows Carriage Return Line Feed`() { + val kdoc = "text\r\ntext" + val expectedDocumentationNode = DocumentationNode( + listOf( + Description( + P( + listOf( + Text("text text") + ) + ) + ) + ) + ) + executeTest(kdoc, expectedDocumentationNode) + } +} + diff --git a/plugins/base/src/test/kotlin/model/ClassesTest.kt b/plugins/base/src/test/kotlin/model/ClassesTest.kt new file mode 100644 index 00000000..5dc8812e --- /dev/null +++ b/plugins/base/src/test/kotlin/model/ClassesTest.kt @@ -0,0 +1,533 @@ +package model + +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.links.sureClassNames +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.KotlinModifier.* +import org.junit.jupiter.api.Assertions.assertNull +import org.junit.jupiter.api.Test +import utils.AbstractModelTest +import utils.assertNotNull +import utils.name +import utils.supers + + +class ClassesTest : AbstractModelTest("/src/main/kotlin/classes/Test.kt", "classes") { + + @Test + fun emptyClass() { + inlineModelTest( + """ + |class Klass {}""" + ) { + with((this / "classes" / "Klass").cast<DClass>()) { + name equals "Klass" + children counts 4 + } + } + } + + @Test + fun emptyObject() { + inlineModelTest( + """ + |object Obj {} + """ + ) { + with((this / "classes" / "Obj").cast<DObject>()) { + name equals "Obj" + children counts 3 + } + } + } + + @Test + fun classWithConstructor() { + inlineModelTest( + """ + |class Klass(name: String) + """ + ) { + with((this / "classes" / "Klass").cast<DClass>()) { + name equals "Klass" + children counts 4 + + with(constructors.firstOrNull().assertNotNull("Constructor")) { + visibility.values allEquals KotlinVisibility.Public + parameters counts 1 + with(parameters.firstOrNull().assertNotNull("Constructor parameter")) { + name equals "name" + type.name equals "String" + } + } + + } + } + } + + @Test + fun classWithFunction() { + inlineModelTest( + """ + |class Klass { + | fun fn() {} + |} + """ + ) { + with((this / "classes" / "Klass").cast<DClass>()) { + name equals "Klass" + children counts 5 + + with((this / "fn").cast<DFunction>()) { + type.name equals "Unit" + parameters counts 0 + visibility.values allEquals KotlinVisibility.Public + } + } + } + } + + @Test + fun classWithProperty() { + inlineModelTest( + """ + |class Klass { + | val name: String = "" + |} + """ + ) { + with((this / "classes" / "Klass").cast<DClass>()) { + name equals "Klass" + children counts 5 + + with((this / "name").cast<DProperty>()) { + name equals "name" + // TODO property name + } + } + } + } + + @Test + fun classWithCompanionObject() { + inlineModelTest( + """ + |class Klass() { + | companion object { + | val x = 1 + | fun foo() {} + | } + |} + """ + ) { + with((this / "classes" / "Klass").cast<DClass>()) { + name equals "Klass" + children counts 5 + + with((this / "Companion").cast<DObject>()) { + name equals "Companion" + children counts 5 + + with((this / "x").cast<DProperty>()) { + name equals "x" + } + + with((this / "foo").cast<DFunction>()) { + name equals "foo" + parameters counts 0 + type.name equals "Unit" + } + } + } + } + } + + @Test + fun dataClass() { + inlineModelTest( + """ + |data class Klass() {} + """ + ) { + with((this / "classes" / "Klass").cast<DClass>()) { + name equals "Klass" + visibility.values allEquals KotlinVisibility.Public + with(extra[AdditionalModifiers]!!.content.entries.single().value.assertNotNull("Extras")) { + this counts 1 + first() equals ExtraModifiers.KotlinOnlyModifiers.Data + } + } + } + } + + @Test + fun sealedClass() { + inlineModelTest( + """ + |sealed class Klass() {} + """ + ) { + with((this / "classes" / "Klass").cast<DClass>()) { + name equals "Klass" + modifier.values.forEach { it equals Sealed } + } + } + } + + @Test + fun annotatedClassWithAnnotationParameters() { + inlineModelTest( + """ + |@Deprecated("should no longer be used") class Foo() {} + """ + ) { + with((this / "classes" / "Foo").cast<DClass>()) { + with(extra[Annotations]!!.content.entries.single().value.assertNotNull("Annotations")) { + this counts 1 + with(first()) { + dri.classNames equals "Deprecated" + params.entries counts 1 + (params["message"].assertNotNull("message") as StringValue).value equals "\"should no longer be used\"" + } + } + } + } + } + + @Test + fun notOpenClass() { + inlineModelTest( + """ + |open class C() { + | open fun f() {} + |} + | + |class D() : C() { + | override fun f() {} + |} + """ + ) { + val C = (this / "classes" / "C").cast<DClass>() + val D = (this / "classes" / "D").cast<DClass>() + + with(C) { + modifier.values.forEach { it equals Open } + with((this / "f").cast<DFunction>()) { + modifier.values.forEach { it equals Open } + } + } + with(D) { + modifier.values.forEach { it equals Final } + with((this / "f").cast<DFunction>()) { + modifier.values.forEach { it equals Open } + } + D.supertypes.flatMap { it.component2() }.firstOrNull()?.dri equals C.dri + } + } + } + + @Test + fun indirectOverride() { + inlineModelTest( + """ + |abstract class C() { + | abstract fun foo() + |} + | + |abstract class D(): C() + | + |class E(): D() { + | override fun foo() {} + |} + """ + ) { + val C = (this / "classes" / "C").cast<DClass>() + val D = (this / "classes" / "D").cast<DClass>() + val E = (this / "classes" / "E").cast<DClass>() + + with(C) { + modifier.values.forEach { it equals Abstract } + ((this / "foo").cast<DFunction>()).modifier.values.forEach { it equals Abstract } + } + + with(D) { + modifier.values.forEach { it equals Abstract } + } + + with(E) { + modifier.values.forEach { it equals Final } + + } + D.supers.single().dri equals C.dri + E.supers.single().dri equals D.dri + } + } + + @Test + fun innerClass() { + inlineModelTest( + """ + |class C { + | inner class D {} + |} + """ + ) { + with((this / "classes" / "C").cast<DClass>()) { + + with((this / "D").cast<DClass>()) { + with(extra[AdditionalModifiers]!!.content.entries.single().value.assertNotNull("AdditionalModifiers")) { + this counts 1 + first() equals ExtraModifiers.KotlinOnlyModifiers.Inner + } + } + } + } + } + + @Test + fun companionObjectExtension() { + inlineModelTest( + """ + |class Klass { + | companion object Default {} + |} + | + |/** + | * The def + | */ + |val Klass.Default.x: Int get() = 1 + """ + ) { + with((this / "classes" / "Klass").cast<DClass>()) { + name equals "Klass" + + with((this / "Default").cast<DObject>()) { + name equals "Default" + // TODO extensions + } + } + } + } + +// @Test fun companionObjectExtension() { +// checkSourceExistsAndVerifyModel("testdata/classes/companionObjectExtension.kt", defaultModelConfig) { model -> +// val pkg = model.members.single() +// val cls = pkg.members.single { it.name == "Foo" } +// val extensions = cls.extensions.filter { it.kind == NodeKind.CompanionObjectProperty } +// assertEquals(1, extensions.size) +// } +// } + + @Test + fun secondaryConstructor() { + inlineModelTest( + """ + |class C() { + | /** This is a secondary constructor. */ + | constructor(s: String): this() {} + |} + """ + ) { + with((this / "classes" / "C").cast<DClass>()) { + name equals "C" + constructors counts 2 + + constructors.map { it.name } allEquals "<init>" + + with(constructors.find { it.parameters.isNullOrEmpty() } notNull "C()") { + parameters counts 0 + } + + with(constructors.find { it.parameters.isNotEmpty() } notNull "C(String)") { + parameters counts 1 + with(parameters.firstOrNull() notNull "Constructor parameter") { + name equals "s" + type.name equals "String" + } + } + } + } + } + + @Test + fun sinceKotlin() { + inlineModelTest( + """ + |/** + | * Useful + | */ + |@SinceKotlin("1.1") + |class C + """ + ) { + with((this / "classes" / "C").cast<DClass>()) { + with(extra[Annotations]!!.content.entries.single().value.assertNotNull("Annotations")) { + this counts 1 + with(first()) { + dri.classNames equals "SinceKotlin" + params.entries counts 1 + (params["version"].assertNotNull("version") as StringValue).value equals "\"1.1\"" + } + } + } + } + } + + @Test + fun privateCompanionObject() { + inlineModelTest( + """ + |class Klass { + | private companion object { + | fun fn() {} + | val a = 0 + | } + |} + """ + ) { + with((this / "classes" / "Klass").cast<DClass>()) { + name equals "Klass" + assertNull(companion, "Companion should not be visible by default") + } + } + } + + @Test + fun companionObject() { + inlineModelTest( + """ + |class Klass { + | companion object { + | fun fn() {} + | val a = 0 + | } + |} + """ + ) { + with((this / "classes" / "Klass").cast<DClass>()) { + name equals "Klass" + with((this / "Companion").cast<DObject>()) { + name equals "Companion" + visibility.values allEquals KotlinVisibility.Public + + with((this / "fn").cast<DFunction>()) { + name equals "fn" + parameters counts 0 + receiver equals null + } + } + } + } + } + + @Test + fun annotatedClass() { + inlineModelTest( + """@Suppress("abc") class Foo() {}""" + ) { + with((this / "classes" / "Foo").cast<DClass>()) { + with(extra[Annotations]?.content?.values?.firstOrNull()?.firstOrNull().assertNotNull("annotations")) { + dri.toString() equals "kotlin/Suppress///PointingToDeclaration/" + (params["names"].assertNotNull("param") as ArrayValue).value equals listOf(StringValue("\"abc\"")) + } + } + } + } + + @Test + fun javaAnnotationClass() { + inlineModelTest( + """ + |import java.lang.annotation.Retention + |import java.lang.annotation.RetentionPolicy + | + |@Retention(RetentionPolicy.SOURCE) + |public annotation class throws() + """ + ) { + with((this / "classes" / "throws").cast<DAnnotation>()) { + with(extra[Annotations]!!.content.entries.single().value.assertNotNull("Annotations")) { + this counts 1 + with(first()) { + dri.classNames equals "Retention" + params["value"].assertNotNull("value") equals EnumValue( + "RetentionPolicy.SOURCE", + DRI("java.lang.annotation", "RetentionPolicy.SOURCE") + ) + } + } + } + } + } + + @Test fun genericAnnotationClass() { + inlineModelTest( + """annotation class Foo<A,B,C,D:Number>() {}""" + ) { + with((this / "classes" / "Foo").cast<DAnnotation>()){ + generics.map { it.name to it.bounds.first().name } equals listOf("A" to "Any", "B" to "Any", "C" to "Any", "D" to "Number") + } + } + } + + @Test fun nestedGenericClasses(){ + inlineModelTest( + """ + |class Outer<OUTER> { + | inner class Inner<INNER, T : OUTER> { } + |} + """.trimMargin() + ){ + with((this / "classes" / "Outer").cast<DClass>()){ + val inner = classlikes.single().cast<DClass>() + inner.generics.map { it.name to it.bounds.first().name } equals listOf("INNER" to "Any", "T" to "OUTER") + } + } + } + + @Test fun allImplementedInterfaces() { + inlineModelTest( + """ + | interface Highest { } + | open class HighestImpl: Highest { } + | interface Lower { } + | interface LowerImplInterface: Lower { } + | class Tested : HighestImpl(), LowerImplInterface { } + """.trimIndent() + ){ + with((this / "classes" / "Tested").cast<DClass>()){ + extra[ImplementedInterfaces]?.interfaces?.entries?.single()?.value?.map { it.sureClassNames }?.sorted() equals listOf("Highest", "Lower", "LowerImplInterface").sorted() + } + } + } + + @Test fun multipleClassInheritance() { + inlineModelTest( + """ + | open class A { } + | open class B: A() { } + | class Tested : B() { } + """.trimIndent() + ) { + with((this / "classes" / "Tested").cast<DClass>()) { + supertypes.entries.single().value.map { it.dri.sureClassNames }.single() equals "B" + } + } + } + + @Test fun multipleClassInheritanceWithInterface(){ + inlineModelTest( + """ + | open class A { } + | open class B: A() { } + | interface X { } + | interface Y : X { } + | class Tested : B(), Y { } + """.trimIndent() + ){ + with((this / "classes" / "Tested").cast<DClass>()) { + supertypes.entries.single().value.map { it.dri.sureClassNames to it.kind }.sortedBy { it.first } equals listOf("B" to KotlinClassKindTypes.CLASS, "Y" to KotlinClassKindTypes.INTERFACE) + } + } + } +}
\ No newline at end of file diff --git a/plugins/base/src/test/kotlin/model/CommentTest.kt b/plugins/base/src/test/kotlin/model/CommentTest.kt new file mode 100644 index 00000000..c1da8ee0 --- /dev/null +++ b/plugins/base/src/test/kotlin/model/CommentTest.kt @@ -0,0 +1,332 @@ +package model + +import org.jetbrains.dokka.model.DProperty +import org.jetbrains.dokka.model.doc.CustomTagWrapper +import org.jetbrains.dokka.model.doc.Text +import org.junit.jupiter.api.Test +import utils.* + +class CommentTest : AbstractModelTest("/src/main/kotlin/comment/Test.kt", "comment") { + + @Test + fun codeBlockComment() { + inlineModelTest( + """ + |/** + | * ```brainfuck + | * ++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>. + | * ``` + | */ + |val prop1 = "" + | + | + |/** + | * ``` + | * a + b - c + | * ``` + | */ + |val prop2 = "" + """ + ) { + with((this / "comment" / "prop1").cast<DProperty>()) { + name equals "prop1" + with(this.docs().firstOrNull()?.root.assertNotNull("Code")) { + (children.firstOrNull() as? Text) + ?.body equals "++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>." + + params["lang"] equals "brainfuck" + } + } + with((this / "comment" / "prop2").cast<DProperty>()) { + name equals "prop2" + comments() equals "a + b - c" + } + } + } + + @Test + fun emptyDoc() { + inlineModelTest( + """ + val property = "test" + """ + ) { + with((this / "comment" / "property").cast<DProperty>()) { + name equals "property" + comments() equals "" + } + } + } + + @Test + fun emptyDocButComment() { + inlineModelTest( + """ + |/* comment */ + |val property = "test" + |fun tst() = property + """ + ) { + val p = this + with((this / "comment" / "property").cast<DProperty>()) { + comments() equals "" + } + } + } + + @Test + fun multilineDoc() { + inlineModelTest( + """ + |/** + | * doc1 + | * + | * doc2 + | * doc3 + | */ + |val property = "test" + """ + ) { + with((this / "comment" / "property").cast<DProperty>()) { + comments() equals "doc1\ndoc2 doc3" + } + } + } + + @Test + fun multilineDocWithComment() { + inlineModelTest( + """ + |/** + | * doc1 + | * + | * doc2 + | * doc3 + | */ + |// comment + |val property = "test" + """ + ) { + with((this / "comment" / "property").cast<DProperty>()) { + comments() equals "doc1\ndoc2 doc3" + } + } + } + + @Test + fun oneLineDoc() { + inlineModelTest( + """ + |/** doc */ + |val property = "test" + """ + ) { + with((this / "comment" / "property").cast<DProperty>()) { + comments() equals "doc" + } + } + } + + @Test + fun oneLineDocWithComment() { + inlineModelTest( + """ + |/** doc */ + |// comment + |val property = "test" + """ + ) { + with((this / "comment" / "property").cast<DProperty>()) { + comments() equals "doc" + } + } + } + + @Test + fun oneLineDocWithEmptyLine() { + inlineModelTest( + """ + |/** doc */ + | + |val property = "test" + """ + ) { + with((this / "comment" / "property").cast<DProperty>()) { + comments() equals "doc" + } + } + } + + @Test + fun emptySection() { + inlineModelTest( + """ + |/** + | * Summary + | * @one + | */ + |val property = "test" + """ + ) { + with((this / "comment" / "property").cast<DProperty>()) { + comments() equals "Summary\none: []" + docs().find { it is CustomTagWrapper && it.name == "one" }.let { + with(it.assertNotNull("'one' entry")) { + root.children counts 0 + root.params.keys counts 0 + } + } + } + } + } + + @Test + fun quotes() { + inlineModelTest( + """ + |/** it's "useful" */ + |val property = "test" + """ + ) { + with((this / "comment" / "property").cast<DProperty>()) { + comments() equals """it's "useful"""" + } + } + } + + @Test + fun section1() { + inlineModelTest( + """ + |/** + | * Summary + | * @one section one + | */ + |val property = "test" + """ + ) { + with((this / "comment" / "property").cast<DProperty>()) { + comments() equals "Summary\none: [section one]" + } + } + } + + + @Test + fun section2() { + inlineModelTest( + """ + |/** + | * Summary + | * @one section one + | * @two section two + | */ + |val property = "test" + """ + ) { + with((this / "comment" / "property").cast<DProperty>()) { + comments() equals "Summary\none: [section one]\ntwo: [section two]" + } + } + } + + @Test + fun multilineSection() { + inlineModelTest( + """ + |/** + | * Summary + | * @one + | * line one + | * line two + | */ + |val property = "test" + """ + ) { + with((this / "comment" / "property").cast<DProperty>()) { + comments() equals "Summary\none: [line one line two]" + } + } + } + +// @Test todo + fun directive() { + inlineModelTest( + """ + |/** + | * Summary + | * + | * @sample example1 + | * @sample example2 + | * @sample X.example3 + | * @sample X.Y.example4 + | */ + |val property = "test" + | + |fun example1(node: String) = if (true) { + | println(property) + |} + | + |fun example2(node: String) { + | if (true) { + | println(property) + | } + |} + | + |class X { + | fun example3(node: String) { + | if (true) { + | println(property) + | } + | } + | + | class Y { + | fun example4(node: String) { + | if (true) { + | println(property) + | } + | } + | } + |} + """ + ) { + with((this / "comment" / "property").cast<DProperty>()) { + this + } + } + } + + +// @Test fun directive() { +// checkSourceExistsAndVerifyModel("testdata/comments/directive.kt", defaultModelConfig) { model -> +// with(model.members.single().members.first()) { +// assertEquals("Summary", content.summary.toTestString()) +// with (content.description) { +// assertEqualsIgnoringSeparators(""" +// |[code lang=kotlin] +// |if (true) { +// | println(property) +// |} +// |[/code] +// |[code lang=kotlin] +// |if (true) { +// | println(property) +// |} +// |[/code] +// |[code lang=kotlin] +// |if (true) { +// | println(property) +// |} +// |[/code] +// |[code lang=kotlin] +// |if (true) { +// | println(property) +// |} +// |[/code] +// |""".trimMargin(), toTestString()) +// } +// } +// } +// } + +}
\ No newline at end of file diff --git a/plugins/base/src/test/kotlin/model/FunctionsTest.kt b/plugins/base/src/test/kotlin/model/FunctionsTest.kt new file mode 100644 index 00000000..c96e7df6 --- /dev/null +++ b/plugins/base/src/test/kotlin/model/FunctionsTest.kt @@ -0,0 +1,396 @@ +package model + +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.* +import org.junit.jupiter.api.Test +import utils.AbstractModelTest +import utils.assertNotNull +import utils.comments +import utils.name + +class FunctionTest : AbstractModelTest("/src/main/kotlin/function/Test.kt", "function") { + + @Test + fun function() { + inlineModelTest( + """ + |/** + | * Function fn + | */ + |fun fn() {} + """ + ) { + with((this / "function" / "fn").cast<DFunction>()) { + name equals "fn" + type.name equals "Unit" + this.children.assertCount(0, "Function children: ") + } + } + } + + @Test + fun overloads() { + inlineModelTest( + """ + |/** + | * Function fn + | */ + |fun fn() {} + | /** + | * Function fn(Int) + | */ + |fun fn(i: Int) {} + """ + ) { + with((this / "function").cast<DPackage>()) { + val fn1 = functions.find { + it.name == "fn" && it.parameters.isNullOrEmpty() + }.assertNotNull("fn()") + val fn2 = functions.find { + it.name == "fn" && it.parameters.isNotEmpty() + }.assertNotNull("fn(Int)") + + with(fn1) { + name equals "fn" + parameters.assertCount(0) + } + + with(fn2) { + name equals "fn" + parameters.assertCount(1) + parameters.first().type.name equals "Int" + } + } + } + } + + @Test + fun functionWithReceiver() { + inlineModelTest( + """ + |/** + | * Function with receiver + | */ + |fun String.fn() {} + | + |/** + | * Function with receiver + | */ + |fun String.fn(x: Int) {} + """ + ) { + with((this / "function").cast<DPackage>()) { + val fn1 = functions.find { + it.name == "fn" && it.parameters.isNullOrEmpty() + }.assertNotNull("fn()") + val fn2 = functions.find { + it.name == "fn" && it.parameters.count() == 1 + }.assertNotNull("fn(Int)") + + with(fn1) { + name equals "fn" + parameters counts 0 + receiver.assertNotNull("fn() receiver") + } + + with(fn2) { + name equals "fn" + parameters counts 1 + receiver.assertNotNull("fn(Int) receiver") + parameters.first().type.name equals "Int" + } + } + } + } + + @Test + fun functionWithParams() { + inlineModelTest( + """ + |/** + | * Multiline + | * + | * Function + | * Documentation + | */ + |fun function(/** parameter */ x: Int) { + |} + """ + ) { + with((this / "function" / "function").cast<DFunction>()) { + comments() equals "Multiline\nFunction Documentation" + + name equals "function" + parameters counts 1 + parameters.firstOrNull().assertNotNull("Parameter: ").also { + it.name equals "x" + it.type.name equals "Int" + it.comments() equals "parameter" + } + + type.assertNotNull("Return type: ").name equals "Unit" + } + } + } + + @Test + fun functionWithNotDocumentedAnnotation() { + inlineModelTest( + """ + |@Suppress("FOO") fun f() {} + """ + ) { + with((this / "function" / "f").cast<DFunction>()) { + with(extra[Annotations]!!.content.entries.single().value.assertNotNull("Annotations")) { + this counts 1 + with(first()) { + dri.classNames equals "Suppress" + params.entries counts 1 + (params["names"].assertNotNull("param") as ArrayValue).value equals listOf(StringValue("\"FOO\"")) + } + } + } + } + } + + @Test + fun inlineFunction() { + inlineModelTest( + """ + |inline fun f(a: () -> String) {} + """ + ) { + with((this / "function" / "f").cast<DFunction>()) { + extra[AdditionalModifiers]!!.content.entries.single().value counts 1 + extra[AdditionalModifiers]!!.content.entries.single().value exists ExtraModifiers.KotlinOnlyModifiers.Inline + } + } + } + + @Test + fun suspendFunction() { + inlineModelTest( + """ + |suspend fun f() {} + """ + ) { + with((this / "function" / "f").cast<DFunction>()) { + extra[AdditionalModifiers]!!.content.entries.single().value counts 1 + extra[AdditionalModifiers]!!.content.entries.single().value exists ExtraModifiers.KotlinOnlyModifiers.Suspend + } + } + } + + @Test + fun suspendInlineFunctionOrder() { + inlineModelTest( + """ + |suspend inline fun f(a: () -> String) {} + """ + ) { + with((this / "function" / "f").cast<DFunction>()) { + extra[AdditionalModifiers]!!.content.entries.single().value counts 2 + extra[AdditionalModifiers]!!.content.entries.single().value exists ExtraModifiers.KotlinOnlyModifiers.Suspend + extra[AdditionalModifiers]!!.content.entries.single().value exists ExtraModifiers.KotlinOnlyModifiers.Inline + } + } + } + + @Test + fun inlineSuspendFunctionOrderChanged() { + inlineModelTest( + """ + |inline suspend fun f(a: () -> String) {} + """ + ) { + with((this / "function" / "f").cast<DFunction>()) { + with(extra[AdditionalModifiers]!!.content.entries.single().value.assertNotNull("AdditionalModifiers")) { + this counts 2 + this exists ExtraModifiers.KotlinOnlyModifiers.Suspend + this exists ExtraModifiers.KotlinOnlyModifiers.Inline + } + } + } + } + + @Test + fun functionWithAnnotatedParam() { + inlineModelTest( + """ + |@Target(AnnotationTarget.VALUE_PARAMETER) + |@Retention(AnnotationRetention.SOURCE) + |@MustBeDocumented + |public annotation class Fancy + | + |fun function(@Fancy notInlined: () -> Unit) {} + """ + ) { + with((this / "function" / "Fancy").cast<DAnnotation>()) { + with(extra[Annotations]!!.content.entries.single().value.assertNotNull("Annotations")) { + this counts 3 + with(map { it.dri.classNames to it }.toMap()) { + with(this["Target"].assertNotNull("Target")) { + (params["allowedTargets"].assertNotNull("allowedTargets") as ArrayValue).value equals listOf( + EnumValue( + "AnnotationTarget.VALUE_PARAMETER", + DRI("kotlin.annotation", "AnnotationTarget.VALUE_PARAMETER") + ) + ) + } + with(this["Retention"].assertNotNull("Retention")) { + (params["value"].assertNotNull("value") as EnumValue) equals EnumValue( + "AnnotationRetention.SOURCE", + DRI("kotlin.annotation", "AnnotationRetention.SOURCE") + ) + } + this["MustBeDocumented"].assertNotNull("MustBeDocumented").params.entries counts 0 + } + } + + } + with((this / "function" / "function" / "notInlined").cast<DParameter>()) { + with(this.extra[Annotations]!!.content.entries.single().value.assertNotNull("Annotations")) { + this counts 1 + with(first()) { + dri.classNames equals "Fancy" + params.entries counts 0 + } + } + } + } + } + + @Test + fun functionWithNoinlineParam() { + inlineModelTest( + """ + |fun f(noinline notInlined: () -> Unit) {} + """ + ) { + with((this / "function" / "f" / "notInlined").cast<DParameter>()) { + extra[AdditionalModifiers]!!.content.entries.single().value counts 1 + extra[AdditionalModifiers]!!.content.entries.single().value exists ExtraModifiers.KotlinOnlyModifiers.NoInline + } + } + } + + @Test + fun annotatedFunctionWithAnnotationParameters() { + inlineModelTest( + """ + |@Target(AnnotationTarget.VALUE_PARAMETER) + |@Retention(AnnotationRetention.SOURCE) + |@MustBeDocumented + |public annotation class Fancy(val size: Int) + | + |@Fancy(1) fun f() {} + """ + ) { + with((this / "function" / "Fancy").cast<DAnnotation>()) { + constructors counts 1 + with(constructors.first()) { + parameters counts 1 + with(parameters.first()) { + type.name equals "Int" + name equals "size" + } + } + + with(extra[Annotations]!!.content.entries.single().value.assertNotNull("Annotations")) { + this counts 3 + with(map { it.dri.classNames to it }.toMap()) { + with(this["Target"].assertNotNull("Target")) { + (params["allowedTargets"].assertNotNull("allowedTargets") as ArrayValue).value equals listOf( + EnumValue( + "AnnotationTarget.VALUE_PARAMETER", + DRI("kotlin.annotation", "AnnotationTarget.VALUE_PARAMETER") + ) + ) + } + with(this["Retention"].assertNotNull("Retention")) { + (params["value"].assertNotNull("value") as EnumValue) equals EnumValue( + "AnnotationRetention.SOURCE", + DRI("kotlin.annotation", "AnnotationRetention.SOURCE") + ) + } + this["MustBeDocumented"].assertNotNull("MustBeDocumented").params.entries counts 0 + } + } + + } + with((this / "function" / "f").cast<DFunction>()) { + with(this.extra[Annotations]!!.content.entries.single().value.assertNotNull("Annotations")) { + this counts 1 + with(this.first()) { + dri.classNames equals "Fancy" + params.entries counts 1 + (params["size"] as StringValue).value equals "1" + } + } + } + } + } + + @Test + fun functionWithDefaultStringParameter() { + inlineModelTest( + """ + |/src/main/kotlin/function/Test.kt + |package function + |fun f(x: String = "") {} + """ + ) { + with((this / "function" / "f").cast<DFunction>()) { + parameters.forEach { p -> + p.name equals "x" + p.type.name.assertNotNull("Parameter type: ") equals "String" + p.extra[DefaultValue]?.value equals "\"\"" + } + } + } + } + + @Test + fun functionWithDefaultFloatParameter() { + inlineModelTest( + """ + |/src/main/kotlin/function/Test.kt + |package function + |fun f(x: Float = 3.14f) {} + """ + ) { + with((this / "function" / "f").cast<DFunction>()) { + parameters.forEach { p -> + p.name equals "x" + p.type.name.assertNotNull("Parameter type: ") equals "Float" + p.extra[DefaultValue]?.value equals "3.14f" + } + } + } + } + + @Test + fun sinceKotlin() { + inlineModelTest( + """ + |/** + | * Quite useful [String] + | */ + |@SinceKotlin("1.1") + |fun f(): String = "1.1 rulezz" + """ + ) { + with((this / "function" / "f").cast<DFunction>()) { + with(extra[Annotations]!!.content.entries.single().value.assertNotNull("Annotations")) { + this counts 1 + with(first()) { + dri.classNames equals "SinceKotlin" + params.entries counts 1 + (params["version"].assertNotNull("version") as StringValue).value equals "\"1.1\"" + } + } + } + } + } + +}
\ No newline at end of file diff --git a/plugins/base/src/test/kotlin/model/InheritorsTest.kt b/plugins/base/src/test/kotlin/model/InheritorsTest.kt new file mode 100644 index 00000000..503cf50c --- /dev/null +++ b/plugins/base/src/test/kotlin/model/InheritorsTest.kt @@ -0,0 +1,95 @@ +package model + +import org.jetbrains.dokka.CoreExtensions +import org.jetbrains.dokka.Platform +import org.jetbrains.dokka.base.transformers.documentables.InheritorsExtractorTransformer +import org.jetbrains.dokka.base.transformers.documentables.InheritorsInfo +import org.jetbrains.dokka.model.DInterface +import org.jetbrains.dokka.plugability.DokkaPlugin +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import utils.AbstractModelTest +import utils.assertNotNull + +class InheritorsTest : AbstractModelTest("/src/main/kotlin/inheritors/Test.kt", "inheritors") { + + object InheritorsPlugin : DokkaPlugin() { + val inheritors by extending { + CoreExtensions.documentableTransformer with InheritorsExtractorTransformer() + } + } + + @Disabled("reenable after fixing subtypes") + @Test + fun simple() { + inlineModelTest( + """|interface A{} + |class B() : A {} + """.trimMargin(), + pluginsOverrides = listOf(InheritorsPlugin) + ) { + with((this / "inheritors" / "A").cast<DInterface>()) { + val map = extra[InheritorsInfo].assertNotNull("InheritorsInfo").value + with(map.keys.also { it counts 1 }.find { it.analysisPlatform == Platform.jvm }.assertNotNull("jvm key").let { map[it]!! } + ) { + this counts 1 + first().classNames equals "B" + } + } + } + } + + @Disabled("reenable after fixing subtypes") + @Test + fun multiplatform() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("common/src/", "jvm/src/") + analysisPlatform = "jvm" + } + sourceSet { + sourceRoots = listOf("common/src/", "js/src/") + analysisPlatform = "js" + } + } + } + + testInline( + """ + |/common/src/main/kotlin/inheritors/Test.kt + |package inheritors + |interface A{} + |/jvm/src/main/kotlin/inheritors/Test.kt + |package inheritors + |class B() : A {} + |/js/src/main/kotlin/inheritors/Test.kt + |package inheritors + |class B() : A {} + |class C() : A {} + """.trimMargin(), + configuration, + cleanupOutput = false, + pluginOverrides = listOf(InheritorsPlugin) + ) { + documentablesTransformationStage = { m -> + with((m / "inheritors" / "A").cast<DInterface>()) { + val map = extra[InheritorsInfo].assertNotNull("InheritorsInfo").value + with(map.keys.also { it counts 2 }) { + with(find { it.analysisPlatform == Platform.jvm }.assertNotNull("jvm key").let { map[it]!! }) { + this counts 1 + first().classNames equals "B" + } + with(find { it.analysisPlatform == Platform.js }.assertNotNull("js key").let { map[it]!! }) { + this counts 2 + val classes = listOf("B", "C") + assertTrue(all { classes.contains(it.classNames) }, "One of subclasses missing in js" ) + } + } + + } + } + } + } +} diff --git a/plugins/base/src/test/kotlin/model/JavaTest.kt b/plugins/base/src/test/kotlin/model/JavaTest.kt new file mode 100644 index 00000000..1f042304 --- /dev/null +++ b/plugins/base/src/test/kotlin/model/JavaTest.kt @@ -0,0 +1,346 @@ +package model + +import org.jetbrains.dokka.base.transformers.documentables.InheritorsInfo +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.links.sureClassNames +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.doc.Param +import org.jetbrains.dokka.model.doc.Text +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test +import utils.AbstractModelTest +import utils.assertNotNull +import utils.name + +class JavaTest : AbstractModelTest("/src/main/kotlin/java/Test.java", "java") { + + @Test + fun function() { + inlineModelTest( + """ + |class Test { + | /** + | * Summary for Function + | * @param name is String parameter + | * @param value is int parameter + | */ + | public void fn(String name, int value) {} + |} + """ + ) { + with((this / "java" / "Test").cast<DClass>()) { + name equals "Test" + children counts 1 + with((this / "fn").cast<DFunction>()) { + name equals "fn" + val params = parameters.map { it.documentation.values.first().children.first() as Param } + params.mapNotNull { it.firstChildOfTypeOrNull<Text>()?.body } equals listOf("is String parameter", "is int parameter") + } + } + } + } + + @Test fun allImplementedInterfacesInJava() { + inlineModelTest( + """ + |interface Highest { } + |interface Lower extends Highest { } + |class Extendable { } + |class Tested extends Extendable implements Lower { } + """){ + with((this / "java" / "Tested").cast<DClass>()){ + extra[ImplementedInterfaces]?.interfaces?.entries?.single()?.value?.map { it.sureClassNames }?.sorted() equals listOf("Highest", "Lower").sorted() + } + } + } + + @Test fun multipleClassInheritanceWithInterface() { + inlineModelTest( + """ + |interface Highest { } + |interface Lower extends Highest { } + |class Extendable { } + |class Tested extends Extendable implements Lower { } + """){ + with((this / "java" / "Tested").cast<DClass>()) { + supertypes.entries.single().value.map { it.dri.sureClassNames to it.kind }.sortedBy { it.first } equals listOf("Extendable" to JavaClassKindTypes.CLASS, "Lower" to JavaClassKindTypes.INTERFACE) + } + } + } + + @Test // todo + fun memberWithModifiers() { + inlineModelTest( + """ + |class Test { + | /** + | * Summary for Function + | * @param name is String parameter + | * @param value is int parameter + | */ + | public void fn(String name, int value) {} + |} + """ + ) { + with((this / "java" / "Test" / "fn").cast<DFunction>()) { + this + } + } + } + + @Test + fun superClass() { + inlineModelTest( + """ + |public class Foo extends Exception implements Cloneable {} + """ + ) { + with((this / "java" / "Foo").cast<DClass>()) { + val sups = listOf("Exception", "Cloneable") + assertTrue( + sups.all { s -> supertypes.values.flatten().any { it.dri.classNames == s } }) + "Foo must extend ${sups.joinToString(", ")}" + } + } + } + + @Test + fun arrayType() { + inlineModelTest( + """ + |class Test { + | public String[] arrayToString(int[] data) { + | return null; + | } + |} + """ + ) { + with((this / "java" / "Test").cast<DClass>()) { + name equals "Test" + children counts 1 + + with((this / "arrayToString").cast<DFunction>()) { + name equals "arrayToString" + type.name equals "Array" + with(parameters.firstOrNull().assertNotNull("parameters")) { + name equals "data" + type.name equals "Array" + } + } + } + } + } + + @Test + fun typeParameter() { + inlineModelTest( + """ + |class Foo<T extends Comparable<T>> { + | public <E> E foo(); + |} + """ + ) { + with((this / "java" / "Foo").cast<DClass>()) { + generics counts 1 + } + } + } + + @Test + fun constructors() { + inlineModelTest( + """ + |class Test { + | public Test() {} + | + | public Test(String s) {} + |} + """ + ) { + with((this / "java" / "Test").cast<DClass>()) { + name equals "Test" + + constructors counts 2 + constructors.find { it.parameters.isNullOrEmpty() }.assertNotNull("Test()") + + with(constructors.find { it.parameters.isNotEmpty() }.assertNotNull("Test(String)")) { + parameters.firstOrNull()?.type?.name equals "String" + } + } + } + } + + @Test + fun innerClass() { + inlineModelTest( + """ + |class InnerClass { + | public class D {} + |} + """ + ) { + with((this / "java" / "InnerClass").cast<DClass>()) { + children counts 1 + with((this / "D").cast<DClass>()) { + name equals "D" + children counts 0 + } + } + } + } + + @Test + fun varargs() { + inlineModelTest( + """ + |class Foo { + | public void bar(String... x); + |} + """ + ) { + with((this / "java" / "Foo").cast<DClass>()) { + name equals "Foo" + children counts 1 + + with((this / "bar").cast<DFunction>()) { + name equals "bar" + with(parameters.firstOrNull().assertNotNull("parameter")) { + name equals "x" + type.name equals "Array" + } + } + } + } + } + + @Test // todo + fun fields() { + inlineModelTest( + """ + |class Test { + | public int i; + | public static final String s; + |} + """ + ) { + with((this / "java" / "Test").cast<DClass>()) { + children counts 2 + + with((this / "i").cast<DProperty>()) { + getter equals null + setter equals null + } + + with((this / "s").cast<DProperty>()) { + getter equals null + setter equals null + } + } + } + } + + @Test + fun staticMethod() { + inlineModelTest( + """ + |class C { + | public static void foo() {} + |} + """ + ) { + with((this / "java" / "C" / "foo").cast<DFunction>()) { + with(extra[AdditionalModifiers]!!.content.entries.single().value.assertNotNull("AdditionalModifiers")) { + this counts 1 + first() equals ExtraModifiers.JavaOnlyModifiers.Static + } + } + } + } + + @Test + fun annotatedAnnotation() { + inlineModelTest( + """ + |import java.lang.annotation.*; + | + |@Target({ElementType.FIELD, ElementType.TYPE, ElementType.METHOD}) + |public @interface Attribute { + | String value() default ""; + |} + """ + ) { + with((this / "java" / "Attribute").cast<DAnnotation>()) { + with(extra[Annotations]!!.content.entries.single().value.assertNotNull("Annotations")) { + with(single()) { + dri.classNames equals "Target" + (params["value"].assertNotNull("value") as ArrayValue).value equals listOf( + EnumValue("ElementType.FIELD", DRI("java.lang.annotation", "ElementType")), + EnumValue("ElementType.TYPE", DRI("java.lang.annotation", "ElementType")), + EnumValue("ElementType.METHOD", DRI("java.lang.annotation", "ElementType")) + ) + } + } + } + } + } + + @Test + fun javaLangObject() { + inlineModelTest( + """ + |class Test { + | public Object fn() { return null; } + |} + """ + ) { + with((this / "java" / "Test" / "fn").cast<DFunction>()) { + assertTrue(type is JavaObject) + } + } + } + + @Test + fun enumValues() { + inlineModelTest( + """ + |enum E { + | Foo + |} + """ + ) { + with((this / "java" / "E").cast<DEnum>()) { + name equals "E" + entries counts 1 + + with((this / "Foo").cast<DEnumEntry>()) { + name equals "Foo" + } + } + } + } + + @Test + fun inheritorLinks() { + inlineModelTest( + """ + |public class InheritorLinks { + | public static class Foo {} + | + | public static class Bar extends Foo {} + |} + """ + ) { + with((this / "java" / "InheritorLinks").cast<DClass>()) { + val dri = (this / "Bar").assertNotNull("Foo dri").dri + with((this / "Foo").cast<DClass>()) { + with(extra[InheritorsInfo].assertNotNull("InheritorsInfo")) { + with(value.values.flatten().distinct()) { + this counts 1 + first() equals dri + } + } + } + } + } + } +} diff --git a/plugins/base/src/test/kotlin/model/PackagesTest.kt b/plugins/base/src/test/kotlin/model/PackagesTest.kt new file mode 100644 index 00000000..86222d95 --- /dev/null +++ b/plugins/base/src/test/kotlin/model/PackagesTest.kt @@ -0,0 +1,134 @@ +package model + +import org.jetbrains.dokka.model.DPackage +import org.junit.jupiter.api.Test +import utils.AbstractModelTest + +class PackagesTest : AbstractModelTest("/src/main/kotlin/packages/Test.kt", "packages") { + + @Test + fun rootPackage() { + inlineModelTest( + """ + | + """.trimIndent(), + prependPackage = false, + configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin") + displayName = "JVM" + } + } + } + ) { + with((this / "[JVM root]").cast<DPackage>()) { + name equals "[JVM root]" + children counts 0 + } + } + } + + @Test + fun simpleNamePackage() { + inlineModelTest( + """ + |package simple + """.trimIndent(), + prependPackage = false + ) { + with((this / "simple").cast<DPackage>()) { + name equals "simple" + children counts 0 + } + } + } + + @Test + fun dottedNamePackage() { + inlineModelTest( + """ + |package dot.name + """.trimIndent(), + prependPackage = false + ) { + with((this / "dot.name").cast<DPackage>()) { + name equals "dot.name" + children counts 0 + } + } + + } + + @Test + fun multipleFiles() { + inlineModelTest( + """ + |package dot.name + |/src/main/kotlin/packages/Test2.kt + |package simple + """.trimIndent(), + prependPackage = false + ) { + children counts 2 + with((this / "dot.name").cast<DPackage>()) { + name equals "dot.name" + children counts 0 + } + with((this / "simple").cast<DPackage>()) { + name equals "simple" + children counts 0 + } + } + } + + @Test + fun multipleFilesSamePackage() { + inlineModelTest( + """ + |package simple + |/src/main/kotlin/packages/Test2.kt + |package simple + """.trimIndent(), + prependPackage = false + ) { + children counts 1 + with((this / "simple").cast<DPackage>()) { + name equals "simple" + children counts 0 + } + } + } + + @Test + fun classAtPackageLevel() { + inlineModelTest( + """ + |package simple.name + | + |class Foo {} + """.trimIndent(), + prependPackage = false + ) { + with((this / "simple.name").cast<DPackage>()) { + name equals "simple.name" + children counts 1 + } + } + } + + // todo +// @Test fun suppressAtPackageLevel() { +// verifyModel( +// ModelConfig( +// roots = arrayOf(KotlinSourceRoot("testdata/packages/classInPackage.kt", false)), +// perPackageOptions = listOf( +// PackageOptionsImpl(prefix = "simple.name", suppress = true) +// ), +// analysisPlatform = analysisPlatform +// ) +// ) { model -> +// assertEquals(0, model.members.count()) +// } +// } +}
\ No newline at end of file diff --git a/plugins/base/src/test/kotlin/model/PropertyTest.kt b/plugins/base/src/test/kotlin/model/PropertyTest.kt new file mode 100644 index 00000000..af952b43 --- /dev/null +++ b/plugins/base/src/test/kotlin/model/PropertyTest.kt @@ -0,0 +1,265 @@ +package model + +import org.jetbrains.dokka.model.* +import org.junit.jupiter.api.Test +import utils.AbstractModelTest +import utils.assertNotNull +import utils.name + +class PropertyTest : AbstractModelTest("/src/main/kotlin/property/Test.kt", "property") { + + @Test + fun valueProperty() { + inlineModelTest( + """ + |val property = "test"""" + ) { + with((this / "property" / "property").cast<DProperty>()) { + name equals "property" + children counts 0 + with(getter.assertNotNull("Getter")) { + type.name equals "String" + } + type.name equals "String" + } + } + } + + @Test + fun variableProperty() { + inlineModelTest( + """ + |var property = "test" + """ + ) { + with((this / "property" / "property").cast<DProperty>()) { + name equals "property" + children counts 0 + setter.assertNotNull("Setter") + with(getter.assertNotNull("Getter")) { + type.name equals "String" + } + type.name equals "String" + } + } + } + + @Test + fun valuePropertyWithGetter() { + inlineModelTest( + """ + |val property: String + | get() = "test" + """ + ) { + with((this / "property" / "property").cast<DProperty>()) { + name equals "property" + children counts 0 + with(getter.assertNotNull("Getter")) { + type.name equals "String" + } + type.name equals "String" + } + } + } + + @Test + fun variablePropertyWithAccessors() { + inlineModelTest( + """ + |var property: String + | get() = "test" + | set(value) {} + """ + ) { + with((this / "property" / "property").cast<DProperty>()) { + name equals "property" + children counts 0 + setter.assertNotNull("Setter") + with(getter.assertNotNull("Getter")) { + type.name equals "String" + } + visibility.values allEquals KotlinVisibility.Public + } + } + } + + @Test + fun propertyWithReceiver() { + inlineModelTest( + """ + |val String.property: Int + | get() = size() * 2 + """ + ) { + with((this / "property" / "property").cast<DProperty>()) { + name equals "property" + children counts 0 + with(receiver.assertNotNull("property receiver")) { + name equals null + type.name equals "String" + } + with(getter.assertNotNull("Getter")) { + type.name equals "Int" + } + visibility.values allEquals KotlinVisibility.Public + } + } + } + + @Test + fun propertyOverride() { + inlineModelTest( + """ + |open class Foo() { + | open val property: Int get() = 0 + |} + |class Bar(): Foo() { + | override val property: Int get() = 1 + |} + """ + ) { + with((this / "property").cast<DPackage>()) { + with((this / "Foo" / "property").cast<DProperty>()) { + name equals "property" + children counts 0 + with(getter.assertNotNull("Getter")) { + type.name equals "Int" + } + } + with((this / "Bar" / "property").cast<DProperty>()) { + name equals "property" + children counts 0 + with(getter.assertNotNull("Getter")) { + type.name equals "Int" + } + } + } + } + } + + @Test + fun sinceKotlin() { + inlineModelTest( + """ + |/** + | * Quite useful [String] + | */ + |@SinceKotlin("1.1") + |val prop: String = "1.1 rulezz" + """ + ) { + with((this / "property" / "prop").cast<DProperty>()) { + with(extra[Annotations]!!.content.entries.single().value.assertNotNull("Annotations")) { + this counts 1 + with(first()) { + dri.classNames equals "SinceKotlin" + params.entries counts 1 + (params["version"].assertNotNull("version") as StringValue).value equals "\"1.1\"" + } + } + } + } + } + + @Test + fun annotatedProperty() { + inlineModelTest( + """ + |@Strictfp var property = "test" + """, + configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/") + classpath = listOfNotNull(jvmStdlibPath) + } + } + } + ) { + with((this / "property" / "property").cast<DProperty>()) { + with(extra[Annotations]!!.content.entries.single().value.assertNotNull("Annotations")) { + this counts 1 + with(first()) { + dri.classNames equals "Strictfp" + params.entries counts 0 + } + } + } + } + } + + @Test fun genericTopLevelExtensionProperty(){ + inlineModelTest( + """ | val <T : Number> List<T>.sampleProperty: T + | get() { TODO() } + """.trimIndent() + ){ + with((this / "property" / "sampleProperty").cast<DProperty>()) { + name equals "sampleProperty" + with(receiver.assertNotNull("Property receiver")) { + type.name equals "List" + } + with(getter.assertNotNull("Getter")) { + type.name equals "T" + } + setter equals null + generics counts 1 + generics.forEach { + it.name equals "T" + it.bounds.first().name equals "Number" + } + visibility.values allEquals KotlinVisibility.Public + } + } + } + + @Test fun genericExtensionPropertyInClass(){ + inlineModelTest( + """ | package test + | class XD<T> { + | var List<T>.sampleProperty: T + | get() { TODO() } + | set(value) { TODO() } + | } + """.trimIndent() + ){ + with((this / "property" / "XD" / "sampleProperty").cast<DProperty>()) { + name equals "sampleProperty" + children counts 0 + with(receiver.assertNotNull("Property receiver")) { + type.name equals "List" + } + with(getter.assertNotNull("Getter")) { + type.name equals "T" + } + with(setter.assertNotNull("Setter")){ + type.name equals "Unit" + } + generics counts 0 + visibility.values allEquals KotlinVisibility.Public + } + } + } +// @Test +// fun annotatedProperty() { +// checkSourceExistsAndVerifyModel( +// "testdata/properties/annotatedProperty.kt", +// modelConfig = ModelConfig( +// analysisPlatform = analysisPlatform, +// withKotlinRuntime = true +// ) +// ) { model -> +// with(model.members.single().members.single()) { +// Assert.assertEquals(1, annotations.count()) +// with(annotations[0]) { +// Assert.assertEquals("Strictfp", name) +// Assert.assertEquals(Content.Empty, content) +// Assert.assertEquals(NodeKind.Annotation, kind) +// } +// } +// } +// } +// +//} +} diff --git a/plugins/base/src/test/kotlin/multiplatform/BasicMultiplatformTest.kt b/plugins/base/src/test/kotlin/multiplatform/BasicMultiplatformTest.kt new file mode 100644 index 00000000..b3ac7b07 --- /dev/null +++ b/plugins/base/src/test/kotlin/multiplatform/BasicMultiplatformTest.kt @@ -0,0 +1,54 @@ +package multiplatform + +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest + +class BasicMultiplatformTest : AbstractCoreTest() { + + @Test + fun dataTestExample() { + val testDataDir = getTestDataDir("multiplatform/basicMultiplatformTest").toAbsolutePath() + + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("$testDataDir/jvmMain/") + } + } + } + + testFromData(configuration) { + pagesTransformationStage = { + assertEquals(6, it.children.firstOrNull()?.children?.count() ?: 0) + } + } + } + + @Test + fun inlineTestExample() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin/multiplatform/Test.kt") + } + } + } + + testInline( + """ + |/src/main/kotlin/multiplatform/Test.kt + |package multiplatform + | + |object Test { + | fun test2(str: String): Unit {println(str)} + |} + """.trimMargin(), + configuration + ) { + pagesGenerationStage = { + assertEquals(3, it.parentMap.size) + } + } + } +} diff --git a/plugins/base/src/test/kotlin/pageMerger/PageNodeMergerTest.kt b/plugins/base/src/test/kotlin/pageMerger/PageNodeMergerTest.kt new file mode 100644 index 00000000..935b9377 --- /dev/null +++ b/plugins/base/src/test/kotlin/pageMerger/PageNodeMergerTest.kt @@ -0,0 +1,126 @@ +package pageMerger + +import org.jetbrains.dokka.pages.ContentPage +import org.jetbrains.dokka.pages.PageNode +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest + +class PageNodeMergerTest : AbstractCoreTest() { + + /* object SameNameStrategy : DokkaPlugin() { + val strategy by extending { CoreExtensions.pageMergerStrategy with SameMethodNamePageMergerStrategy } + } + + class DefaultStrategy(val strList: MutableList<String> = mutableListOf()) : DokkaPlugin(), DokkaLogger { + val strategy by extending { CoreExtensions.pageMergerStrategy with DefaultPageMergerStrategy(this@DefaultStrategy) } + + override var warningsCount: Int = 0 + override var errorsCount: Int = 0 + + override fun debug(message: String) = TODO() + + override fun info(message: String) = TODO() + + override fun progress(message: String) = TODO() + + override fun warn(message: String) { + strList += message + } + + override fun error(message: String) = TODO() + + override fun report() = TODO() + } + */ + + @Test + fun sameNameStrategyTest() { + + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin/pageMerger/Test.kt") + } + } + } + + testInline( + """ + |/src/main/kotlin/pageMerger/Test.kt + |package pageMerger + | + |fun testT(): Int = 1 + |fun testT(i: Int): Int = i + | + |object Test { + | fun test(): String = "" + | fun test(str: String): String = str + |} + """.trimMargin(), + configuration/*, + pluginOverrides = listOf(SameNameStrategy)*/ + ) { + pagesTransformationStage = { + val allChildren = it.childrenRec().filterIsInstance<ContentPage>() + val testT = allChildren.filter { it.name == "testT" } + val test = allChildren.filter { it.name == "test" } + + assertTrue(testT.size == 1) { "There can be only one testT page" } + assertTrue(testT.first().dri.size == 2) { "testT page should have 2 DRI, but has ${testT.first().dri.size}" } + + assertTrue(test.size == 1) { "There can be only one test page" } + assertTrue(test.first().dri.size == 2) { "test page should have 2 DRI, but has ${test.first().dri.size}" } + } + } + } + + @Disabled("TODO: reenable when we have infrastructure for turning off extensions") + @Test + fun defaultStrategyTest() { + val strList: MutableList<String> = mutableListOf() + + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin/pageMerger/Test.kt") + } + } + } + + testInline( + """ + |/src/main/kotlin/pageMerger/Test.kt + |package pageMerger + | + |fun testT(): Int = 1 + |fun testT(i: Int): Int = i + | + |object Test { + | fun test(): String = "" + | fun test(str: String): String = str + |} + """.trimMargin(), + configuration/*, + pluginOverrides = listOf(DefaultStrategy(strList)) */ + ) { + pagesTransformationStage = { root -> + val allChildren = root.childrenRec().filterIsInstance<ContentPage>() + val testT = allChildren.filter { it.name == "testT" } + val test = allChildren.filter { it.name == "test" } + + assertTrue(testT.size == 1) { "There can be only one testT page" } + assertTrue(testT.first().dri.size == 1) { "testT page should have single DRI, but has ${testT.first().dri.size}" } + + assertTrue(test.size == 1) { "There can be only one test page" } + assertTrue(test.first().dri.size == 1) { "test page should have single DRI, but has ${test.first().dri.size}" } + + assertTrue(strList.count() == 2) { "Expected 2 warnings, got ${strList.count()}" } + } + } + } + + fun PageNode.childrenRec(): List<PageNode> = listOf(this) + children.flatMap { it.childrenRec() } + +} diff --git a/plugins/base/src/test/kotlin/renderers/html/DivergentTest.kt b/plugins/base/src/test/kotlin/renderers/html/DivergentTest.kt new file mode 100644 index 00000000..8ab277f1 --- /dev/null +++ b/plugins/base/src/test/kotlin/renderers/html/DivergentTest.kt @@ -0,0 +1,328 @@ +package renderers.html + +import org.jetbrains.dokka.Platform +import org.jetbrains.dokka.SourceRootImpl +import org.jetbrains.dokka.base.renderers.html.HtmlRenderer +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.pages.ContentDivergentGroup +import org.junit.jupiter.api.Test +import renderers.* +import utils.Div +import utils.Span +import utils.match + +class DivergentTest : HtmlRenderingOnlyTestBase() { + + @Test + fun simpleWrappingCase() { + val page = TestPage { + divergentGroup(ContentDivergentGroup.GroupID("test")) { + instance(setOf(DRI("test", "Test")), setOf(js)) { + divergent { + text("a") + } + } + } + } + HtmlRenderer(context).render(page) + renderedContent.match(Div(Div(Div(Div("a"))))) + } + + @Test + fun noPlatformHintCase() { + val page = TestPage { + divergentGroup(ContentDivergentGroup.GroupID("test"), implicitlySourceSetHinted = false) { + instance(setOf(DRI("test", "Test")), setOf(js)) { + divergent { + text("a") + } + } + } + } + HtmlRenderer(context).render(page) + renderedContent.match(Div(Div("a"))) + } + + @Test + fun divergentBetweenSourceSets() { + val page = TestPage { + divergentGroup(ContentDivergentGroup.GroupID("test")) { + instance(setOf(DRI("test", "Test")), setOf(js)) { + divergent { + text("a") + } + } + instance(setOf(DRI("test", "Test")), setOf(jvm)) { + divergent { + text("b") + } + } + instance(setOf(DRI("test", "Test")), setOf(native)) { + divergent { + text("c") + } + } + } + } + + HtmlRenderer(context).render(page) + renderedContent.match(Div(Div(Div(Div("a"), Div("b"), Div("c"))))) + } + + @Test + fun divergentInOneSourceSet() { + val page = TestPage { + divergentGroup(ContentDivergentGroup.GroupID("test")) { + instance(setOf(DRI("test", "Test")), setOf(js)) { + divergent { + text("a") + } + } + instance(setOf(DRI("test", "Test2")), setOf(js)) { + divergent { + text("b") + } + } + instance(setOf(DRI("test", "Test3")), setOf(js)) { + divergent { + text("c") + } + } + } + } + + HtmlRenderer(context).render(page) + renderedContent.match(Div(Div((Div(Div("abc")))))) + } + + @Test + fun divergentInAndBetweenSourceSets() { + val page = TestPage { + divergentGroup(ContentDivergentGroup.GroupID("test")) { + instance(setOf(DRI("test", "Test")), setOf(native)) { + divergent { + text("a") + } + } + instance(setOf(DRI("test", "Test")), setOf(js)) { + divergent { + text("b") + } + } + instance(setOf(DRI("test", "Test")), setOf(jvm)) { + divergent { + text("c") + } + } + instance(setOf(DRI("test", "Test2")), setOf(js)) { + divergent { + text("d") + } + } + instance(setOf(DRI("test", "Test3")), setOf(native)) { + divergent { + text("e") + } + } + } + } + + HtmlRenderer(context).render(page) + renderedContent.match(Div(Div(Div(Div("ae"), Div("bd"), Div("c"))))) + } + + @Test + fun divergentInAndBetweenSourceSetsWithGrouping() { + val page = TestPage { + divergentGroup(ContentDivergentGroup.GroupID("test")) { + instance(setOf(DRI("test", "Test")), setOf(native)) { + divergent { + text("a") + } + after { + text("a+") + } + } + instance(setOf(DRI("test", "Test")), setOf(js)) { + divergent { + text("b") + } + after { + text("bd+") + } + } + instance(setOf(DRI("test", "Test")), setOf(jvm)) { + divergent { + text("c") + } + } + instance(setOf(DRI("test", "Test2")), setOf(js)) { + divergent { + text("d") + } + after { + text("bd+") + } + } + instance(setOf(DRI("test", "Test3")), setOf(native)) { + divergent { + text("e") + } + after { + text("e+") + } + } + } + } + + HtmlRenderer(context).render(page) + renderedContent.match( + Div(Div(Span(Div(Div("NATIVE")))), Div(Div(Div("a"))), "a+"), + Div(Div(Span(Div(Div("JS")))), Div(Div(Div("bd"))), "bd+"), + Div(Div(Span(Div(Div("JVM")))), Div(Div(Div("c")))), + Div(Div(Span(Div(Div("NATIVE")))), Div(Div(Div("e"))), "e+"), + ) + } + + @Test + fun divergentSameBefore() { + val page = TestPage { + divergentGroup(ContentDivergentGroup.GroupID("test")) { + instance(setOf(DRI("test", "Test")), setOf(native)) { + before { + text("ab-") + } + divergent { + text("a") + } + } + instance(setOf(DRI("test", "Test2")), setOf(native)) { + before { + text("ab-") + } + divergent { + text("b") + } + } + } + } + + HtmlRenderer(context).render(page) + renderedContent.match( + Div( + Div( + Div("ab-"), + Span() + ), + Div(Div(Div("ab"))) + ) + ) + } + + @Test + fun divergentSameAfter() { + val page = TestPage { + divergentGroup(ContentDivergentGroup.GroupID("test")) { + instance(setOf(DRI("test", "Test")), setOf(native)) { + divergent { + text("a") + } + after { + text("ab+") + } + } + instance(setOf(DRI("test", "Test2")), setOf(native)) { + divergent { + text("b") + } + after { + text("ab+") + } + } + } + } + + HtmlRenderer(context).render(page) + renderedContent.match( + Div( + Div(Div(Div("ab"))), + "ab+" + ) + ) + } + + @Test + fun divergentGroupedByBeforeAndAfter() { + val page = TestPage { + divergentGroup(ContentDivergentGroup.GroupID("test")) { + instance(setOf(DRI("test", "Test")), setOf(native)) { + before { + text("ab-") + } + divergent { + text("a") + } + after { + text("ab+") + } + } + instance(setOf(DRI("test", "Test2")), setOf(native)) { + before { + text("ab-") + } + divergent { + text("b") + } + after { + text("ab+") + } + } + } + } + + HtmlRenderer(context).render(page) + renderedContent.match( + Div( + Div(Div("ab-"), Span()), + Div(Div(Div("ab"))), + "ab+" + ) + ) + } + + @Test + fun divergentDifferentBeforeAndAfter() { + val page = TestPage { + divergentGroup(ContentDivergentGroup.GroupID("test")) { + instance(setOf(DRI("test", "Test")), setOf(native)) { + before { + text("a-") + } + divergent { + text("a") + } + after { + text("ab+") + } + } + instance(setOf(DRI("test", "Test2")), setOf(native)) { + before { + text("b-") + } + divergent { + text("b") + } + after { + text("ab+") + } + } + } + } + + HtmlRenderer(context).render(page) + renderedContent.match( + Div(Div(Div("a-"), Span()), Div(Div(Div("a"))), "ab+"), + Div(Div(Div("b-"), Span()), Div(Div(Div(("b")))), "ab+") + ) + } +} diff --git a/plugins/base/src/test/kotlin/renderers/html/GroupWrappingTest.kt b/plugins/base/src/test/kotlin/renderers/html/GroupWrappingTest.kt new file mode 100644 index 00000000..c0c03998 --- /dev/null +++ b/plugins/base/src/test/kotlin/renderers/html/GroupWrappingTest.kt @@ -0,0 +1,78 @@ +package renderers.html + +import org.jetbrains.dokka.base.renderers.html.HtmlRenderer +import org.jetbrains.dokka.pages.TextStyle +import org.junit.jupiter.api.Test +import renderers.* +import utils.Div +import utils.P +import utils.match + +class GroupWrappingTest : HtmlRenderingOnlyTestBase() { + + @Test + fun notWrapped() { + val page = TestPage { + group { + text("a") + text("b") + } + text("c") + } + + HtmlRenderer(context).render(page) + + renderedContent.match("abc") + } + + @Test + fun paragraphWrapped() { + val page = TestPage { + group(styles = setOf(TextStyle.Paragraph)) { + text("a") + text("b") + } + text("c") + } + + HtmlRenderer(context).render(page) + + renderedContent.match(P("ab"), "c") + } + + @Test + fun blockWrapped() { + val page = TestPage { + group(styles = setOf(TextStyle.Block)) { + text("a") + text("b") + } + text("c") + } + + HtmlRenderer(context).render(page) + + renderedContent.match(Div("ab"), "c") + } + + @Test + fun nested() { + val page = TestPage { + group(styles = setOf(TextStyle.Block)) { + text("a") + group(styles = setOf(TextStyle.Block)) { + group(styles = setOf(TextStyle.Block)) { + text("b") + text("c") + } + } + text("d") + } + } + + HtmlRenderer(context).render(page) + + renderedContent.match(Div("a", Div(Div("bc")), "d")) + } + +} diff --git a/plugins/base/src/test/kotlin/renderers/html/HtmlRenderingOnlyTestBase.kt b/plugins/base/src/test/kotlin/renderers/html/HtmlRenderingOnlyTestBase.kt new file mode 100644 index 00000000..b6765fda --- /dev/null +++ b/plugins/base/src/test/kotlin/renderers/html/HtmlRenderingOnlyTestBase.kt @@ -0,0 +1,90 @@ +package renderers.html + +import org.jetbrains.dokka.DokkaConfigurationImpl +import org.jetbrains.dokka.Platform +import org.jetbrains.dokka.SourceRootImpl +import org.jetbrains.dokka.base.DokkaBase +import org.jetbrains.dokka.base.renderers.DefaultTabSortingStrategy +import org.jetbrains.dokka.base.renderers.RootCreator +import org.jetbrains.dokka.base.resolvers.external.DokkaExternalLocationProviderFactory +import org.jetbrains.dokka.base.resolvers.external.JavadocExternalLocationProviderFactory +import org.jetbrains.dokka.base.resolvers.local.DefaultLocationProviderFactory +import org.jetbrains.dokka.testApi.context.MockContext +import org.jsoup.Jsoup +import org.jsoup.nodes.Element +import org.jsoup.nodes.Node +import org.jsoup.nodes.TextNode +import renderers.RenderingOnlyTestBase +import utils.TestOutputWriter +import renderers.defaultSourceSet + +abstract class HtmlRenderingOnlyTestBase : RenderingOnlyTestBase<Element>() { + + protected val js = defaultSourceSet.copy( + "root", + "JS", + defaultSourceSet.sourceSetID.copy(sourceSetName = "js"), + analysisPlatform = Platform.js, + sourceRoots = listOf(SourceRootImpl("pl1")) + ) + protected val jvm = defaultSourceSet.copy( + "root", + "JVM", + defaultSourceSet.sourceSetID.copy(sourceSetName = "jvm"), + + analysisPlatform = Platform.jvm, + sourceRoots = listOf(SourceRootImpl("pl1")) + ) + protected val native = defaultSourceSet.copy( + "root", + "NATIVE", + defaultSourceSet.sourceSetID.copy(sourceSetName = "native"), + analysisPlatform = Platform.native, + sourceRoots = listOf(SourceRootImpl("pl1")) + ) + + val files = TestOutputWriter() + override val context = MockContext( + DokkaBase().outputWriter to { _ -> files }, + DokkaBase().locationProviderFactory to ::DefaultLocationProviderFactory, + DokkaBase().htmlPreprocessors to { _ -> RootCreator }, + DokkaBase().externalLocationProviderFactory to { ::JavadocExternalLocationProviderFactory }, + DokkaBase().externalLocationProviderFactory to { ::DokkaExternalLocationProviderFactory }, + DokkaBase().tabSortingStrategy to { DefaultTabSortingStrategy() }, + testConfiguration = DokkaConfigurationImpl( + "", null, false, listOf(js, jvm, native), emptyList(), emptyMap(), emptyList(), false + ) + ) + + override val renderedContent: Element by lazy { + files.contents.getValue("test-page.html").let { Jsoup.parse(it) }.select("#content").single() + } + + protected fun linesAfterContentTag() = + files.contents.getValue("test-page.html").lines() + .dropWhile { !it.contains("""<div id="content">""") } + .joinToString(separator = "") { it.trim() } +} + +fun Element.match(vararg matchers: Any): Unit = + childNodes() + .filter { it !is TextNode || it.text().isNotBlank() } + .let { it.drop(it.size - matchers.size) } + .zip(matchers) + .forEach { (n, m) -> m.accepts(n) } + +open class Tag(val name: String, vararg val matchers: Any) +class Div(vararg matchers: Any) : Tag("div", *matchers) +class P(vararg matchers: Any) : Tag("p", *matchers) +class Span(vararg matchers: Any) : Tag("span", *matchers) + +private fun Any.accepts(n: Node) { + when (this) { + is String -> assert(n is TextNode && n.text().trim() == this.trim()) { "\"$this\" expected but found: $n" } + is Tag -> { + assert(n is Element && n.tagName() == name) { "Tag $name expected but found: $n" } + if (n is Element && matchers.isNotEmpty()) n.match(*matchers) + } + else -> throw IllegalArgumentException("$this is not proper matcher") + } +} diff --git a/plugins/base/src/test/kotlin/renderers/html/SourceSetDependentHintTest.kt b/plugins/base/src/test/kotlin/renderers/html/SourceSetDependentHintTest.kt new file mode 100644 index 00000000..cf7f47e6 --- /dev/null +++ b/plugins/base/src/test/kotlin/renderers/html/SourceSetDependentHintTest.kt @@ -0,0 +1,139 @@ +package renderers.html + +import org.jetbrains.dokka.Platform +import org.jetbrains.dokka.SourceRootImpl +import org.jetbrains.dokka.base.renderers.html.HtmlRenderer +import org.jetbrains.dokka.pages.TextStyle +import org.junit.jupiter.api.Test +import renderers.TestPage +import renderers.defaultSourceSet +import renderers.RenderingOnlyTestBase +import utils.Div +import utils.match + +class SourceSetDependentHintTest : HtmlRenderingOnlyTestBase() { + + private val pl1 = defaultSourceSet.copy( + "root", + "pl1", + defaultSourceSet.sourceSetID.copy(sourceSetName = "pl1"), + analysisPlatform = Platform.js, + sourceRoots = listOf(SourceRootImpl("pl1")) + ) + private val pl2 = defaultSourceSet.copy( + "root", + "pl2", + defaultSourceSet.sourceSetID.copy(sourceSetName = "pl2"), + analysisPlatform = Platform.jvm, + sourceRoots = listOf(SourceRootImpl("pl1")) + ) + private val pl3 = defaultSourceSet.copy( + "root", + "pl3", + defaultSourceSet.sourceSetID.copy(sourceSetName = "pl3"), + analysisPlatform = Platform.native, + sourceRoots = listOf(SourceRootImpl("pl1")) + ) + + @Test + fun platformIndependentCase() { + val page = TestPage { + sourceSetDependentHint(sourceSets = setOf(pl1, pl2, pl3), styles = setOf(TextStyle.Block)) { + text("a") + text("b") + text("c") + } + } + + HtmlRenderer(context).render(page) + renderedContent.match(Div(Div(Div("abc")))) + } + + @Test + fun completelyDivergentCase() { + val page = TestPage { + sourceSetDependentHint(sourceSets = setOf(pl1, pl2, pl3), styles = setOf(TextStyle.Block)) { + text("a", sourceSets = setOf(pl1)) + text("b", sourceSets = setOf(pl2)) + text("c", sourceSets = setOf(pl3)) + } + } + + HtmlRenderer(context).render(page) + renderedContent.match(Div(Div(Div("a")), Div(Div("b")), Div(Div("c")))) + } + + @Test + fun overlappingCase() { + val page = TestPage { + sourceSetDependentHint(sourceSets = setOf(pl1, pl2), styles = setOf(TextStyle.Block)) { + text("a", sourceSets = setOf(pl1)) + text("b", sourceSets = setOf(pl1, pl2)) + text("c", sourceSets = setOf(pl2)) + } + } + + HtmlRenderer(context).render(page) + renderedContent.match(Div(Div(Div("ab")), Div(Div("bc")))) + } + + @Test + fun caseThatCanBeSimplified() { + val page = TestPage { + sourceSetDependentHint(sourceSets = setOf(pl1, pl2), styles = setOf(TextStyle.Block)) { + text("a", sourceSets = setOf(pl1, pl2)) + text("b", sourceSets = setOf(pl1)) + text("b", sourceSets = setOf(pl2)) + } + } + + HtmlRenderer(context).render(page) + renderedContent.match(Div(Div(Div("ab")))) + } + + @Test + fun caseWithGroupBreakingSimplification() { + val page = TestPage { + sourceSetDependentHint(sourceSets = setOf(pl1, pl2), styles = setOf(TextStyle.Block)) { + group(styles = setOf(TextStyle.Block)) { + text("a", sourceSets = setOf(pl1, pl2)) + text("b", sourceSets = setOf(pl1)) + } + text("b", sourceSets = setOf(pl2)) + } + } + + HtmlRenderer(context).render(page) + renderedContent.match(Div(Div(Div(Div("ab"))), Div(Div(Div("a"), "b")))) + } + + @Test + fun caseWithGroupNotBreakingSimplification() { + val page = TestPage { + sourceSetDependentHint(sourceSets = setOf(pl1, pl2)) { + group { + text("a", sourceSets = setOf(pl1, pl2)) + text("b", sourceSets = setOf(pl1)) + } + text("b", sourceSets = setOf(pl2)) + } + } + + HtmlRenderer(context).render(page) + renderedContent.match(Div(Div("ab"))) + } + + @Test + fun partiallyUnifiedCase() { + val page = TestPage { + sourceSetDependentHint(sourceSets = setOf(pl1, pl2, pl3), styles = setOf(TextStyle.Block)) { + text("a", sourceSets = setOf(pl1)) + text("a", sourceSets = setOf(pl2)) + text("b", sourceSets = setOf(pl3)) + } + } + + HtmlRenderer(context).render(page) + renderedContent.match(Div(Div(Div("a")), Div(Div("b")))) + } +} diff --git a/plugins/base/src/test/kotlin/resourceLinks/ResourceLinksTest.kt b/plugins/base/src/test/kotlin/resourceLinks/ResourceLinksTest.kt new file mode 100644 index 00000000..4f8a834b --- /dev/null +++ b/plugins/base/src/test/kotlin/resourceLinks/ResourceLinksTest.kt @@ -0,0 +1,71 @@ +package resourceLinks + +import org.jetbrains.dokka.base.DokkaBase +import org.jetbrains.dokka.pages.RootPageNode +import org.jetbrains.dokka.plugability.DokkaPlugin +import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest +import org.jetbrains.dokka.transformers.pages.PageTransformer +import org.jsoup.Jsoup +import org.junit.jupiter.api.Test +import utils.TestOutputWriterPlugin + +class ResourceLinksTest : AbstractCoreTest() { + class TestResourcesAppenderPlugin(val resources: List<String>) : DokkaPlugin() { + class TestResourcesAppender(val resources: List<String>) : PageTransformer { + override fun invoke(input: RootPageNode) = input.transformContentPagesTree { + it.modified( + embeddedResources = it.embeddedResources + resources + ) + } + } + + val appender by extending { + plugin<DokkaBase>().htmlPreprocessors with TestResourcesAppender(resources) + } + } + @Test + fun resourceLinksTest() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin/test/Test.kt") + } + } + } + val absoluteResources = listOf( + "https://stackpath.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css", + "https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js" + ) + val relativeResources = listOf( + "test/relativePath.js", + "test/relativePath.css" + ) + + val source = + """ + |/src/main/kotlin/test/Test.kt + |package example + """.trimIndent() + val writerPlugin = TestOutputWriterPlugin() + testInline( + source, + configuration, + pluginOverrides = listOf(TestResourcesAppenderPlugin(absoluteResources + relativeResources), writerPlugin) + ) { + renderingStage = { + root, context -> Jsoup + .parse(writerPlugin.writer.contents["root/example.html"]) + .head() + .select("link, script") + .let { + absoluteResources.forEach { + r -> assert(it.`is`("[href=$r], [src=$r]")) + } + relativeResources.forEach { + r -> assert(it.`is`("[href=../$r] , [src=../$r]")) + } + } + } + } + } +} diff --git a/plugins/base/src/test/kotlin/signatures/DivergentSignatureTest.kt b/plugins/base/src/test/kotlin/signatures/DivergentSignatureTest.kt new file mode 100644 index 00000000..7635ab05 --- /dev/null +++ b/plugins/base/src/test/kotlin/signatures/DivergentSignatureTest.kt @@ -0,0 +1,175 @@ +package signatures + +import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest +import org.jsoup.Jsoup +import org.jsoup.nodes.Element +import org.jsoup.select.Elements +import org.junit.jupiter.api.Test +import java.nio.file.Paths +import utils.TestOutputWriterPlugin + +class DivergentSignatureTest : AbstractCoreTest() { + + @Test + fun `group { common + jvm + js }`() { + + val testDataDir = getTestDataDir("multiplatform/basicMultiplatformTest").toAbsolutePath() + + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + moduleName = "example" + displayName = "js" + name = "js" + analysisPlatform = "js" + sourceRoots = listOf("jsMain", "commonMain", "jvmAndJsSecondCommonMain").map { + Paths.get("$testDataDir/$it/kotlin").toString() + } + } + sourceSet { + moduleName = "example" + displayName = "jvm" + name = "jvm" + analysisPlatform = "jvm" + sourceRoots = listOf("jvmMain", "commonMain", "jvmAndJsSecondCommonMain").map { + Paths.get("$testDataDir/$it/kotlin").toString() + } + } + sourceSet { + moduleName = "example" + displayName = "common" + name = "common" + analysisPlatform = "common" + sourceRoots = listOf("commonMain").map { + Paths.get("$testDataDir/$it/kotlin").toString() + } + } + } + } + + val writerPlugin = TestOutputWriterPlugin() + + testFromData( + configuration, + pluginOverrides = listOf(writerPlugin) + ) { + renderingStage = { _, _ -> + val content = writerPlugin.renderedContent("example/example/-clock/get-time.html") + + assert(content.count() == 1) + assert(content.select("[data-filterable-current=example/js example/jvm example/common]").single().brief == "") + } + } + } + + @Test + fun `group { common + jvm }, group { js }`() { + + val testDataDir = getTestDataDir("multiplatform/basicMultiplatformTest").toAbsolutePath() + + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + moduleName = "example" + displayName = "js" + name = "js" + analysisPlatform = "js" + sourceRoots = listOf("jsMain", "commonMain", "jvmAndJsSecondCommonMain").map { + Paths.get("$testDataDir/$it/kotlin").toString() + } + } + sourceSet { + moduleName = "example" + displayName = "jvm" + name = "jvm" + analysisPlatform = "jvm" + sourceRoots = listOf("jvmMain", "commonMain", "jvmAndJsSecondCommonMain").map { + Paths.get("$testDataDir/$it/kotlin").toString() + } + } + sourceSet { + moduleName = "example" + displayName = "common" + name = "common" + analysisPlatform = "common" + sourceRoots = listOf("commonMain").map { + Paths.get("$testDataDir/$it/kotlin").toString() + } + } + } + } + + val writerPlugin = TestOutputWriterPlugin() + + testFromData( + configuration, + pluginOverrides = listOf(writerPlugin) + ) { + renderingStage = { _, _ -> + val content = writerPlugin.renderedContent("example/example/-clock/get-times-in-millis.html") + assert(content.count() == 2) + assert(content.select("[data-filterable-current=example/jvm example/common]").single().brief == "Time in minis") + assert(content.select("[data-filterable-current=example/js]").single().brief == "JS implementation of getTimeInMillis js" ) + } + } + } + + @Test + fun `group { js }, group { jvm }, group { js }`() { + + val testDataDir = getTestDataDir("multiplatform/basicMultiplatformTest").toAbsolutePath() + + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + moduleName = "example" + displayName = "js" + name = "js" + analysisPlatform = "js" + sourceRoots = listOf("jsMain", "commonMain", "jvmAndJsSecondCommonMain").map { + Paths.get("$testDataDir/$it/kotlin").toString() + } + } + sourceSet { + moduleName = "example" + displayName = "jvm" + name = "jvm" + analysisPlatform = "jvm" + sourceRoots = listOf("jvmMain", "commonMain", "jvmAndJsSecondCommonMain").map { + Paths.get("$testDataDir/$it/kotlin").toString() + } + } + sourceSet { + moduleName = "example" + displayName = "common" + name = "common" + analysisPlatform = "common" + sourceRoots = listOf("commonMain").map { + Paths.get("$testDataDir/$it/kotlin").toString() + } + } + } + } + + val writerPlugin = TestOutputWriterPlugin() + + testFromData( + configuration, + pluginOverrides = listOf(writerPlugin) + ) { + renderingStage = { _, _ -> + val content = writerPlugin.renderedContent("example/example/-clock/get-year.html") + assert(content.count() == 3) + assert(content.select("[data-filterable-current=example/jvm]").single().brief == "JVM custom kdoc jvm") + assert(content.select("[data-filterable-current=example/js]").single().brief == "JS custom kdoc js") + assert(content.select("[data-filterable-current=example/common]").single().brief == "common") + } + } + } + + private fun TestOutputWriterPlugin.renderedContent(path: String) = writer.contents.getValue(path) + .let { Jsoup.parse(it) }.select("#content").single().select("div.divergent-group") + + private val Element.brief: String + get() = children().select(".brief-with-platform-tags").text() +}
\ No newline at end of file diff --git a/plugins/base/src/test/kotlin/signatures/SignatureTest.kt b/plugins/base/src/test/kotlin/signatures/SignatureTest.kt new file mode 100644 index 00000000..9f2ae435 --- /dev/null +++ b/plugins/base/src/test/kotlin/signatures/SignatureTest.kt @@ -0,0 +1,379 @@ +package signatures + +import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest +import org.jsoup.Jsoup +import org.junit.jupiter.api.Test +import utils.* + +class SignatureTest : AbstractCoreTest() { + + fun source(signature: String) = + """ + |/src/main/kotlin/test/Test.kt + |package example + | + | $signature + """.trimIndent() + + @Test + fun `fun`() { + + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin/test/Test.kt") + } + } + } + + val source = source("fun simpleFun(): String = \"Celebrimbor\"") + val writerPlugin = TestOutputWriterPlugin() + + testInline( + source, + configuration, + pluginOverrides = listOf(writerPlugin) + ) { + renderingStage = { _, _ -> + writerPlugin.writer.renderedContent("root/example/simple-fun.html").match( + "fun ", A("simpleFun"), "(): ", A("String"), Span() + ) + } + } + } + + @Test + fun `open fun`() { + + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin/test/Test.kt") + } + } + } + + val source = source("open fun simpleFun(): String = \"Celebrimbor\"") + val writerPlugin = TestOutputWriterPlugin() + + testInline( + source, + configuration, + pluginOverrides = listOf(writerPlugin) + ) { + renderingStage = { _, _ -> + writerPlugin.writer.renderedContent("root/example/simple-fun.html").match( + "open fun ", A("simpleFun"), "(): ", A("String"), Span() + ) + } + } + } + + @Test + fun `open suspend fun`() { + + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin/test/Test.kt") + } + } + } + + val source = source("open suspend fun simpleFun(): String = \"Celebrimbor\"") + val writerPlugin = TestOutputWriterPlugin() + + testInline( + source, + configuration, + pluginOverrides = listOf(writerPlugin) + ) { + renderingStage = { _, _ -> + writerPlugin.writer.renderedContent("root/example/simple-fun.html").match( + "open suspend fun ", A("simpleFun"), "(): ", A("String"), Span() + ) + } + } + } + + @Test + fun `fun with params`() { + + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin/test/Test.kt") + } + } + } + + val source = source("fun simpleFun(a: Int, b: Boolean, c: Any): String = \"Celebrimbor\"") + val writerPlugin = TestOutputWriterPlugin() + + testInline( + source, + configuration, + pluginOverrides = listOf(writerPlugin) + ) { + renderingStage = { _, _ -> + writerPlugin.writer.renderedContent("root/example/simple-fun.html").match( + "fun ", A("simpleFun"), "(a: ", A("Int"), + ", b: ", A("Boolean"), ", c: ", A("Any"), + "): ", A("String"), Span() + ) + } + } + } + + @Test + fun `fun with function param`() { + + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin/test/Test.kt") + } + } + } + + val source = source("fun simpleFun(a: (Int) -> String): String = \"Celebrimbor\"") + val writerPlugin = TestOutputWriterPlugin() + + testInline( + source, + configuration, + pluginOverrides = listOf(writerPlugin) + ) { + renderingStage = { _, _ -> + writerPlugin.writer.renderedContent("root/example/simple-fun.html").match( + "fun ", A("simpleFun"), "(a: (", A("Int"), + ") -> ", A("String"), "): ", A("String"), Span() + ) + } + } + } + + @Test + fun `fun with generic param`() { + + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin/test/Test.kt") + } + } + } + + val source = source("fun <T> simpleFun(): T = \"Celebrimbor\" as T") + val writerPlugin = TestOutputWriterPlugin() + + testInline( + source, + configuration, + pluginOverrides = listOf(writerPlugin) + ) { + renderingStage = { _, _ -> + writerPlugin.writer.renderedContent("root/example/simple-fun.html").match( + "fun <", A("T"), " : ", A("Any"), "?> ", A("simpleFun"), "(): ", + A("T"), Span() + ) + } + } + } + + @Test + fun `fun with generic bounded param`() { + + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin/test/Test.kt") + } + } + } + + val source = source("fun <T : String> simpleFun(): T = \"Celebrimbor\" as T") + val writerPlugin = TestOutputWriterPlugin() + + testInline( + source, + configuration, + pluginOverrides = listOf(writerPlugin) + ) { + renderingStage = { _, _ -> + writerPlugin.writer.renderedContent("root/example/simple-fun.html").match( + "fun <", A("T"), " : ", A("String"), "> ", A("simpleFun"), + "(): ", A("T"), Span() + ) + } + } + } + + @Test + fun `fun with keywords, params and generic bound`() { + + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin/test/Test.kt") + } + } + } + + val source = source("inline suspend fun <T : String> simpleFun(a: Int, b: String): T = \"Celebrimbor\" as T") + val writerPlugin = TestOutputWriterPlugin() + + testInline( + source, + configuration, + pluginOverrides = listOf(writerPlugin) + ) { + renderingStage = { _, _ -> + writerPlugin.writer.renderedContent("root/example/simple-fun.html").match( + "inline suspend fun <", A("T"), " : ", A("String"), "> ", A("simpleFun"), + "(a: ", A("Int"), ", b: ", A("String"), "): ", A("T"), Span() + ) + } + } + } + + + @Test + fun `fun with annotation`() { + + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin/test/Test.kt") + } + } + } + + val source = """ + |/src/main/kotlin/test/Test.kt + |package example + | + | @MustBeDocumented() + | @Target(AnnotationTarget.FUNCTION) + | annotation class Marking + | + | @Marking() + | fun simpleFun(): String = "Celebrimbor" + """.trimIndent() + val writerPlugin = TestOutputWriterPlugin() + + testInline( + source, + configuration, + pluginOverrides = listOf(writerPlugin) + ) { + renderingStage = { _, _ -> + writerPlugin.writer.renderedContent("root/example/simple-fun.html").match( + Div( + Div("@", A("Marking"), "()") + ), + "fun ", A("simpleFun"), + "(): ", A("String"), Span() + ) + } + } + } + + @Test + fun `fun with two annotations`() { + + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin/test/Test.kt") + } + } + } + + val source = """ + |/src/main/kotlin/test/Test.kt + |package example + | + | @MustBeDocumented() + | @Target(AnnotationTarget.FUNCTION) + | annotation class Marking(val msg: String) + | + | @MustBeDocumented() + | @Target(AnnotationTarget.FUNCTION) + | annotation class Marking2(val int: Int) + | + | @Marking("Nenya") + | @Marking2(1) + | fun simpleFun(): String = "Celebrimbor" + """.trimIndent() + val writerPlugin = TestOutputWriterPlugin() + + testInline( + source, + configuration, + pluginOverrides = listOf(writerPlugin) + ) { + renderingStage = { _, _ -> + writerPlugin.writer.renderedContent("root/example/simple-fun.html") + .match( + Div( + Div("@", A("Marking"), "(", Span("msg = ", Span("\"Nenya\"")), Wbr, ")"), + Div("@", A("Marking2"), "(", Span("int = ", Span("1")), Wbr, ")") + ), + "fun ", A("simpleFun"), + "(): ", A("String"), Span() + ) + } + } + } + + @Test + fun `fun with annotation with array`() { + + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin/test/Test.kt") + } + } + } + + val source = """ + |/src/main/kotlin/test/Test.kt + |package example + | + | @MustBeDocumented() + | @Target(AnnotationTarget.FUNCTION) + | annotation class Marking(val msg: Array<String>) + | + | @Marking(["Nenya", "Vilya", "Narya"]) + | @Marking2(1) + | fun simpleFun(): String = "Celebrimbor" + """.trimIndent() + val writerPlugin = TestOutputWriterPlugin() + + testInline( + source, + configuration, + pluginOverrides = listOf(writerPlugin) + ) { + renderingStage = { _, _ -> + writerPlugin.writer.renderedContent("root/example/simple-fun.html").match( + Div( + Div("@", A("Marking"), "(", Span("msg = [", + Span(Span("\"Nenya\""), ", "), Wbr, + Span(Span("\"Vilya\""), ", "), Wbr, + Span(Span("\"Narya\"")), Wbr, "]"), Wbr, ")" + ) + ), + "fun ", A("simpleFun"), + "(): ", A("String"), Span() + ) + } + } + } + + private fun TestOutputWriter.renderedContent(path: String = "root/example.html") = + contents.getValue(path).let { Jsoup.parse(it) }.select("#content") + .single().select("div.symbol div.monospace").first() +}
\ No newline at end of file diff --git a/plugins/base/src/test/kotlin/transformerBuilders/PageTransformerBuilderTest.kt b/plugins/base/src/test/kotlin/transformerBuilders/PageTransformerBuilderTest.kt new file mode 100644 index 00000000..d8e057da --- /dev/null +++ b/plugins/base/src/test/kotlin/transformerBuilders/PageTransformerBuilderTest.kt @@ -0,0 +1,150 @@ +package transformerBuilders; + +import org.jetbrains.dokka.CoreExtensions +import org.jetbrains.dokka.pages.PageNode +import org.jetbrains.dokka.pages.RendererSpecificResourcePage +import org.jetbrains.dokka.pages.RenderingStrategy +import org.jetbrains.dokka.plugability.DokkaPlugin +import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest +import org.jetbrains.dokka.transformers.pages.PageTransformer +import org.jetbrains.dokka.transformers.pages.pageMapper +import org.jetbrains.dokka.transformers.pages.pageScanner +import org.jetbrains.dokka.transformers.pages.pageStructureTransformer +import org.junit.jupiter.api.Test + +class PageTransformerBuilderTest : AbstractCoreTest() { + + class ProxyPlugin(transformer: PageTransformer) : DokkaPlugin() { + val pageTransformer by extending { CoreExtensions.pageTransformer with transformer } + } + + @Test + fun scannerTest() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin/transformerBuilder/Test.kt") + } + } + } + val list = mutableListOf<String>() + + var orig: PageNode? = null + + testInline( + """ + |/src/main/kotlin/transformerBuilder/Test.kt + |package transformerBuilder + | + |object Test { + | fun test2(str: String): Unit {println(str)} + |} + """.trimMargin(), + configuration, + pluginOverrides = listOf(ProxyPlugin(pageScanner { + list += name + })) + ) { + pagesGenerationStage = { + orig = it + } + pagesTransformationStage = { root -> + list.assertCount(4, "Page list: ") + orig?.let { root.assertTransform(it) } + } + } + } + + @Test + fun mapperTest() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin/transformerBuilder/Test.kt") + } + } + } + + var orig: PageNode? = null + + testInline( + """ + |/src/main/kotlin/transformerBuilder/Test.kt + |package transformerBuilder + | + |object Test { + | fun test2(str: String): Unit {println(str)} + |} + """.trimMargin(), + configuration, + pluginOverrides = listOf(ProxyPlugin(pageMapper { + modified(name = name + "2") + })) + ) { + pagesGenerationStage = { + orig = it + } + pagesTransformationStage = { + it.let { root -> + root.name.assertEqual("root2", "Root name: ") + orig?.let { + root.assertTransform(it) { node -> node.modified(name = node.name + "2") } + } + } + } + } + } + + @Test + fun structureTransformerTest() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin/transformerBuilder/Test.kt") + } + } + } + + testInline( + """ + |/src/main/kotlin/transformerBuilder/Test.kt + |package transformerBuilder + | + |object Test { + | fun test2(str: String): Unit {println(str)} + |} + """.trimMargin(), + configuration, + pluginOverrides = listOf(ProxyPlugin(pageStructureTransformer { + val ch = children.first() + modified( + children = listOf( + ch, + RendererSpecificResourcePage("test", emptyList(), RenderingStrategy.DoNothing) + ) + ) + })) + ) { + pagesTransformationStage = { root -> + root.children.assertCount(2, "Root children: ") + root.children.first().name.assertEqual("transformerBuilder") + root.children[1].name.assertEqual("test") + } + } + } + + private fun <T> Collection<T>.assertCount(n: Int, prefix: String = "") = + assert(count() == n) { "${prefix}Expected $n, got ${count()}" } + + private fun <T> T.assertEqual(expected: T, prefix: String = "") = assert(this == expected) { + "${prefix}Expected $expected, got $this" + } + + private fun PageNode.assertTransform(expected: PageNode, block: (PageNode) -> PageNode = { it }): Unit = this.let { + it.name.assertEqual(block(expected).name) + it.children.zip(expected.children).forEach { (g, e) -> + g.name.assertEqual(block(e).name) + g.assertTransform(e, block) + } + } +} diff --git a/plugins/base/src/test/kotlin/transformers/CommentsToContentConverterTest.kt b/plugins/base/src/test/kotlin/transformers/CommentsToContentConverterTest.kt new file mode 100644 index 00000000..5197afc6 --- /dev/null +++ b/plugins/base/src/test/kotlin/transformers/CommentsToContentConverterTest.kt @@ -0,0 +1,403 @@ +package transformers + +import org.jetbrains.dokka.base.transformers.pages.comments.DocTagToContentConverter +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.doc.* +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test +import matchers.content.* +import org.jetbrains.dokka.pages.* +import org.jetbrains.kotlin.utils.addToStdlib.assertedCast + +class CommentsToContentConverterTest { + private val converter = DocTagToContentConverter + + private fun executeTest( + docTag:DocTag, + match: ContentMatcherBuilder<ContentComposite>.() -> Unit + ) { + val dci = DCI( + setOf( + DRI("kotlin", "Any") + ), + ContentKind.Comment + ) + converter.buildContent( + Li( + listOf( + docTag + ) + ), + dci, + emptySet() + ).single().assertNode(match) + } + + @Test + fun `simple text`() { + val docTag = P(listOf(Text("This is simple test of string Next line"))) + executeTest(docTag) { + +"This is simple test of string Next line" + } + } + + @Test + fun `simple text with new line`() { + val docTag = P( + listOf( + Text("This is simple test of string"), + Br, + Text("Next line") + ) + ) + executeTest(docTag) { + +"This is simple test of string" + node<ContentBreakLine>() + +"Next line" + } + } + + @Test + fun `paragraphs`() { + val docTag = P( + listOf( + P(listOf(Text("Paragraph number one"))), + P(listOf(Text("Paragraph"), Br, Text("number two"))) + ) + ) + executeTest(docTag) { + +"Paragraph number one" + +"Paragraph" + node<ContentBreakLine>() + +"number two" + } + } + + @Test + fun `unordered list with empty lines`() { + val docTag = Ul( + listOf( + Li(listOf(P(listOf(Text("list item 1 continue 1"))))), + Li(listOf(P(listOf(Text("list item 2"), Br, Text("continue 2"))))) + ) + ) + executeTest(docTag) { + node<ContentList> { + group { + +"list item 1 continue 1" + } + group { + +"list item 2" + node<ContentBreakLine>() + +"continue 2" + } + } + } + } + + @Test + fun `nested list`() { + val docTag = P( + listOf( + Ul( + listOf( + Li(listOf(P(listOf(Text("Outer first Outer next line"))))), + Li(listOf(P(listOf(Text("Outer second"))))), + Ul( + listOf( + Li(listOf(P(listOf(Text("Middle first Middle next line"))))), + Li(listOf(P(listOf(Text("Middle second"))))), + Ul( + listOf( + Li(listOf(P(listOf(Text("Inner first Inner next line"))))) + ) + ), + Li(listOf(P(listOf(Text("Middle third"))))) + ) + ), + Li(listOf(P(listOf(Text("Outer third"))))) + ) + ), + P(listOf(Text("New paragraph"))) + ) + ) + executeTest(docTag) { + node<ContentList> { + group { +"Outer first Outer next line" } + group { +"Outer second" } + node<ContentList> { + group { +"Middle first Middle next line" } + group { +"Middle second" } + node<ContentList> { + group { +"Inner first Inner next line" } + } + group { +"Middle third" } + } + group { +"Outer third" } + } + +"New paragraph" + } + } + + @Test + fun `header and paragraphs`() { + val docTag = P( + listOf( + H1(listOf(Text("Header 1"))), + P(listOf(Text("Following text"))), + P(listOf(Text("New paragraph"))) + ) + ) + executeTest(docTag) { + header(1) { +"Header 1" } + +"Following text" + +"New paragraph" + } + } + + @Test + fun `header levels`() { + val docTag = P( + listOf( + H1(listOf(Text("Header 1"))), + P(listOf(Text("Text 1"))), + H2(listOf(Text("Header 2"))), + P(listOf(Text("Text 2"))), + H3(listOf(Text("Header 3"))), + P(listOf(Text("Text 3"))), + H4(listOf(Text("Header 4"))), + P(listOf(Text("Text 4"))), + H5(listOf(Text("Header 5"))), + P(listOf(Text("Text 5"))), + H6(listOf(Text("Header 6"))), + P(listOf(Text("Text 6"))) + ) + ) + executeTest(docTag) { + header(1) {+"Header 1"} + +"Text 1" + header(2) {+"Header 2"} + +"Text 2" + header(3) {+"Header 3"} + +"Text 3" + header(4) {+"Header 4"} + +"Text 4" + header(5) {+"Header 5"} + +"Text 5" + header(6) {+"Header 6"} + +"Text 6" + } + } + + @Test + fun `block quotes`() { + val docTag = P( + listOf( + BlockQuote( + listOf( + P( + listOf( + Text("Blockquotes are very handy in email to emulate reply text. This line is part of the same quote.") + ) + ) + ) + ), + P(listOf(Text("Quote break."))), + BlockQuote( + listOf( + P(listOf(Text("Quote"))) + ) + ) + ) + ) + executeTest(docTag) { + node<ContentCodeBlock> { + +"Blockquotes are very handy in email to emulate reply text. This line is part of the same quote." + } + +"Quote break." + node<ContentCodeBlock> { + +"Quote" + } + } + } + + @Test + fun `nested block quotes`() { + val docTag = P( + listOf( + BlockQuote( + listOf( + P(listOf(Text("text 1 text 2"))), + BlockQuote( + listOf( + P(listOf(Text("text 3 text 4"))) + ) + ), + P(listOf(Text("text 5"))) + ) + ), + P(listOf(Text("Quote break."))), + BlockQuote( + listOf( + P(listOf(Text("Quote"))) + ) + ) + ) + ) + executeTest(docTag) { + node<ContentCodeBlock> { + +"text 1 text 2" + node<ContentCodeBlock> { + +"text 3 text 4" + } + +"text 5" + } + +"Quote break." + node<ContentCodeBlock> { + +"Quote" + } + } + } + + @Test + fun `multiline code`() { + val docTag = P( + listOf( + CodeBlock( + listOf( + Text("val x: Int = 0"), Br, + Text("val y: String = \"Text\""), Br, Br, + Text(" val z: Boolean = true"), Br, + Text("for(i in 0..10) {"), Br, + Text(" println(i)"), Br, + Text("}") + ), + mapOf("lang" to "kotlin") + ), + P(listOf(Text("Sample text"))) + ) + ) + executeTest(docTag) { + node<ContentCodeBlock> { + +"val x: Int = 0" + node<ContentBreakLine>() + +"val y: String = \"Text\"" + node<ContentBreakLine>() + node<ContentBreakLine>() + +" val z: Boolean = true" + node<ContentBreakLine>() + +"for(i in 0..10) {" + node<ContentBreakLine>() + +" println(i)" + node<ContentBreakLine>() + +"}" + } + +"Sample text" + } + } + + @Test + fun `inline link`() { + val docTag = P( + listOf( + A( + listOf(Text("I'm an inline-style link")), + mapOf("href" to "https://www.google.com") + ) + ) + ) + executeTest(docTag) { + link { + +"I'm an inline-style link" + check { + assertEquals( + assertedCast<ContentResolvedLink> { "Link should be resolved" }.address, + "https://www.google.com" + ) + } + } + } + } + + + + @Test + fun `ordered list`() { + val docTag = + Ol( + listOf( + Li( + listOf( + P(listOf(Text("test1"))), + P(listOf(Text("test2"))), + ) + ), + Li( + listOf( + P(listOf(Text("test3"))), + P(listOf(Text("test4"))), + ) + ) + ) + ) + executeTest(docTag) { + node<ContentList> { + group { + +"test1" + +"test2" + } + group { + +"test3" + +"test4" + } + } + } + } + + @Test + fun `nested ordered list`() { + val docTag = P( + listOf( + Ol( + listOf( + Li(listOf(P(listOf(Text("Outer first Outer next line"))))), + Li(listOf(P(listOf(Text("Outer second"))))), + Ol( + listOf( + Li(listOf(P(listOf(Text("Middle first Middle next line"))))), + Li(listOf(P(listOf(Text("Middle second"))))), + Ol( + listOf( + Li(listOf(P(listOf(Text("Inner first Inner next line"))))) + ), + mapOf("start" to "1") + ), + Li(listOf(P(listOf(Text("Middle third"))))) + ), + mapOf("start" to "1") + ), + Li(listOf(P(listOf(Text("Outer third"))))) + ), + mapOf("start" to "1") + ), + P(listOf(Text("New paragraph"))) + ) + ) + executeTest(docTag) { + node<ContentList> { + group { +"Outer first Outer next line" } + group { +"Outer second" } + node<ContentList> { + group { +"Middle first Middle next line" } + group { +"Middle second" } + node<ContentList> { + +"Inner first Inner next line" + } + group { +"Middle third" } + } + group { +"Outer third" } + } + +"New paragraph" + } + } +}
\ No newline at end of file diff --git a/plugins/base/src/test/kotlin/transformers/ReportUndocumentedTransformerTest.kt b/plugins/base/src/test/kotlin/transformers/ReportUndocumentedTransformerTest.kt new file mode 100644 index 00000000..72948372 --- /dev/null +++ b/plugins/base/src/test/kotlin/transformers/ReportUndocumentedTransformerTest.kt @@ -0,0 +1,919 @@ +package transformers + +import org.jetbrains.dokka.PackageOptionsImpl +import org.jetbrains.dokka.Platform +import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test + + +class ReportUndocumentedTransformerTest : AbstractCoreTest() { + @Test + fun `undocumented class gets reported`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + reportUndocumented = true + sourceRoots = listOf("src/main/kotlin/Test.kt") + } + } + } + + testInline( + """ + |/src/main/kotlin/Test.kt + |package sample + | + |class X + """.trimMargin(), + configuration + ) { + pagesTransformationStage = { + assertNoUndocumentedReport(Regex("init")) + assertSingleUndocumentedReport(Regex("""sample/X/""")) + } + } + } + + @Test + fun `undocumented non-public class does not get reported`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + reportUndocumented = true + sourceRoots = listOf("src/main/kotlin/Test.kt") + } + } + } + + testInline( + """ + |/src/main/kotlin/Test.kt + |package sample + | + |internal class X + """.trimMargin(), + configuration + ) { + pagesTransformationStage = { + assertNoUndocumentedReport() + } + } + } + + @Test + fun `undocumented function gets reported`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + reportUndocumented = true + sourceRoots = listOf("src/main/kotlin/Test.kt") + } + } + } + + testInline( + """ + |/src/main/kotlin/Test.kt + |package sample + | + |/** Documented */ + |class X { + | fun x() + |} + """.trimMargin(), + configuration + ) { + pagesTransformationStage = { + assertSingleUndocumentedReport(Regex("X")) + assertSingleUndocumentedReport(Regex("X/x")) + } + } + } + + @Test + fun `undocumented property gets reported`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + reportUndocumented = true + sourceRoots = listOf("src/main/kotlin/Test.kt") + } + } + } + + testInline( + """ + |/src/main/kotlin/Test.kt + |package sample + | + |/** Documented */ + |class X { + | val x: Int = 0 + |} + """.trimMargin(), + configuration + ) { + pagesTransformationStage = { + assertSingleUndocumentedReport(Regex("X")) + assertSingleUndocumentedReport(Regex("X/x")) + } + } + } + + @Test + fun `undocumented primary constructor does not get reported`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + reportUndocumented = true + sourceRoots = listOf("src/main/kotlin/Test.kt") + } + } + } + + testInline( + """ + |/src/main/kotlin/Test.kt + |package sample + | + |/** Documented */ + |class X(private val x: Int) { + |} + """.trimMargin(), + configuration + ) { + pagesTransformationStage = { + assertNoUndocumentedReport() + } + } + } + + @Test + fun `data class component functions do not get reported`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + reportUndocumented = true + sourceRoots = listOf("src/main/kotlin") + } + } + } + + testInline( + """ + |/src/main/kotlin/Test.kt + |package sample + | + |/** Documented */ + |data class X(val x: Int) { + |} + """.trimMargin(), + configuration + ) { + pagesTransformationStage = { + assertNoUndocumentedReport(Regex("component")) + assertNumberOfUndocumentedReports(1) + } + } + } + + @Disabled + @Test + fun `undocumented secondary constructor gets reported`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + reportUndocumented = true + sourceRoots = listOf("src/main/kotlin/Test.kt") + } + } + } + + testInline( + """ + |/src/main/kotlin/Test.kt + |package sample + | + |/** Documented */ + |class X { + | constructor(unit: Unit) : this() + |} + """.trimMargin(), + configuration + ) { + pagesTransformationStage = { + assertSingleUndocumentedReport(Regex("X")) + assertSingleUndocumentedReport(Regex("X.*init.*Unit")) + } + } + } + + @Test + fun `undocumented inherited function does not get reported`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + reportUndocumented = true + sourceRoots = listOf("src/main/kotlin/Test.kt") + } + } + } + + testInline( + """ + |/src/main/kotlin/Test.kt + |package sample + | + |/** Documented */ + |open class A { + | fun a() = Unit + |} + | + |/** Documented */ + |class B : A() + """.trimMargin(), + configuration + ) { + pagesTransformationStage = { + assertNoUndocumentedReport(Regex("B")) + assertSingleUndocumentedReport(Regex("A.*a")) + } + } + } + + @Test + fun `undocumented inherited property does not get reported`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + reportUndocumented = true + sourceRoots = listOf("src/main/kotlin/Test.kt") + } + } + } + + testInline( + """ + |/src/main/kotlin/Test.kt + |package sample + | + |/** Documented */ + |open class A { + | val a = Unit + |} + | + |/** Documented */ + |class B : A() + """.trimMargin(), + configuration + ) { + pagesTransformationStage = { + assertNoUndocumentedReport(Regex("B")) + assertSingleUndocumentedReport(Regex("A.*a")) + } + } + } + + @Test + fun `overridden function does not get reported when super is documented`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + reportUndocumented = true + sourceRoots = listOf("src/main/kotlin/Test.kt") + } + } + } + + testInline( + """ + |/src/main/kotlin/Test.kt + |package sample + |import kotlin.Exception + | + |/** Documented */ + |open class A { + | /** Documented */ + | fun a() = Unit + |} + | + |/** Documented */ + |class B : A() { + | override fun a() = throw Exception() + |} + """.trimMargin(), + configuration + ) { + pagesTransformationStage = { + assertNoUndocumentedReport() + } + } + } + + @Test + fun `overridden property does not get reported when super is documented`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + reportUndocumented = true + sourceRoots = listOf("src/main/kotlin/Test.kt") + } + } + } + + testInline( + """ + |/src/main/kotlin/Test.kt + |package sample + |import kotlin.Exception + | + |/** Documented */ + |open class A { + | /** Documented */ + | open val a = 0 + |} + | + |/** Documented */ + |class B : A() { + | override val a = 1 + |} + """.trimMargin(), + configuration + ) { + pagesTransformationStage = { + assertNoUndocumentedReport() + } + } + } + + @Test + fun `report disabled by source set`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + reportUndocumented = false + sourceRoots = listOf("src/main/kotlin/Test.kt") + } + } + } + + testInline( + """ + |/src/main/kotlin/Test.kt + |package sample + | + |class X + """.trimMargin(), + configuration + ) { + pagesTransformationStage = { + assertNoUndocumentedReport() + } + } + } + + @Test + fun `report enabled by package configuration`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + perPackageOptions += packageOptions( + prefix = "sample", + reportUndocumented = true, + ) + reportUndocumented = false + sourceRoots = listOf("src/main/kotlin/Test.kt") + } + } + } + + testInline( + """ + |/src/main/kotlin/Test.kt + |package sample + | + |class X + """.trimMargin(), + configuration + ) { + pagesTransformationStage = { + assertSingleUndocumentedReport(Regex("X")) + } + } + } + + @Test + fun `report enabled by more specific package configuration`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + perPackageOptions += packageOptions( + prefix = "sample", + reportUndocumented = false, + ) + perPackageOptions += packageOptions( + prefix = "sample.enabled", + reportUndocumented = true, + ) + reportUndocumented = false + sourceRoots = listOf("src/main/kotlin/") + } + } + } + + testInline( + """ + |/src/main/kotlin/sample/disabled/Disabled.kt + |package sample.disabled + |class Disabled + | + |/src/main/kotlin/sample/enabled/Enabled.kt + |package sample.enabled + |class Enabled + """.trimMargin(), + configuration + ) { + pagesTransformationStage = { + assertSingleUndocumentedReport(Regex("Enabled")) + assertNumberOfUndocumentedReports(1) + } + } + } + + @Test + fun `report disabled by more specific package configuration`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + perPackageOptions += packageOptions( + prefix = "sample", + reportUndocumented = true, + ) + perPackageOptions += packageOptions( + prefix = "sample.disabled", + reportUndocumented = false, + ) + reportUndocumented = true + sourceRoots = listOf("src/main/kotlin/") + } + } + } + + testInline( + """ + |/src/main/kotlin/sample/disabled/Disabled.kt + |package sample.disabled + |class Disabled + | + |/src/main/kotlin/sample/enabled/Enabled.kt + |package sample.enabled + |class Enabled + """.trimMargin(), + configuration + ) { + pagesTransformationStage = { + assertSingleUndocumentedReport(Regex("Enabled")) + assertNumberOfUndocumentedReports(1) + } + } + } + + @Test + fun `multiplatform undocumented class gets reported`() { + val configuration = dokkaConfiguration { + sourceSets { + val commonMain = sourceSet { + reportUndocumented = true + analysisPlatform = Platform.common.toString() + name = "commonMain" + displayName = "commonMain" + sourceRoots = listOf("src/commonMain/kotlin") + } + + sourceSet { + reportUndocumented = true + analysisPlatform = Platform.jvm.toString() + name = "jvmMain" + displayName = "jvmMain" + sourceRoots = listOf("src/jvmMain/kotlin") + dependentSourceSets = setOf(commonMain.sourceSetID) + } + } + } + + testInline( + """ + |/src/commonMain/kotlin/sample/Common.kt + |package sample + |expect class X + | + |/src/jvmMain/kotlin/sample/JvmMain.kt + |package sample + |actual class X + """.trimMargin(), + configuration + ) { + pagesTransformationStage = { + assertNumberOfUndocumentedReports(2, Regex("X")) + assertSingleUndocumentedReport(Regex("X.*jvmMain")) + assertSingleUndocumentedReport(Regex("X.*commonMain")) + } + } + } + + @Test + fun `multiplatform undocumented class does not get reported if expect is documented`() { + val configuration = dokkaConfiguration { + sourceSets { + val commonMain = sourceSet { + reportUndocumented = true + analysisPlatform = Platform.common.toString() + name = "commonMain" + displayName = "commonMain" + sourceRoots = listOf("src/commonMain/kotlin") + } + + sourceSet { + reportUndocumented = true + analysisPlatform = Platform.jvm.toString() + name = "jvmMain" + displayName = "jvmMain" + sourceRoots = listOf("src/jvmMain/kotlin") + dependentSourceSets = setOf(commonMain.sourceSetID) + } + } + } + + testInline( + """ + |/src/commonMain/kotlin/sample/Common.kt + |package sample + |/** Documented */ + |expect class X + | + |/src/jvmMain/kotlin/sample/JvmMain.kt + |package sample + |actual class X + """.trimMargin(), + configuration + ) { + pagesTransformationStage = { + assertNumberOfUndocumentedReports(0) + } + } + } + + @Test + fun `multiplatform undocumented function gets reported`() { + val configuration = dokkaConfiguration { + sourceSets { + val commonMain = sourceSet { + reportUndocumented = true + analysisPlatform = Platform.common.toString() + name = "commonMain" + displayName = "commonMain" + sourceRoots = listOf("src/commonMain/kotlin") + } + + sourceSet { + reportUndocumented = true + analysisPlatform = Platform.jvm.toString() + name = "jvmMain" + displayName = "jvmMain" + sourceRoots = listOf("src/jvmMain/kotlin") + dependentSourceSets = setOf(commonMain.sourceSetID) + } + + sourceSet { + reportUndocumented = true + analysisPlatform = Platform.native.toString() + name = "macosMain" + displayName = "macosMain" + sourceRoots = listOf("src/macosMain/kotlin") + dependentSourceSets = setOf(commonMain.sourceSetID) + } + } + } + + testInline( + """ + |/src/commonMain/kotlin/sample/Common.kt + |package sample + |expect fun x() + | + |/src/macosMain/kotlin/sample/MacosMain.kt + |package sample + |/** Documented */ + |actual fun x() = Unit + | + |/src/jvmMain/kotlin/sample/JvmMain.kt + |package sample + |actual fun x() = Unit + """.trimMargin(), + configuration + ) { + pagesTransformationStage = { + assertNumberOfUndocumentedReports(2) + assertSingleUndocumentedReport(Regex("x.*commonMain")) + assertSingleUndocumentedReport(Regex("x.*jvmMain")) + } + } + } + + @Test + fun `java undocumented class gets reported`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + reportUndocumented = true + sourceRoots = listOf("src/main/java") + } + } + } + + testInline( + """ + |/src/main/java/sample/Test.java + |package sample + |public class Test { } + """.trimMargin(), + configuration + ) { + pagesTransformationStage = { + assertNoUndocumentedReport(Regex("init")) + assertSingleUndocumentedReport(Regex("""Test""")) + assertNumberOfUndocumentedReports(1) + } + } + } + + @Test + fun `java undocumented non-public class does not get reported`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + reportUndocumented = true + sourceRoots = listOf("src/main/java") + } + } + } + + testInline( + """ + |/src/main/java/sample/Test.java + |package sample + |class Test { } + """.trimMargin(), + configuration + ) { + pagesTransformationStage = { + assertNoUndocumentedReport() + } + } + } + + @Test + fun `java undocumented constructor does not get reported`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + reportUndocumented = true + sourceRoots = listOf("src/main/java") + } + } + } + + testInline( + """ + |/src/main/java/sample/Test.java + |package sample + |/** Documented */ + |public class Test { + | public Test() { + | } + |} + """.trimMargin(), + configuration + ) { + pagesTransformationStage = { + assertNoUndocumentedReport() + } + } + } + + @Test + fun `java undocumented method gets reported`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + reportUndocumented = true + sourceRoots = listOf("src/main/java") + } + } + } + + testInline( + """ + |/src/main/java/sample/X.java + |package sample + |/** Documented */ + |public class X { + | public void x { } + |} + """.trimMargin(), + configuration + ) { + pagesTransformationStage = { + assertSingleUndocumentedReport(Regex("X")) + assertSingleUndocumentedReport(Regex("X.*x")) + assertNumberOfUndocumentedReports(1) + } + } + } + + @Test + fun `java undocumented property gets reported`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + reportUndocumented = true + sourceRoots = listOf("src/main/java") + } + } + } + + testInline( + """ + |/src/main/java/sample/X.java + |package sample + |/** Documented */ + |public class X { + | public int x = 0; + |} + """.trimMargin(), + configuration + ) { + pagesTransformationStage = { + assertSingleUndocumentedReport(Regex("X")) + assertSingleUndocumentedReport(Regex("X.*x")) + assertNumberOfUndocumentedReports(1) + } + } + } + + @Test + fun `java undocumented inherited method gets reported`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + reportUndocumented = true + sourceRoots = listOf("src/main/java") + } + } + } + + testInline( + """ + |/src/main/java/sample/Super.java + |package sample + |/** Documented */ + |public class Super { + | public void x() {} + |} + | + |/src/main/java/sample/X.java + |package sample + |/** Documented */ + |public class X extends Super { + | public void x() {} + |} + | + """.trimMargin(), + configuration + ) { + pagesTransformationStage = { + assertSingleUndocumentedReport(Regex("X")) + assertSingleUndocumentedReport(Regex("X.*x")) + assertSingleUndocumentedReport(Regex("Super.*x")) + assertNumberOfUndocumentedReports(2) + } + } + } + + @Test + fun `java documented inherited method does not get reported`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + reportUndocumented = true + sourceRoots = listOf("src/main/java") + } + } + } + + testInline( + """ + |/src/main/java/sample/Super.java + |package sample + |/** Documented */ + |public class Super { + | /** Documented */ + | public void x() {} + |} + | + |/src/main/java/sample/X.java + |package sample + |/** Documented */ + |public class X extends Super { + | + |} + | + """.trimMargin(), + configuration + ) { + pagesTransformationStage = { + assertNoUndocumentedReport() + } + } + } + + @Test + fun `java overridden function does not get reported when super is documented`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + reportUndocumented = true + sourceRoots = listOf("src/main/java") + } + } + } + + testInline( + """ + |/src/main/java/sample/Super.java + |package sample + |/** Documented */ + |public class Super { + | /** Documented */ + | public void x() {} + |} + | + |/src/main/java/sample/X.java + |package sample + |/** Documented */ + |public class X extends Super { + | @Override + | public void x() {} + |} + | + """.trimMargin(), + configuration + ) { + pagesTransformationStage = { + assertNoUndocumentedReport() + } + } + } + + private fun assertNumberOfUndocumentedReports(expectedReports: Int, regex: Regex = Regex(".")) { + val reports = logger.warnMessages + .filter { it.startsWith("Undocumented:") } + val matchingReports = reports + .filter { it.contains(regex) } + + assertEquals( + expectedReports, matchingReports.size, + "Expected $expectedReports report of documented code ($regex).\n" + + "Found matching reports: $matchingReports\n" + + "Found reports: $reports" + ) + } + + private fun assertSingleUndocumentedReport(regex: Regex) { + assertNumberOfUndocumentedReports(1, regex) + } + + private fun assertNoUndocumentedReport(regex: Regex) { + assertNumberOfUndocumentedReports(0, regex) + } + + private fun assertNoUndocumentedReport() { + assertNoUndocumentedReport(Regex(".")) + } + + private fun packageOptions( + prefix: String, + reportUndocumented: Boolean?, + includeNonPublic: Boolean = true, + skipDeprecated: Boolean = false, + suppress: Boolean = false + ) = PackageOptionsImpl( + prefix = prefix, + reportUndocumented = reportUndocumented, + includeNonPublic = includeNonPublic, + skipDeprecated = skipDeprecated, + suppress = suppress + ) +} diff --git a/plugins/base/src/test/kotlin/translators/DefaultDescriptorToDocumentableTranslatorTest.kt b/plugins/base/src/test/kotlin/translators/DefaultDescriptorToDocumentableTranslatorTest.kt new file mode 100644 index 00000000..b0754429 --- /dev/null +++ b/plugins/base/src/test/kotlin/translators/DefaultDescriptorToDocumentableTranslatorTest.kt @@ -0,0 +1,87 @@ +package translators + +import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest +import org.junit.jupiter.api.Assertions +import org.junit.jupiter.api.Test + +class DefaultDescriptorToDocumentableTranslatorTest : AbstractCoreTest() { + + @Test + fun `data class kdocs over generated methods`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin") + } + } + } + + testInline( + """ + |/src/main/kotlin/sample/XD.kt + |package sample + |/** + | * But the fat Hobbit, he knows. Eyes always watching. + | */ + |data class XD(val xd: String) { + | /** + | * But the fat Hobbit, he knows. Eyes always watching. + | */ + | fun custom(): String = "" + | + | /** + | * Memory is not what the heart desires. That is only a mirror. + | */ + | override fun equals(other: Any?): Boolean = true + |} + """.trimIndent(), + configuration + ) { + documentablesMergingStage = { module -> + assert(module.documentationOf("XD", "copy") == "") + assert(module.documentationOf("XD", "equals") == "Memory is not what the heart desires. That is only a mirror.") + assert(module.documentationOf("XD", "hashCode") == "") + assert(module.documentationOf("XD", "toString") == "") + assert(module.documentationOf("XD", "custom") == "But the fat Hobbit, he knows. Eyes always watching.") + } + } + } + + @Test + fun `simple class kdocs`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin") + } + } + } + + testInline( + """ + |/src/main/kotlin/sample/XD.kt + |package sample + |/** + | * But the fat Hobbit, he knows. Eyes always watching. + | */ + |class XD(val xd: String) { + | /** + | * But the fat Hobbit, he knows. Eyes always watching. + | */ + | fun custom(): String = "" + | + | /** + | * Memory is not what the heart desires. That is only a mirror. + | */ + | override fun equals(other: Any?): Boolean = true + |} + """.trimIndent(), + configuration + ) { + documentablesMergingStage = { module -> + assert(module.documentationOf("XD", "custom") == "But the fat Hobbit, he knows. Eyes always watching.") + assert(module.documentationOf("XD", "equals") == "Memory is not what the heart desires. That is only a mirror.") + } + } + } +}
\ No newline at end of file diff --git a/plugins/base/src/test/kotlin/translators/DefaultPsiToDocumentableTranslatorTest.kt b/plugins/base/src/test/kotlin/translators/DefaultPsiToDocumentableTranslatorTest.kt new file mode 100644 index 00000000..eb682b14 --- /dev/null +++ b/plugins/base/src/test/kotlin/translators/DefaultPsiToDocumentableTranslatorTest.kt @@ -0,0 +1,144 @@ +package translators + +import org.jetbrains.dokka.model.DModule +import org.jetbrains.dokka.model.doc.Description +import org.jetbrains.dokka.model.doc.Text +import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test + +class DefaultPsiToDocumentableTranslatorTest : AbstractCoreTest() { + + @Test + fun `method overriding two documented classes picks closest class documentation`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/java") + } + } + } + + testInline( + """ + |/src/main/java/sample/BaseClass1.java + |package sample + |public class BaseClass1 { + | /** B1 */ + | void x() { } + |} + | + |/src/main/java/sample/BaseClass2.java + |package sample + |public class BaseClass2 extends BaseClass1 { + | /** B2 */ + | void x() { } + |} + | + |/src/main/java/sample/X.java + |package sample + |public class X extends BaseClass2 { + | void x() { } + |} + """.trimIndent(), + configuration + ) { + documentablesMergingStage = { module -> + val documentationOfFunctionX = module.documentationOf("X", "x") + assertTrue( + "B2" in documentationOfFunctionX, + "Expected nearest super method documentation to be parsed as documentation. " + + "Documentation: $documentationOfFunctionX" + ) + } + } + } + + @Test + fun `method overriding class and interface picks class documentation`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/java") + } + } + } + + testInline( + """ + |/src/main/java/sample/BaseClass1.java + |package sample + |public class BaseClass1 { + | /** B1 */ + | void x() { } + |} + | + |/src/main/java/sample/Interface1.java + |package sample + |public interface Interface1 { + | /** I1 */ + | void x() {} + |} + | + |/src/main/java/sample/X.java + |package sample + |public class X extends BaseClass1 implements Interface1 { + | void x() { } + |} + """.trimMargin(), + configuration + ) { + documentablesMergingStage = { module -> + val documentationOfFunctionX = module.documentationOf("X", "x") + assertTrue( + "B1" in documentationOfFunctionX, + "Expected documentation of superclass being prioritized over interface " + + "Documentation: $documentationOfFunctionX" + ) + } + } + } + + @Test + fun `method overriding two classes picks closest documented class documentation`() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/java") + } + } + } + + testInline( + """ + |/src/main/java/sample/BaseClass1.java + |package sample + |public class BaseClass1 { + | /** B1 */ + | void x() { } + |} + | + |/src/main/java/sample/BaseClass2.java + |package sample + |public class BaseClass2 extends BaseClass1 { + | void x() {} + |} + | + |/src/main/java/sample/X.java + |package sample + |public class X extends BaseClass2 { + | void x() { } + |} + """.trimMargin(), + configuration + ) { + documentablesMergingStage = { module -> + val documentationOfFunctionX = module.documentationOf("X", "x") + assertTrue( + "B1" in documentationOfFunctionX, + "Expected Documentation \"B1\", found: \"$documentationOfFunctionX\"" + ) + } + } + } +} diff --git a/plugins/base/src/test/kotlin/translators/utils.kt b/plugins/base/src/test/kotlin/translators/utils.kt new file mode 100644 index 00000000..96d3035a --- /dev/null +++ b/plugins/base/src/test/kotlin/translators/utils.kt @@ -0,0 +1,16 @@ +package translators + +import org.jetbrains.dokka.model.DModule +import org.jetbrains.dokka.model.doc.Description +import org.jetbrains.dokka.model.doc.Text + +fun DModule.documentationOf(className: String, functionName: String): String { + return (packages.single() + .classlikes.single { it.name == className } + .functions.single { it.name == functionName } + .documentation.values.singleOrNull() + ?.children?.singleOrNull() + .run { this as? Description } + ?.root?.children?.single() as? Text) + ?.body.orEmpty() +}
\ No newline at end of file diff --git a/plugins/base/src/test/kotlin/utils/JsoupUtils.kt b/plugins/base/src/test/kotlin/utils/JsoupUtils.kt new file mode 100644 index 00000000..e8c7838d --- /dev/null +++ b/plugins/base/src/test/kotlin/utils/JsoupUtils.kt @@ -0,0 +1,29 @@ +package utils + +import org.jsoup.nodes.Element +import org.jsoup.nodes.Node +import org.jsoup.nodes.TextNode + +fun Element.match(vararg matchers: Any): Unit = + childNodes() + .filter { it !is TextNode || it.text().isNotBlank() } + .let { it.drop(it.size - matchers.size) } + .zip(matchers) + .forEach { (n, m) -> m.accepts(n) } + +open class Tag(val name: String, vararg val matchers: Any) +class Div(vararg matchers: Any) : Tag("div", *matchers) +class P(vararg matchers: Any) : Tag("p", *matchers) +class Span(vararg matchers: Any) : Tag("span", *matchers) +class A(vararg matchers: Any) : Tag("a", *matchers) +object Wbr : Tag("wbr") +private fun Any.accepts(n: Node) { + when (this) { + is String -> assert(n is TextNode && n.text().trim() == this.trim()) { "\"$this\" expected but found: $n" } + is Tag -> { + assert(n is Element && n.tagName() == name) { "Tag $name expected but found: $n" } + if (n is Element && matchers.isNotEmpty()) n.match(*matchers) + } + else -> throw IllegalArgumentException("$this is not proper matcher") + } +}
\ No newline at end of file diff --git a/plugins/base/src/test/kotlin/utils/ModelUtils.kt b/plugins/base/src/test/kotlin/utils/ModelUtils.kt new file mode 100644 index 00000000..87a9c802 --- /dev/null +++ b/plugins/base/src/test/kotlin/utils/ModelUtils.kt @@ -0,0 +1,37 @@ +package utils + +import org.jetbrains.dokka.DokkaConfigurationImpl +import org.jetbrains.dokka.model.DModule +import org.jetbrains.dokka.plugability.DokkaPlugin + +abstract class AbstractModelTest(val path: String? = null, val pkg: String) : ModelDSL(), AssertDSL { + + fun inlineModelTest( + query: String, + platform: String = "jvm", + prependPackage: Boolean = true, + cleanupOutput: Boolean = true, + pluginsOverrides: List<DokkaPlugin> = emptyList(), + configuration: DokkaConfigurationImpl? = null, + block: DModule.() -> Unit + ) { + val testConfiguration = configuration ?: dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/") + analysisPlatform = platform + } + } + } + val prepend = path.let { p -> p?.let { "|$it\n" } ?: "" } + if (prependPackage) "|package $pkg" else "" + + testInline( + query = ("$prepend\n$query").trim().trimIndent(), + configuration = testConfiguration, + cleanupOutput = cleanupOutput, + pluginOverrides = pluginsOverrides + ) { + documentablesTransformationStage = block + } + } +} diff --git a/plugins/base/src/test/kotlin/utils/TestUtils.kt b/plugins/base/src/test/kotlin/utils/TestUtils.kt new file mode 100644 index 00000000..bd0e1fe2 --- /dev/null +++ b/plugins/base/src/test/kotlin/utils/TestUtils.kt @@ -0,0 +1,79 @@ +package utils + +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.doc.* +import org.jetbrains.dokka.model.doc.P +import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest +import org.junit.jupiter.api.Assertions.assertTrue +import kotlin.collections.orEmpty + +@DslMarker +annotation class TestDSL + +@TestDSL +abstract class ModelDSL : AbstractCoreTest() { + operator fun Documentable?.div(name: String): Documentable? = + this?.children?.find { it.name == name } + + inline fun <reified T : Documentable> Documentable?.cast(): T = + (this as? T).assertNotNull() +} + +@TestDSL +interface AssertDSL { + infix fun Any?.equals(other: Any?) = this.assertEqual(other) + infix fun Collection<Any>?.allEquals(other: Any?) = + this?.also { c -> c.forEach { it equals other } } ?: run { assert(false) { "Collection is empty" } } + infix fun <T> Collection<T>?.exists(e: T) { + assertTrue(this.orEmpty().isNotEmpty(), "Collection cannot be null or empty") + assertTrue(this!!.any{it == e}, "Collection doesn't contain $e") + } + + infix fun <T> Collection<T>?.counts(n: Int) = this.orEmpty().assertCount(n) + + infix fun <T> T?.notNull(name: String): T = this.assertNotNull(name) + + fun <T> Collection<T>.assertCount(n: Int, prefix: String = "") = + assert(count() == n) { "${prefix}Expected $n, got ${count()}" } + + fun <T> T?.assertEqual(expected: T, prefix: String = "") = assert(this == expected) { + "${prefix}Expected $expected, got $this" + } +} + +inline fun <reified T : Any> Any?.assertIsInstance(name: String): T = + this.let { it as? T } ?: throw AssertionError("$name should not be null") + +fun TagWrapper.text(): String = when (val t = this) { + is NamedTagWrapper -> "${t.name}: [${t.root.text()}]" + else -> t.root.text() +} + +fun DocTag.text(): String = when (val t = this) { + is Text -> t.body + is Code -> t.children.joinToString("\n") { it.text() } + is P -> t.children.joinToString(separator = "\n") { it.text() } + else -> t.toString() +} + +fun <T : Documentable> T?.comments(): String = docs().map { it.text() } + .joinToString(separator = "\n") { it } + +fun <T> T?.assertNotNull(name: String = ""): T = this ?: throw AssertionError("$name should not be null") + +fun <T : Documentable> T?.docs() = this?.documentation.orEmpty().values.flatMap { it.children } + +val DClass.supers + get() = supertypes.flatMap { it.component2() } + +val Bound.name: String? + get() = when (this) { + is Nullable -> inner.name + is OtherParameter -> name + is PrimitiveJavaType -> name + is TypeConstructor -> dri.classNames + is JavaObject -> "Object" + is Void -> "void" + is Dynamic -> "dynamic" + is UnresolvedBound -> "<ERROR CLASS>" + }
\ No newline at end of file diff --git a/plugins/base/src/test/kotlin/utils/contentUtils.kt b/plugins/base/src/test/kotlin/utils/contentUtils.kt new file mode 100644 index 00000000..7fcd8e89 --- /dev/null +++ b/plugins/base/src/test/kotlin/utils/contentUtils.kt @@ -0,0 +1,243 @@ +package utils + +import matchers.content.* +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.pages.ContentGroup +import kotlin.text.Typography.nbsp + +//TODO: Try to unify those functions after update to 1.4 +fun ContentMatcherBuilder<*>.functionSignature( + annotations: Map<String, Set<String>>, + visibility: String, + modifier: String, + keywords: Set<String>, + name: String, + returnType: String? = null, + vararg params: Pair<String, ParamAttributes> +) = + platformHinted { + bareSignature(annotations, visibility, modifier, keywords, name, returnType, *params) + } + +fun ContentMatcherBuilder<*>.bareSignature( + annotations: Map<String, Set<String>>, + visibility: String, + modifier: String, + keywords: Set<String>, + name: String, + returnType: String? = null, + vararg params: Pair<String, ParamAttributes> +) = group { + annotations.entries.forEach { + group { + unwrapAnnotation(it) + } + } + +("$visibility $modifier ${keywords.joinToString("") { "$it " }} fun") + link { +name } + +"(" + params.forEachIndexed { id, (n, t) -> + + t.annotations.forEach { + unwrapAnnotation(it) + } + t.keywords.forEach { + +it + } + + +"$n:" + group { link { +(t.type) } } + if (id != params.lastIndex) + +", " + } + +")" + if (returnType != null) { + +(": ") + group { + link { + +(returnType) + } + } + } +} + +fun ContentMatcherBuilder<*>.functionSignatureWithReceiver( + annotations: Map<String, Set<String>>, + visibility: String?, + modifier: String?, + keywords: Set<String>, + receiver: String, + name: String, + returnType: String? = null, + vararg params: Pair<String, ParamAttributes> +) = + platformHinted { + bareSignatureWithReceiver(annotations, visibility, modifier, keywords, receiver, name, returnType, *params) + } + +fun ContentMatcherBuilder<*>.bareSignatureWithReceiver( + annotations: Map<String, Set<String>>, + visibility: String?, + modifier: String?, + keywords: Set<String>, + receiver: String, + name: String, + returnType: String? = null, + vararg params: Pair<String, ParamAttributes> +) = group { // TODO: remove it when double wrapping for signatures will be resolved + annotations.entries.forEach { + group { + unwrapAnnotation(it) + } + } + +("$visibility $modifier ${keywords.joinToString("") { "$it " }} fun") + group { + link { +receiver } + } + +"." + link { +name } + +"(" + params.forEachIndexed { id, (n, t) -> + + t.annotations.forEach { + unwrapAnnotation(it) + } + t.keywords.forEach { + +it + } + + +"$n:" + group { link { +(t.type) } } + if (id != params.lastIndex) + +", " + } + +")" + if (returnType != null) { + +(": ") + group { + link { + +(returnType) + } + } + } +} + +fun ContentMatcherBuilder<*>.propertySignature( + annotations: Map<String, Set<String>>, + visibility: String, + modifier: String, + keywords: Set<String>, + preposition: String, + name: String, + type: String? = null +) { + group { + header { +"Package test" } + skipAllNotMatching() + } + group { + group { + skipAllNotMatching() + header { +"Properties" } + table { + group { + link { +name } + platformHinted { + group { + annotations.entries.forEach { + group { + unwrapAnnotation(it) + } + } + +("$visibility $modifier ${keywords.joinToString("") { "$it " }} $preposition") + link { +name } + if (type != null) { + +(": ") + group { + link { + +(type) + } + } + } + } + } + } + } + } + } +} + + +fun ContentMatcherBuilder<*>.typealiasSignature(name: String, expressionTarget: String) { + group { + header { +"Package test" } + skipAllNotMatching() + } + group { + group { + skipAllNotMatching() + header { +"Types" } + table { + group { + link { +name } + divergentGroup { + divergentInstance { + group { + group { + group { + group { + +"typealias " + group { + link { +name } + skipAllNotMatching() + } + +" = " + group { + link { +expressionTarget } + } + } + } + } + } + } + skipAllNotMatching() + } + } + skipAllNotMatching() + } + skipAllNotMatching() + } + } +} + +fun ContentMatcherBuilder<*>.pWrapped(text: String) = + group {// TODO: remove it when double wrapping for descriptions will be resolved + group { +text } + } + +fun ContentMatcherBuilder<*>.unnamedTag(tag: String, content: ContentMatcherBuilder<ContentGroup>.() -> Unit) = + group { + header(4) { +tag } + group { content() } + } + +fun ContentMatcherBuilder<*>.unwrapAnnotation(elem: Map.Entry<String, Set<String>>) { + group { + +"@" + link { +elem.key } + +"(" + elem.value.forEach { + group { + +("$it = ") + skipAllNotMatching() + } + } + +")" + } +} + +data class ParamAttributes( + val annotations: Map<String, Set<String>>, + val keywords: Set<String>, + val type: String +) diff --git a/plugins/base/src/test/resources/linkable/includes/include1.md b/plugins/base/src/test/resources/linkable/includes/include1.md new file mode 100644 index 00000000..03d9037d --- /dev/null +++ b/plugins/base/src/test/resources/linkable/includes/include1.md @@ -0,0 +1,7 @@ +# Module example + +This is JVM documentation for module example + +# Package example + +This is JVM documentation for package example
\ No newline at end of file diff --git a/plugins/base/src/test/resources/linkable/includes/include2.md b/plugins/base/src/test/resources/linkable/includes/include2.md new file mode 100644 index 00000000..1574003d --- /dev/null +++ b/plugins/base/src/test/resources/linkable/includes/include2.md @@ -0,0 +1,7 @@ +# Module example + +This is JS documentation for module example + +# Package greeteer + +This is JS documentation for package greeteer
\ No newline at end of file diff --git a/plugins/base/src/test/resources/linkable/samples/jsMain/kotlin/JsClass.kt b/plugins/base/src/test/resources/linkable/samples/jsMain/kotlin/JsClass.kt new file mode 100644 index 00000000..b61ce704 --- /dev/null +++ b/plugins/base/src/test/resources/linkable/samples/jsMain/kotlin/JsClass.kt @@ -0,0 +1,9 @@ +package p2 + +class JsClass { + + /** + * @sample samples.SamplesJs.exampleUsage + */ + fun printWithExclamation(msg: String) = println(msg + "!") +}
\ No newline at end of file diff --git a/plugins/base/src/test/resources/linkable/samples/jsMain/resources/Samples.kt b/plugins/base/src/test/resources/linkable/samples/jsMain/resources/Samples.kt new file mode 100644 index 00000000..55be0ad8 --- /dev/null +++ b/plugins/base/src/test/resources/linkable/samples/jsMain/resources/Samples.kt @@ -0,0 +1,10 @@ +package samples + +import p2.JsClass + +class SamplesJs { + + fun exampleUsage() { + JsClass().printWithExclamation("Hi, Js") + } +}
\ No newline at end of file diff --git a/plugins/base/src/test/resources/linkable/samples/jvmMain/kotlin/JvmClass.kt b/plugins/base/src/test/resources/linkable/samples/jvmMain/kotlin/JvmClass.kt new file mode 100644 index 00000000..960184e6 --- /dev/null +++ b/plugins/base/src/test/resources/linkable/samples/jvmMain/kotlin/JvmClass.kt @@ -0,0 +1,9 @@ +package p2 + +class JvmClass { + + /** + * @sample samples.SamplesJvm.exampleUsage + */ + fun printWithExclamation(msg: String) = println(msg + "!") +}
\ No newline at end of file diff --git a/plugins/base/src/test/resources/linkable/samples/jvmMain/resources/Samples.kt b/plugins/base/src/test/resources/linkable/samples/jvmMain/resources/Samples.kt new file mode 100644 index 00000000..69418fa9 --- /dev/null +++ b/plugins/base/src/test/resources/linkable/samples/jvmMain/resources/Samples.kt @@ -0,0 +1,10 @@ +package samples + +import p2.JvmClass + +class SamplesJvm { + + fun exampleUsage() { + JvmClass().printWithExclamation("Hi, Jvm") + } +}
\ No newline at end of file diff --git a/plugins/base/src/test/resources/linkable/sources/jsMain/kotlin/JsClass.kt b/plugins/base/src/test/resources/linkable/sources/jsMain/kotlin/JsClass.kt new file mode 100644 index 00000000..00dd009b --- /dev/null +++ b/plugins/base/src/test/resources/linkable/sources/jsMain/kotlin/JsClass.kt @@ -0,0 +1,3 @@ +package p1 + +class JsClass
\ No newline at end of file diff --git a/plugins/base/src/test/resources/linkable/sources/jvmMain/kotlin/JvmClass.kt b/plugins/base/src/test/resources/linkable/sources/jvmMain/kotlin/JvmClass.kt new file mode 100644 index 00000000..2113c589 --- /dev/null +++ b/plugins/base/src/test/resources/linkable/sources/jvmMain/kotlin/JvmClass.kt @@ -0,0 +1,3 @@ +package p1 + +class JvmClass
\ No newline at end of file diff --git a/plugins/base/src/test/resources/multiplatform/basicMultiplatformTest/commonMain/kotlin/Clock.kt b/plugins/base/src/test/resources/multiplatform/basicMultiplatformTest/commonMain/kotlin/Clock.kt new file mode 100644 index 00000000..4753cb32 --- /dev/null +++ b/plugins/base/src/test/resources/multiplatform/basicMultiplatformTest/commonMain/kotlin/Clock.kt @@ -0,0 +1,15 @@ +package example + +/** + * Documentation for expected class Clock + * in common module + */ +expect open class Clock() { + fun getTime(): String + /** + * Time in minis + */ + fun getTimesInMillis(): String + fun getYear(): String +} + diff --git a/plugins/base/src/test/resources/multiplatform/basicMultiplatformTest/commonMain/kotlin/House.kt b/plugins/base/src/test/resources/multiplatform/basicMultiplatformTest/commonMain/kotlin/House.kt new file mode 100644 index 00000000..c879dee7 --- /dev/null +++ b/plugins/base/src/test/resources/multiplatform/basicMultiplatformTest/commonMain/kotlin/House.kt @@ -0,0 +1,24 @@ +package example + +class House(val street: String, val number: Int) { + + /** + * The owner of the house + */ + var owner: String = "" + + /** + * The owner of the house + */ + val differentOwner: String = "" + + fun addFloor() {} + + class Basement { + val pickles : List<Any> = mutableListOf() + } + + companion object { + val DEFAULT = House("",0) + } +}
\ No newline at end of file diff --git a/plugins/base/src/test/resources/multiplatform/basicMultiplatformTest/jsMain/kotlin/Clock.kt b/plugins/base/src/test/resources/multiplatform/basicMultiplatformTest/jsMain/kotlin/Clock.kt new file mode 100644 index 00000000..51b8fdc6 --- /dev/null +++ b/plugins/base/src/test/resources/multiplatform/basicMultiplatformTest/jsMain/kotlin/Clock.kt @@ -0,0 +1,28 @@ +package example + +import greeteer.Greeter +import kotlin.js.Date + +/** + * Documentation for actual class Clock in JS + */ +actual open class Clock { + actual fun getTime() = Date.now().toString() + fun onlyJsFunction(): Int = 42 + + /** + * JS implementation of getTimeInMillis + */ + actual fun getTimesInMillis(): String = Date.now().toString() + + /** + * JS custom kdoc + */ + actual fun getYear(): String { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } +} + +fun main() { + Greeter().greet().also { println(it) } +}
\ No newline at end of file diff --git a/plugins/base/src/test/resources/multiplatform/basicMultiplatformTest/jvmAndJsSecondCommonMain/kotlin/Greeter.kt b/plugins/base/src/test/resources/multiplatform/basicMultiplatformTest/jvmAndJsSecondCommonMain/kotlin/Greeter.kt new file mode 100644 index 00000000..8a52e2f3 --- /dev/null +++ b/plugins/base/src/test/resources/multiplatform/basicMultiplatformTest/jvmAndJsSecondCommonMain/kotlin/Greeter.kt @@ -0,0 +1,10 @@ +package greeteer + +import example.Clock + +class Greeter { + /** + * Some docs for the [greet] function + */ + fun greet() = Clock().let{ "Hello there! THe time is ${it.getTime()}" } +}
\ No newline at end of file diff --git a/plugins/base/src/test/resources/multiplatform/basicMultiplatformTest/jvmMain/kotlin/example/Clock.kt b/plugins/base/src/test/resources/multiplatform/basicMultiplatformTest/jvmMain/kotlin/example/Clock.kt new file mode 100644 index 00000000..fec06207 --- /dev/null +++ b/plugins/base/src/test/resources/multiplatform/basicMultiplatformTest/jvmMain/kotlin/example/Clock.kt @@ -0,0 +1,41 @@ +package example + +import greeteer.Greeter + +/** + * Documentation for actual class Clock in JVM + */ +actual open class Clock { + actual fun getTime(): String = System.currentTimeMillis().toString() + actual fun getTimesInMillis(): String = System.currentTimeMillis().toString() + + /** + * Documentation for onlyJVMFunction on... + * wait for it... + * ...JVM! + */ + fun onlyJVMFunction(): Double = 2.5 + /** + * Custom equals function + */ + override fun equals(other: Any?): Boolean { + return super.equals(other) + } + + open fun getDayOfTheWeek(): String { + TODO("not implemented") + } + + /** + * JVM custom kdoc + */ + actual fun getYear(): String { + TODO("not implemented") + } +} + +fun clockList() = listOf(Clock()) + +fun main() { + Greeter().greet().also { println(it) } +}
\ No newline at end of file diff --git a/plugins/base/src/test/resources/multiplatform/basicMultiplatformTest/jvmMain/kotlin/example/ClockDays.kt b/plugins/base/src/test/resources/multiplatform/basicMultiplatformTest/jvmMain/kotlin/example/ClockDays.kt new file mode 100644 index 00000000..136ae5c8 --- /dev/null +++ b/plugins/base/src/test/resources/multiplatform/basicMultiplatformTest/jvmMain/kotlin/example/ClockDays.kt @@ -0,0 +1,15 @@ +package example + +/** + * frgergergrthe + * */ +enum class ClockDays { + /** + * dfsdfsdfds + * */ + FIRST, + SECOND, // test2 + THIRD, // test3 + FOURTH, // test4 + FIFTH // test5 +}
\ No newline at end of file diff --git a/plugins/base/src/test/resources/multiplatform/basicMultiplatformTest/jvmMain/kotlin/example/ParticularClock.kt b/plugins/base/src/test/resources/multiplatform/basicMultiplatformTest/jvmMain/kotlin/example/ParticularClock.kt new file mode 100644 index 00000000..40813b50 --- /dev/null +++ b/plugins/base/src/test/resources/multiplatform/basicMultiplatformTest/jvmMain/kotlin/example/ParticularClock.kt @@ -0,0 +1,32 @@ +package example + +import greeteer.Greeter + +class ParticularClock(private val clockDay: ClockDays) : Clock() { + + /** + * Rings bell [times] + */ + fun ringBell(times: Int) {} + + /** + * Uses provider [greeter] + */ + fun useGreeter(greeter: Greeter) { + + } + + /** + * Day of the week + */ + override fun getDayOfTheWeek() = clockDay.name +} + +/** + * A sample extension function + * When $a \ne 0$, there are two solutions to \(ax^2 + bx + c = 0\) and they are $$x = {-b \pm \sqrt{b^2-4ac} \over 2a}.$$ + * @usesMathJax + */ +fun Clock.extensionFun() { + +}
\ No newline at end of file diff --git a/plugins/base/test-utils/build.gradle.kts b/plugins/base/test-utils/build.gradle.kts new file mode 100644 index 00000000..4c39ed60 --- /dev/null +++ b/plugins/base/test-utils/build.gradle.kts @@ -0,0 +1,4 @@ +dependencies { + compileOnly(project(":plugins:base")) + implementation(project(":testApi")) +}
\ No newline at end of file diff --git a/plugins/base/test-utils/src/main/kotlin/renderers/RenderingOnlyTestBase.kt b/plugins/base/test-utils/src/main/kotlin/renderers/RenderingOnlyTestBase.kt new file mode 100644 index 00000000..e5ff8fa8 --- /dev/null +++ b/plugins/base/test-utils/src/main/kotlin/renderers/RenderingOnlyTestBase.kt @@ -0,0 +1,8 @@ +package renderers + +import org.jetbrains.dokka.testApi.context.MockContext + +abstract class RenderingOnlyTestBase<T> { + abstract val context: MockContext + abstract val renderedContent: T +} diff --git a/plugins/base/test-utils/src/main/kotlin/renderers/TestPage.kt b/plugins/base/test-utils/src/main/kotlin/renderers/TestPage.kt new file mode 100644 index 00000000..0dae8ce6 --- /dev/null +++ b/plugins/base/test-utils/src/main/kotlin/renderers/TestPage.kt @@ -0,0 +1,52 @@ +package renderers + +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.Documentable +import org.jetbrains.dokka.model.doc.DocTag +import org.jetbrains.dokka.model.properties.PropertyContainer +import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.utilities.DokkaConsoleLogger +import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder +import org.jetbrains.dokka.base.signatures.KotlinSignatureProvider +import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter + +class TestPage(callback: PageContentBuilder.DocumentableContentBuilder.() -> Unit) : RootPageNode(), ContentPage { + override val dri: Set<DRI> = setOf(DRI.topLevel) + override val documentable: Documentable? = null + override val embeddedResources: List<String> = emptyList() + override val name: String + get() = "testPage" + override val children: List<PageNode> + get() = emptyList() + + override val content: ContentNode = PageContentBuilder( + EmptyCommentConverter, + KotlinSignatureProvider(EmptyCommentConverter, DokkaConsoleLogger), + DokkaConsoleLogger + ).contentFor( + DRI.topLevel, + emptySet(), + block = callback + ) + + override fun modified( + name: String, + content: ContentNode, + dri: Set<DRI>, + embeddedResources: List<String>, + children: List<PageNode> + ) = this + + override fun modified(name: String, children: List<PageNode>) = this +} + +internal object EmptyCommentConverter : CommentsToContentConverter { + override fun buildContent( + docTag: DocTag, + dci: DCI, + sourceSets: Set<DokkaConfiguration.DokkaSourceSet>, + styles: Set<Style>, + extras: PropertyContainer<ContentNode> + ): List<ContentNode> = emptyList() +}
\ No newline at end of file diff --git a/plugins/base/test-utils/src/main/kotlin/renderers/defaultSourceSet.kt b/plugins/base/test-utils/src/main/kotlin/renderers/defaultSourceSet.kt new file mode 100644 index 00000000..7358d2c2 --- /dev/null +++ b/plugins/base/test-utils/src/main/kotlin/renderers/defaultSourceSet.kt @@ -0,0 +1,31 @@ +package renderers + +import org.jetbrains.dokka.DokkaSourceSetID +import org.jetbrains.dokka.DokkaSourceSetImpl +import org.jetbrains.dokka.Platform + +val defaultSourceSet = DokkaSourceSetImpl( + moduleDisplayName = "DEFAULT", + displayName = "DEFAULT", + sourceSetID = DokkaSourceSetID("DEFAULT", "DEFAULT"), + classpath = emptyList(), + sourceRoots = emptyList(), + dependentSourceSets = emptySet(), + samples = emptyList(), + includes = emptyList(), + includeNonPublic = false, + includeRootPackage = false, + reportUndocumented = false, + skipEmptyPackages = true, + skipDeprecated = false, + jdkVersion = 8, + sourceLinks = emptyList(), + perPackageOptions = emptyList(), + externalDocumentationLinks = emptyList(), + languageVersion = null, + apiVersion = null, + noStdlibLink = false, + noJdkLink = false, + suppressedFiles = emptyList(), + analysisPlatform = Platform.DEFAULT +) diff --git a/plugins/base/test-utils/src/main/kotlin/utils/TestOutputWriter.kt b/plugins/base/test-utils/src/main/kotlin/utils/TestOutputWriter.kt new file mode 100644 index 00000000..00b865b4 --- /dev/null +++ b/plugins/base/test-utils/src/main/kotlin/utils/TestOutputWriter.kt @@ -0,0 +1,32 @@ +package utils + +import org.jetbrains.dokka.base.DokkaBase +import org.jetbrains.dokka.base.renderers.OutputWriter +import org.jetbrains.dokka.plugability.DokkaPlugin + +class TestOutputWriterPlugin(failOnOverwrite: Boolean = true) : DokkaPlugin() { + val writer = TestOutputWriter(failOnOverwrite) + + private val dokkaBase by lazy { plugin<DokkaBase>() } + + val testWriter by extending { + (dokkaBase.outputWriter + with writer + override dokkaBase.fileWriter) + } +} + +class TestOutputWriter(private val failOnOverwrite: Boolean = true) : OutputWriter { + val contents: Map<String, String> get() = _contents + + private val _contents = mutableMapOf<String, String>() + override suspend fun write(path: String, text: String, ext: String) { + val fullPath = "$path$ext" + _contents.putIfAbsent(fullPath, text)?.also { + if (failOnOverwrite) throw AssertionError("File $fullPath is being overwritten.") + } + } + + override suspend fun writeResources(pathFrom: String, pathTo: String) = + write(pathTo, "*** content of $pathFrom ***", "") +} diff --git a/plugins/build.gradle.kts b/plugins/build.gradle.kts new file mode 100644 index 00000000..bb3b1a3d --- /dev/null +++ b/plugins/build.gradle.kts @@ -0,0 +1,23 @@ +subprojects { + apply { + plugin("maven-publish") + plugin("com.jfrog.bintray") + } + + dependencies { + compileOnly(project(":core")) + implementation(kotlin("stdlib-jdk8")) + implementation(kotlin("stdlib")) + implementation(kotlin("reflect")) + + testImplementation(project(":testApi")) + testImplementation("org.junit.jupiter:junit-jupiter:5.6.0") + } + + tasks.test { + useJUnitPlatform() + testLogging { + events("passed", "skipped", "failed") + } + } +}
\ No newline at end of file diff --git a/plugins/developer_guide.md b/plugins/developer_guide.md new file mode 100644 index 00000000..b7b184f8 --- /dev/null +++ b/plugins/developer_guide.md @@ -0,0 +1,413 @@ +# Guide to Dokka Plugin development + +## Configuration + +Dokka requires configured kotlin plugin and dokka-core dependency. + +```kotlin +plugins { + kotlin("jvm") version <kotlin_version> +} + +dependencies { + compileOnly("org.jetbrains.dokka:dokka-core:<dokka_version>>) +} + +tasks.withType<KotlinCompile> { + kotlinOptions.jvmTarget = "1.8" +} +``` + +## Building sample plugin + +In order to load a plugin into dokka, your class must extend `DokkaPlugin` class. All instances are automatically loaded during dokka setup using `java.util.ServiceLoader`. + +Dokka provides a set of entry points, for which user can create their own implementations. They must be delegated using `DokkaPlugin.extending(definition: ExtendingDSL.() -> Extension<T, *, *>)` function,that returns a delegate `ExtensionProvider` with supplied definition. + +To create a definition, you can use one of two infix functions`with(T)` or `providing( (DokkaContext) -> T)` where `T` is the type of an extended endpoint. You can also use infix functions: +* `applyIf( () -> Boolean )` to add additional condition specifying whether or not the extension should be used +* `order((OrderDsl.() -> Unit))` to determine if your extension should be used before or after another particular extension for the same endpoint +* `override( Extension<T, *, *> )` to override other extension. Overridden extension won't be loaded and overridding one will inherit ordering from it. + +Following sample provides custom translator object as a `DokkaCore.sourceToDocumentableTranslator` + +```kotlin +package org.jetbrains.dokka.sample + +import org.jetbrains.dokka.plugability.DokkaPlugin + +class SamplePlugin : DokkaPlugin() { + extension by extending { + DokkaCore.sourceToDocumentableTranslator with CustomSourceToDocumentableTranslator + } +} + +object CustomSourceToDocumentableTranslator: SourceToDocumentableTranslator { + override fun invoke(sourceSet: SourceSetData, context: DokkaContext): DModule +} +``` + +### Registering extension point + +You can register your own extension point using `extensionPoint` function declared in `DokkaPlugin` class + +```kotlin +class SamplePlugin : DokkaPlugin() { + val extensionPoint by extensionPoint<SampleExtensionPointInterface>() +} + +interface SampleExtensionPointInterface +``` + +### Obtaining extension instance + +All registered plugins are accessible with `DokkaContext.plugin` function. All plugins that extends `DokkaPlugin` can use `DokkaPlugin.plugin` function, that uses underlying `DokkaContext` instance. If you want to pass context to your extension, you can obtain it using aforementioned `providing` infix function. + +With plugin instance obtained, you can browse extensions registered for this plugins' extension points using `querySingle` and `query` methods: + +```kotlin + context.plugin<DokkaBase>().query { htmlPreprocessors } + context.plugin<DokkaBase>().querySingle { samplesTransformer } +``` + +You can also browse `DokkaContext` directly, using `single` and `get` methods: + +```kotlin +class SamplePlugin : DokkaPlugin() { + + val extensionPoint by extensionPoint<SampleExtensionPointInterface>() + val anotherExtensionPoint by extensionPoint<AnotherSampleExtensionPointInterface>() + + val extension by extending { + extensionPoint with SampleExtension() + } + + val anotherExtension by extending { + anotherExtensionPoint providing { context -> + AnotherSampleExtension(context.single(extensionPoint)) + } + } +} + +interface SampleExtensionPointInterface +interface AnotherSampleExtensionPointInterface + +class SampleExtension: SampleExtensionPointInterface +class AnotherSampleExtension(sampleExtension: SampleExtensionPointInterface): AnotherSampleExtensionPointInterface +``` + +## Dokka Data Model + +There a four data models that dokka uses: Documentable Model, Documentation Model, Page Model and Content Model. + +### Documentable Model + +Documentable model represents parsed data, returned by compiler analysis. It retains basic order structure of parsed `Psi` or `Descriptor` models. + +After creation, it is a collection of trees, each with `DModel` as a root. After the Merge step, all trees are folded into one. + +The main building block of this model is `Documentable` class, that is a base class for all more specific types that represents elements of parsed Kotlin and Java classes with pretty self-explanatory names: `DPackage`, `DFunction` and so on. `DClasslike` is a base for class-like elements, such as Classes, Enums, Interfaces and so on. + +There are three non-documentable classes important for the model: `DRI`, `SourceSetDependent` and `ExtraProperty`. + +* `DRI` (Dokka Resource Identifier) is an unique value that identifies specific `Documentable`. All references to other documentables different than direct ownership are described using DRIs. For example, `DFunction` with parameter of type `X` has only X's DRI, not the actual reference to X's Documentable object. +* `SourceSetDependent` is a map that handles multiplatform data, by connecting platform-specific data, declared with either `expect` or `actual` modifier, to a particular Source Set +* `ExtraProperty` is used to store any additional information that falls outside of regular model. It is highly recommended to use extras to provide any additional information when creating custom Dokka plugins. This element is a bit more complex, so you can read more about how to use it below. + +#### `ExtraProperty` class usage + +`ExtraProperty` classes are used both by Documentable and Content models. To declare a new extra, you need to implement `ExtraProperty` interface. + +```kotlin +interface ExtraProperty<in C : Any> { + interface Key<in C : Any, T : Any> { + fun mergeStrategyFor(left: T, right: T): MergeStrategy<C> = MergeStrategy.Fail { + throw NotImplementedError("Property merging for $this is not implemented") + } + } + + val key: Key<C, *> +} +``` + +It is advised to use following pattern when declaring new extras: + +```kotlin +data class CustomExtra( [any values relevant to your extra ] ): ExtraProperty<Documentable> { + companion object : CustomExtra.Key<Documentable, CustomExtra> + override val key: CustomExtra.Key<Documentable, *> = CustomExtra +} +``` +Merge strategy for extras is invoked only if merged objects have different values for same Extra. If you don't expect it to happen, you can omit implementing `mergeStrategyFor` function. + +All extras for `ContentNode` and `Documentable` classes are stored in `PropertyContainer<C : Any>` class instances. The `C` generic class parameter limits the type of properties, that can be stored in the container - it must match generic `C` class parameter from `ExtraProperty` interface. For example, if you would create `DFunction`-only `ExtraProperty`, it will be limited to be added only to `PropertyContainer<DFunction>`. + +In following example we will create `Documentable`-only property, store it in the container and then retrieve its value: + +```kotlin +data class CustomExtra(val customExtraValue: String) : ExtraProperty<Documentable> { + + companion object: ExtraProperty.Key<Documentable, CustomExtra> + + override val key: ExtraProperty.Key<Documentable, *> = CustomExtra +} + +val extra : PropertyContainer<DFunction> = PropertyContainer.withAll( + CustomExtra("our value") +) + +val customExtraValue : String? = extra[CustomProperty]?.customExtraValue +``` + +You can also use extras as markers, without storing any data in them: + +```kotlin + +object MarkerExtra : ExtraProperty<Any>, ExtraProperty.Key<Any, MarkerExtra> { + override val key: ExtraProperty.Key<Any, *> = this +} + +val extra : PropertyContainer<Any> = PropertyContainer.withAll(MarkerExtra) + +val isMarked : Boolean = extra[MarkerExtra] != null + +``` + +### Documentation Model + +Documentation model is used along Documentable Model to store data obtained by parsing code commentaries. + +There are three important classes here: + +* `DocTag` describes a specific documentation syntax element, for example: header, footer, list, link, raw text, paragraph, etc. +* `TagWrapper` described a whole comment description or a specific comment tag, for example: @See, @Returns, @Author; and holds consisting `DocTag` elements +* `DocumentationNode` acts as a container for `TagWrappers` for a specific `Documentable` + +DocumentationNodes are references by a specific `Documentable` + +### Page Model + +Page Model represents the structure of future generated documentation pages and is independent of the final output format, which each node corresponding to exactly one output file. `Renderer` is processing each page separately.Subclasses of `PageNode` represents different kinds of rendered pages for Modules, Packages, Classes etc. + +The Page Model is a tree structure, with `RootPageNode` as a root. + +### Content Model + +Content Model describes how the actual page content is presented. It organizes it's structure into groups, tables, links, etc. Each node is identified by unique `DCI` (Dokka Content Identifier) and all references to other nodes different than direct ownership are described using DCIs. + +`DCI` aggregates `DRI`s of all `Documentables` that make up specific `ContentNode`. + +Also, all `ExtraProperty` info from consisting `Documentable`s is propagated into Content Model and available for `Renderer`. + +## Extension points + +### Core extension points + +We will discuss all base extension points along with the steps, that `DokkaGenerator` does to build a documentation. + +#### Setting up Kotlin and Java analysis process and initializing plugins + +The provided Maven / CLI / Gradle configuration is read.Then, all the `DokkaPlugin` classes are loaded and the extensions are created. + + No entry points here. + +#### Creating documentation models + +The documentation models are created. + +This step uses `DokkaCore.sourceToDocumentableTranslator` entry point. All extensions registered using this entry point will be invoked. Each of them is required to implement `SourceToDocumentableTranslator` interface: + +```kotlin +interface SourceToDocumentableTranslator { + fun invoke(sourceSet: SourceSetData, context: DokkaContext): DModule +} +``` +By default, two translators are created: +* `DefaultDescriptorToDocumentableTranslator` that handles Kotlin files +* `DefaultPsiToDocumentableTranslator` that handles Java files + +After this step, all data from different source sets and languages are kept separately. + +#### Pre-merge documentation transform + +Here you can apply any transformation to model data before different source sets are merged. + +This step uses `DokkaCore.preMergeDocumentableTransformer` entry point. All extensions registered using this entry point will be invoked. Each of them is required to implement `PreMergeDocumentableTransformer` interface: + +```kotlin +interface PreMergeDocumentableTransformer { + operator fun invoke(modules: List<DModule>, context: DokkaContext): List<DModule> +} +``` +By default, three transformers are created: +* `DocumentableVisibilityFilter` that, depending on configuration, filters out all private members from declared packages +* `ActualTypealiasAdder` that handles Kotlin typealiases +* `ModuleAndPackageDocumentationTransformer` that creates documentation content for models and packages itself + +#### Merging + +All `DModule` instances are merged into one. + +This step uses `DokkaCore.documentableMerger` entry point. It is required to have exactly one extension registered for this entry point. Having more will trigger an error, unless only one is not overridden. + +The extension is required to implement `DocumentableMerger` interface: + +```kotlin +interface DocumentableMerger { + operator fun invoke(modules: Collection<DModule>, context: DokkaContext): DModule +} +``` + +By default, `DefaultDocumentableMerger` is created. This extension is treated as a fallback, so it can be overridden by a custom one. + +#### Merged data transformation + +You can apply any transformation to already merged data + +This step uses `DokkaCore.documentableTransformer` entry point. All extensions registered using this entry point will be invoked. Each of them is required to implement `DocumentableTransformer` interface: + +```kotlin +interface DocumentableTransformer { + operator fun invoke(original: DModule, context: DokkaContext): DModule +} +``` + +By default, `InheritorsExtractorTransformer` is created, that extracts inherited classes data across source sets and creates inheritance map. + +#### Creating page models + +The documentable model is translated into page format, that aggregates all tha data that will be available for different pages of documentation. + +This step uses `DokkaCore.documentableToPageTranslator` entry point. It is required to have exactly one extension registered for this entry point. Having more will trigger an error, unless only one is not overridden. + +The extension is required to implement `DocumentableToPageTranslator` interface: + +```kotlin +interface DocumentableToPageTranslator { + operator fun invoke(module: DModule): ModulePageNode +} +``` + +By default, `DefaultDocumentableToPageTranslator` is created. This extension is treated as a fallback, so it can be overridden by a custom one. + +#### Transforming page models + +You can apply any transformations to paged data. + +This step uses `DokkaCore.pageTransformer` entry point. All extensions registered using this entry point will be invoked. Each of them is required to implement `PageTransformer` interface: + +```kotlin +interface PageTransformer { + operator fun invoke(input: RootPageNode): RootPageNode +} +``` +By default, two transformers are created: +* `PageMerger` merges some pages depending on `MergeStrategy` +* `DeprecatedStrikethroughTransformer` marks all deprecated members on every page + +#### Rendering + +All pages are rendered to desired format. + +This step uses `DokkaCore.renderer` entry point. It is required to have exactly one extension registered for this entry point. Having more will trigger an error, unless only one is not overridden. + +The extension is required to implement `Renderer` interface: + +```kotlin +interface Renderer { + fun render(root: RootPageNode) +} +``` + +By default, only `HtmlRenderer`, that extends basic `DefaultRenderer`, is created, but it will be registered only if configuration parameter `format` is set to `html`. Using any other value without providing valid renderer will cause dokka to fail. + +### Multimodule page generation endpoints + +Multimodule page generation is a separate process, that declares two additional entry points: + +#### Multimodule page creation + +Generation of the page that points to all module for which we generates documentation. + +This step uses `CoreExtensions.allModulePageCreator` entry point. It is required to have exactly one extension registered for this entry point. Having more will trigger an error, unless only one is not overridden. + +The extension is required to implement `PageCreator` interface: + +```kotlin +interface PageCreator { + operator fun invoke(): RootPageNode +} +``` + +By default, `MultimodulePageCreator` is created. This extension is treated as a fallback, so it can be replaced by a custom one. + +#### Multimodule page transformation + +Additional transformation that we might apply for multimodule page. + +This step uses `CoreExtensions.allModulePageTransformer` entry point. All extensions registered using this entry point will be invoked. Each of them is required to implement common `PageTransformer` interface. + +### Default extensions' extension points + +Default core extension points already have an implementation for providing basic dokka functionality. All of them are declared in `DokkaBase` plugin. If you don't want this default extensions to load, all you need to do is not load dokka base and load your plugin instead. + + ```kotlin +val customPlugin by configurations.creating + +dependencies { + customPlugin("[custom plugin load signature]") +} +tasks { + val dokka by getting(DokkaTask::class) { + pluginsConfig = alternativeAndIndependentPlugins + outputDirectory = dokkaOutputDir + outputFormat = "html" + [...] + } +} +``` + + You will then need to implement extensions for all core extension points. + +`DokkaBase` also register several new extension points, with which you can change default behaviour of `DokkaBase` extensions. In order to use them, you need to add `dokka-base` to you dependencies: + +```kotlin + compileOnly("org.jetbrains.dokka:dokka-base:<dokka_version>") +``` + +Then, you need to obtain `DokkaBase` instance using `plugin` function: + +```kotlin +class SamplePlugin : DokkaPlugin() { + + val dokkaBase = plugin<DokkaBase>() + + val extension by extending { + dokkaBase.pageMergerStrategy with SamplePageMergerStrategy order { + before(dokkaBase.fallbackMerger) + } + } +} + +object SamplePageMergerStrategy: PageMergerStrategy { + override fun tryMerge(pages: List<PageNode>, path: List<String>): List<PageNode> { + ... + } + +} +``` + +#### Following extension points are available with base plugin + +| Entry point | Function | Required interface | Used by | Singular | Preregistered extensions +|---|:---|:---:|:---:|:---:|:---:| +| `pageMergerStrategy` | determines what kind of pages should be merged | `PageMergerStrategy` | `PageMerger` | false | `FallbackPageMergerStrategy` `SameMethodNamePageMergerStrategy` | +| `commentsToContentConverter` | transforms comment model into page content model | `CommentsToContentConverter` | `DefaultDocumentableToPageTransformer` `SignatureProvider` | true | `DocTagToContentConverter` | +| `signatureProvider` | provides representation of methods signatures | `SignatureProvider` | `DefaultDocumentableToPageTransformer` | true | `KotlinSignatureProvider` | +| `locationProviderFactory` | provides `LocationProvider` instance that returns paths for requested elements | `LocationProviderFactory` | `DefaultRenderer` `HtmlRenderer` `PackageListService` | true | `DefaultLocationProviderFactory` which returns `DefaultLocationProvider` | +| `externalLocationProviderFactory` | provides `ExternalLocationProvider` instance that returns paths for elements that are not part of generated documentation | `ExternalLocationProviderFactory` | `DefaultLocationProvider` | false | `JavadocExternalLocationProviderFactory` `DokkaExternalLocationProviderFactory` | +| `outputWriter` | writes rendered pages files | `OutputWriter` | `DefaultRenderer` `HtmlRenderer` | true | `FileWriter`| +| `htmlPreprocessors` | transforms page content before HTML rendering | `PageTransformer`| `DefaultRenderer` `HtmlRenderer` | false | `RootCreator` `SourceLinksTransformer` `NavigationPageInstaller` `SearchPageInstaller` `ResourceInstaller` `StyleAndScriptsAppender` `PackageListCreator` | +| `samplesTransformer` | transforms content for code samples for HTML rendering | `SamplesTransformer` | `HtmlRenderer` | true | `DefaultSamplesTransformer` | + + diff --git a/plugins/gfm/build.gradle.kts b/plugins/gfm/build.gradle.kts new file mode 100644 index 00000000..3f69043d --- /dev/null +++ b/plugins/gfm/build.gradle.kts @@ -0,0 +1,11 @@ +import org.jetbrains.registerDokkaArtifactPublication + +dependencies { + implementation(project(":plugins:base")) + testImplementation(project(":plugins:base")) + testImplementation(project(":plugins:base:test-utils")) +} + +registerDokkaArtifactPublication("gfmPlugin") { + artifactId = "gfm-plugin" +} diff --git a/plugins/gfm/src/main/kotlin/GfmPlugin.kt b/plugins/gfm/src/main/kotlin/GfmPlugin.kt new file mode 100644 index 00000000..dcc9c0a6 --- /dev/null +++ b/plugins/gfm/src/main/kotlin/GfmPlugin.kt @@ -0,0 +1,345 @@ +package org.jetbrains.dokka.gfm + +import org.jetbrains.dokka.CoreExtensions +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.base.DokkaBase +import org.jetbrains.dokka.base.renderers.DefaultRenderer +import org.jetbrains.dokka.base.renderers.PackageListCreator +import org.jetbrains.dokka.base.renderers.RootCreator +import org.jetbrains.dokka.base.resolvers.local.DefaultLocationProvider +import org.jetbrains.dokka.base.resolvers.local.LocationProviderFactory +import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.DokkaPlugin +import org.jetbrains.dokka.plugability.plugin +import org.jetbrains.dokka.plugability.query +import org.jetbrains.dokka.transformers.pages.PageTransformer + +class GfmPlugin : DokkaPlugin() { + + val gfmPreprocessors by extensionPoint<PageTransformer>() + + private val dokkaBase by lazy { plugin<DokkaBase>() } + + val renderer by extending { + (CoreExtensions.renderer + providing { CommonmarkRenderer(it) } + override dokkaBase.htmlRenderer) + } + + val locationProvider by extending { + (dokkaBase.locationProviderFactory + providing { MarkdownLocationProviderFactory(it) } + override dokkaBase.locationProvider) + } + + val rootCreator by extending { + gfmPreprocessors with RootCreator + } + + val packageListCreator by extending { + (gfmPreprocessors + providing { PackageListCreator(it, "gfm", "md") } + order { after(rootCreator) }) + } +} + +open class CommonmarkRenderer( + context: DokkaContext +) : DefaultRenderer<StringBuilder>(context) { + + override val preprocessors = context.plugin<GfmPlugin>().query { gfmPreprocessors } + + override fun StringBuilder.wrapGroup( + node: ContentGroup, + pageContext: ContentPage, + childrenCallback: StringBuilder.() -> Unit + ) { + return when { + node.hasStyle(TextStyle.Block) -> { + childrenCallback() + buildNewLine() + } + node.hasStyle(TextStyle.Paragraph) -> { + buildParagraph() + childrenCallback() + buildParagraph() + } + else -> childrenCallback() + } + } + + override fun StringBuilder.buildHeader(level: Int, node: ContentHeader, content: StringBuilder.() -> Unit) { + buildParagraph() + append("#".repeat(level) + " ") + content() + buildNewLine() + } + + override fun StringBuilder.buildLink(address: String, content: StringBuilder.() -> Unit) { + append("[") + content() + append("]($address)") + } + + override fun StringBuilder.buildList( + node: ContentList, + pageContext: ContentPage, + sourceSetRestriction: Set<DokkaSourceSet>? + ) { + buildListLevel(node, pageContext) + } + + private fun StringBuilder.buildListItem(items: List<ContentNode>, pageContext: ContentPage) { + items.forEach { + if (it is ContentList) { + buildList(it, pageContext) + } else { + append("<li>") + append(buildString { it.build(this, pageContext, it.sourceSets) }.trim()) + append("</li>") + } + } + } + + private fun StringBuilder.buildListLevel(node: ContentList, pageContext: ContentPage) { + if (node.ordered) { + append("<ol>") + buildListItem(node.children, pageContext) + append("</ol>") + } else { + append("<ul>") + buildListItem(node.children, pageContext) + append("</ul>") + } + } + + override fun StringBuilder.buildNewLine() { + append(" \n") + } + + private fun StringBuilder.buildParagraph() { + append("\n\n") + } + + override fun StringBuilder.buildPlatformDependent( + content: PlatformHintedContent, + pageContext: ContentPage, + sourceSetRestriction: Set<DokkaSourceSet>? + ) { + buildPlatformDependentItem(content.inner, content.sourceSets, pageContext) + } + + private fun StringBuilder.buildPlatformDependentItem( + content: ContentNode, + sourceSets: Set<DokkaSourceSet>, + pageContext: ContentPage, + ) { + if (content is ContentGroup && content.children.firstOrNull { it is ContentTable } != null) { + buildContentNode(content, pageContext, sourceSets) + } else { + val distinct = sourceSets.map { + it to buildString { buildContentNode(content, pageContext, setOf(it)) } + }.groupBy(Pair<DokkaSourceSet, String>::second, Pair<DokkaSourceSet, String>::first) + + distinct.filter { it.key.isNotBlank() }.forEach { (text, platforms) -> + append(" ") + buildSourceSetTags(platforms.toSet()) + append(" $text ") + buildNewLine() + } + } + } + + override fun StringBuilder.buildResource(node: ContentEmbeddedResource, pageContext: ContentPage) { + append("Resource") + } + + override fun StringBuilder.buildTable( + node: ContentTable, + pageContext: ContentPage, + sourceSetRestriction: Set<DokkaSourceSet>? + ) { + buildNewLine() + if (node.dci.kind == ContentKind.Sample || node.dci.kind == ContentKind.Parameters) { + node.sourceSets.forEach { sourcesetData -> + append(sourcesetData.displayName) + buildNewLine() + buildTable( + node.copy( + children = node.children.filter { it.sourceSets.contains(sourcesetData) }, + dci = node.dci.copy(kind = ContentKind.Main) + ), pageContext, sourceSetRestriction + ) + buildNewLine() + } + } else { + val size = node.header.size + + if (node.header.isNotEmpty()) { + append("| ") + node.header.forEach { + it.children.forEach { + append(" ") + it.build(this, pageContext, it.sourceSets) + } + append("| ") + } + append("\n") + } else { + append("| ".repeat(size)) + if (size > 0) append("|\n") + } + + append("|---".repeat(size)) + if (size > 0) append("|\n") + + node.children.forEach { + val builder = StringBuilder() + it.children.forEach { + builder.append("| ") + builder.append(buildString { it.build(this, pageContext) }.replace(Regex("#+ "), "") ) // Workaround for headers inside tables + } + append(builder.toString().withEntersAsHtml()) + append(" | ".repeat(size - it.children.size)) + append("\n") + } + } + } + + override fun StringBuilder.buildText(textNode: ContentText) { + if(textNode.text.isNotBlank()) { + val decorators = decorators(textNode.style) + append(textNode.text.takeWhile { it == ' ' } ) + append(decorators) + append(textNode.text.trim()) + append(decorators.reversed()) + append(textNode.text.takeLastWhile { it == ' ' }) + } + } + + override fun StringBuilder.buildNavigation(page: PageNode) { + locationProvider.ancestors(page).asReversed().forEach { node -> + append("/") + if (node.isNavigable) buildLink(node, page) + else append(node.name) + } + buildParagraph() + } + + override fun buildPage(page: ContentPage, content: (StringBuilder, ContentPage) -> Unit): String = + buildString { + content(this, page) + } + + override fun buildError(node: ContentNode) { + context.logger.warn("Markdown renderer has encountered problem. The unmatched node is $node") + } + + override fun StringBuilder.buildDivergent(node: ContentDivergentGroup, pageContext: ContentPage) { + + val distinct = + node.groupDivergentInstances(pageContext, { instance, contentPage, sourceSet -> + instance.before?.let { before -> + buildString { buildContentNode(before, pageContext, setOf(sourceSet)) } + } ?: "" + }, { instance, contentPage, sourceSet -> + instance.after?.let { after -> + buildString { buildContentNode(after, pageContext, setOf(sourceSet)) } + } ?: "" + }) + + distinct.values.forEach { entry -> + val (instance, sourceSets) = entry.getInstanceAndSourceSets() + + buildSourceSetTags(sourceSets) + buildNewLine() + instance.before?.let { + append("Brief description") + buildNewLine() + buildContentNode(it, pageContext, setOf(sourceSets.first())) // It's workaround to render content only once + buildNewLine() + } + + append("Content") + buildNewLine() + entry.groupBy { buildString { buildContentNode(it.first.divergent, pageContext, setOf(it.second)) } } + .values.forEach { innerEntry -> + val (innerInstance, innerSourceSets) = innerEntry.getInstanceAndSourceSets() + if(sourceSets.size > 1) { + buildSourceSetTags(innerSourceSets) + buildNewLine() + } + innerInstance.divergent.build(this@buildDivergent, pageContext, setOf(innerSourceSets.first())) // It's workaround to render content only once + buildNewLine() + } + + instance.after?.let { + append("More info") + buildNewLine() + buildContentNode(it, pageContext, setOf(sourceSets.first())) // It's workaround to render content only once + buildNewLine() + } + + buildParagraph() + } + } + + private fun decorators(styles: Set<Style>) = buildString { + styles.forEach { + when (it) { + TextStyle.Bold -> append("**") + TextStyle.Italic -> append("*") + TextStyle.Strong -> append("**") + TextStyle.Strikethrough -> append("~~") + else -> Unit + } + } + } + + private val PageNode.isNavigable: Boolean + get() = this !is RendererSpecificPage || strategy != RenderingStrategy.DoNothing + + private fun StringBuilder.buildLink(to: PageNode, from: PageNode) = + buildLink(locationProvider.resolve(to, from)) { + append(to.name) + } + + override suspend fun renderPage(page: PageNode) { + val path by lazy { locationProvider.resolve(page, skipExtension = true) } + when (page) { + is ContentPage -> outputWriter.write(path, buildPage(page) { c, p -> buildPageContent(c, p) }, ".md") + is RendererSpecificPage -> when (val strategy = page.strategy) { + is RenderingStrategy.Copy -> outputWriter.writeResources(strategy.from, path) + is RenderingStrategy.Write -> outputWriter.write(path, strategy.text, "") + is RenderingStrategy.Callback -> outputWriter.write(path, strategy.instructions(this, page), ".md") + RenderingStrategy.DoNothing -> Unit + } + else -> throw AssertionError( + "Page ${page.name} cannot be rendered by renderer as it is not renderer specific nor contains content" + ) + } + } + + private fun String.withEntersAsHtml(): String = replace("\n", "<br>") + + private fun List<Pair<ContentDivergentInstance, DokkaSourceSet>>.getInstanceAndSourceSets() = this.let { Pair(it.first().first, it.map { it.second }.toSet()) } + + private fun StringBuilder.buildSourceSetTags(sourceSets: Set<DokkaSourceSet>) = + append(sourceSets.joinToString(prefix = "[", postfix = "]") { it.displayName }) +} + +class MarkdownLocationProviderFactory(val context: DokkaContext) : LocationProviderFactory { + + override fun getLocationProvider(pageNode: RootPageNode) = MarkdownLocationProvider(pageNode, context) +} + +class MarkdownLocationProvider( + pageGraphRoot: RootPageNode, + dokkaContext: DokkaContext +) : DefaultLocationProvider( + pageGraphRoot, + dokkaContext +) { + override val extension = ".md" +} diff --git a/plugins/gfm/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin b/plugins/gfm/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin new file mode 100644 index 00000000..ae291d68 --- /dev/null +++ b/plugins/gfm/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin @@ -0,0 +1 @@ +org.jetbrains.dokka.gfm.GfmPlugin diff --git a/plugins/gfm/src/test/kotlin/renderers/gfm/DivergentTest.kt b/plugins/gfm/src/test/kotlin/renderers/gfm/DivergentTest.kt new file mode 100644 index 00000000..0c8b942e --- /dev/null +++ b/plugins/gfm/src/test/kotlin/renderers/gfm/DivergentTest.kt @@ -0,0 +1,374 @@ +package renderers.gfm + +import org.jetbrains.dokka.DokkaSourceSetID +import org.jetbrains.dokka.Platform +import org.jetbrains.dokka.SourceRootImpl +import org.jetbrains.dokka.gfm.CommonmarkRenderer +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.pages.ContentDivergentGroup +import org.junit.jupiter.api.Test +import renderers.defaultSourceSet +import renderers.TestPage + +class DivergentTest : GfmRenderingOnlyTestBase() { + private val js = defaultSourceSet.copy( + "root", + "js", + DokkaSourceSetID("root", "js"), + analysisPlatform = Platform.js, + sourceRoots = listOf(SourceRootImpl("pl1")) + ) + private val jvm = defaultSourceSet.copy( + "root", + "jvm", + DokkaSourceSetID("root", "jvm"), + analysisPlatform = Platform.jvm, + sourceRoots = listOf(SourceRootImpl("pl1")) + ) + private val native = defaultSourceSet.copy( + "root", + "native", + DokkaSourceSetID("root", "native"), + analysisPlatform = Platform.native, + sourceRoots = listOf(SourceRootImpl("pl1")) + ) + + @Test + fun simpleWrappingCase() { + val page = TestPage { + divergentGroup(ContentDivergentGroup.GroupID("test")) { + instance(setOf(DRI("test", "Test")), setOf(js)) { + divergent { + text("a") + } + } + } + } + val expect = "//[testPage](test-page.md)\n\n[js] \nContent \na \n\n\n" + CommonmarkRenderer(context).render(page) + assert(renderedContent == expect) + } + + @Test + fun noPlatformHintCase() { + val page = TestPage { + divergentGroup(ContentDivergentGroup.GroupID("test"), implicitlySourceSetHinted = false) { + instance(setOf(DRI("test", "Test")), setOf(js)) { + divergent { + text("a") + } + } + } + } + val expect = "//[testPage](test-page.md)\n\n[js] \nContent \na \n\n\n" + CommonmarkRenderer(context).render(page) + assert(renderedContent == expect) + } + + @Test + fun divergentBetweenSourceSets() { + val page = TestPage { + divergentGroup(ContentDivergentGroup.GroupID("test")) { + instance(setOf(DRI("test", "Test")), setOf(js)) { + divergent { + text("a") + } + } + instance(setOf(DRI("test", "Test")), setOf(jvm)) { + divergent { + text("b") + } + } + instance(setOf(DRI("test", "Test")), setOf(native)) { + divergent { + text("c") + } + } + } + } + val expect = "//[testPage](test-page.md)\n\n[js, jvm, native] \nContent \n[js] \na \n[jvm] \nb \n[native] \nc \n\n\n" + CommonmarkRenderer(context).render(page) + assert(renderedContent == expect) + } + + @Test + fun divergentInOneSourceSet() { + val page = TestPage { + divergentGroup(ContentDivergentGroup.GroupID("test")) { + instance(setOf(DRI("test", "Test")), setOf(js)) { + divergent { + text("a") + } + } + instance(setOf(DRI("test", "Test2")), setOf(js)) { + divergent { + text("b") + } + } + instance(setOf(DRI("test", "Test3")), setOf(js)) { + divergent { + text("c") + } + } + } + } + val expect = "//[testPage](test-page.md)\n\n[js] \nContent \na \nb \nc \n\n\n" + CommonmarkRenderer(context).render(page) + assert(renderedContent == expect) + } + + @Test + fun divergentInAndBetweenSourceSets() { + val page = TestPage { + divergentGroup(ContentDivergentGroup.GroupID("test")) { + instance(setOf(DRI("test", "Test")), setOf(native)) { + divergent { + text("a") + } + } + instance(setOf(DRI("test", "Test")), setOf(js)) { + divergent { + text("b") + } + } + instance(setOf(DRI("test", "Test")), setOf(jvm)) { + divergent { + text("c") + } + } + instance(setOf(DRI("test", "Test2")), setOf(js)) { + divergent { + text("d") + } + } + instance(setOf(DRI("test", "Test3")), setOf(native)) { + divergent { + text("e") + } + } + } + } + val expect = "//[testPage](test-page.md)\n\n[native, js, jvm] \nContent \n[native] \na \n[js] \nb \n[jvm] \nc \n[js] \nd \n[native] \ne \n\n\n" + CommonmarkRenderer(context).render(page) + assert(renderedContent == expect) + } + + @Test + fun divergentInAndBetweenSourceSetsWithGrouping() { + val page = TestPage { + divergentGroup(ContentDivergentGroup.GroupID("test")) { + instance(setOf(DRI("test", "Test")), setOf(native)) { + divergent { + text("a") + } + after { + text("a+") + } + } + instance(setOf(DRI("test", "Test")), setOf(js)) { + divergent { + text("b") + } + after { + text("bd+") + } + } + instance(setOf(DRI("test", "Test")), setOf(jvm)) { + divergent { + text("c") + } + } + instance(setOf(DRI("test", "Test2")), setOf(js)) { + divergent { + text("d") + } + after { + text("bd+") + } + } + instance(setOf(DRI("test", "Test3")), setOf(native)) { + divergent { + text("e") + } + after { + text("e+") + } + } + } + } + val expect = "//[testPage](test-page.md)\n\n[native] \nContent \na \nMore info \na+ \n\n\n[js] \nContent \nb \nd \nMore info \nbd+ \n\n\n[jvm] \nContent \nc \n\n\n[native] \nContent \ne \nMore info \ne+ \n\n\n" + CommonmarkRenderer(context).render(page) + assert(renderedContent == expect) + } + + @Test + fun divergentSameBefore() { + val page = TestPage { + divergentGroup(ContentDivergentGroup.GroupID("test")) { + instance(setOf(DRI("test", "Test")), setOf(native)) { + before { + text("ab-") + } + divergent { + text("a") + } + } + instance(setOf(DRI("test", "Test2")), setOf(native)) { + before { + text("ab-") + } + divergent { + text("b") + } + } + } + } + val expect = "//[testPage](test-page.md)\n\n[native] \nBrief description \nab- \nContent \na \nb \n\n\n" + CommonmarkRenderer(context).render(page) + assert(renderedContent == expect) + } + + @Test + fun divergentSameAfter() { + val page = TestPage { + divergentGroup(ContentDivergentGroup.GroupID("test")) { + instance(setOf(DRI("test", "Test")), setOf(native)) { + divergent { + text("a") + } + after { + text("ab+") + } + } + instance(setOf(DRI("test", "Test2")), setOf(native)) { + divergent { + text("b") + } + after { + text("ab+") + } + } + } + } + val expect = "//[testPage](test-page.md)\n\n[native] \nContent \na \nb \nMore info \nab+ \n\n\n" + CommonmarkRenderer(context).render(page) + assert(renderedContent == expect) + } + + @Test + fun divergentGroupedByBeforeAndAfter() { + val page = TestPage { + divergentGroup(ContentDivergentGroup.GroupID("test")) { + instance(setOf(DRI("test", "Test")), setOf(native)) { + before { + text("ab-") + } + divergent { + text("a") + } + after { + text("ab+") + } + } + instance(setOf(DRI("test", "Test2")), setOf(native)) { + before { + text("ab-") + } + divergent { + text("b") + } + after { + text("ab+") + } + } + } + } + val expect = "//[testPage](test-page.md)\n\n[native] \nBrief description \nab- \nContent \na \nb \nMore info \nab+ \n\n\n" + CommonmarkRenderer(context).render(page) + assert(renderedContent == expect) + } + + @Test + fun divergentDifferentBeforeAndAfter() { + val page = TestPage { + divergentGroup(ContentDivergentGroup.GroupID("test")) { + instance(setOf(DRI("test", "Test")), setOf(native)) { + before { + text("a-") + } + divergent { + text("a") + } + after { + text("ab+") + } + } + instance(setOf(DRI("test", "Test2")), setOf(native)) { + before { + text("b-") + } + divergent { + text("b") + } + after { + text("ab+") + } + } + } + } + val expect = "//[testPage](test-page.md)\n\n[native] \nBrief description \na- \nContent \na \nMore info \nab+ \n\n\n[native] \nBrief description \nb- \nContent \nb \nMore info \nab+ \n\n\n" + CommonmarkRenderer(context).render(page) + assert(renderedContent == expect) + } + + @Test + fun divergentInAndBetweenSourceSetsWithGroupingAncCommonParts() { + val page = TestPage { + divergentGroup(ContentDivergentGroup.GroupID("test")) { + instance(setOf(DRI("test", "Test")), setOf(native)) { + divergent { + text("a") + } + after { + text("a+") + } + } + instance(setOf(DRI("test", "Test")), setOf(js)) { + divergent { + text("b") + } + after { + text("bd+") + } + } + instance(setOf(DRI("test", "Test")), setOf(jvm)) { + divergent { + text("c") + } + after { + text("bd+") + } + } + instance(setOf(DRI("test", "Test2")), setOf(js)) { + divergent { + text("d") + } + after { + text("bd+") + } + } + instance(setOf(DRI("test", "Test3")), setOf(native)) { + divergent { + text("e") + } + after { + text("e+") + } + } + } + } + val expect = "//[testPage](test-page.md)\n\n[native] \nContent \na \nMore info \na+ \n\n\n[js, jvm] \nContent \n[js] \nb \n[jvm] \nc \n[js] \nd \nMore info \nbd+ \n\n\n[native] \nContent \ne \nMore info \ne+ \n\n\n" + CommonmarkRenderer(context).render(page) + assert(renderedContent == expect) + } +}
\ No newline at end of file diff --git a/plugins/gfm/src/test/kotlin/renderers/gfm/GfmRenderingOnlyTestBase.kt b/plugins/gfm/src/test/kotlin/renderers/gfm/GfmRenderingOnlyTestBase.kt new file mode 100644 index 00000000..6d0dd3a6 --- /dev/null +++ b/plugins/gfm/src/test/kotlin/renderers/gfm/GfmRenderingOnlyTestBase.kt @@ -0,0 +1,32 @@ +package renderers.gfm + +import org.jetbrains.dokka.DokkaConfigurationImpl +import org.jetbrains.dokka.gfm.GfmPlugin +import org.jetbrains.dokka.gfm.MarkdownLocationProviderFactory +import org.jetbrains.dokka.testApi.context.MockContext +import org.jetbrains.dokka.base.DokkaBase +import org.jetbrains.dokka.base.renderers.RootCreator +import org.jetbrains.dokka.base.resolvers.external.DokkaExternalLocationProviderFactory +import org.jetbrains.dokka.base.resolvers.external.JavadocExternalLocationProviderFactory +import renderers.RenderingOnlyTestBase +import utils.TestOutputWriter + +abstract class GfmRenderingOnlyTestBase : RenderingOnlyTestBase<String>() { + + val files = TestOutputWriter() + override val context = MockContext( + DokkaBase().outputWriter to { _ -> files }, + DokkaBase().locationProviderFactory to ::MarkdownLocationProviderFactory, + DokkaBase().externalLocationProviderFactory to { ::JavadocExternalLocationProviderFactory }, + DokkaBase().externalLocationProviderFactory to { ::DokkaExternalLocationProviderFactory }, + GfmPlugin().gfmPreprocessors to { _ -> RootCreator }, + + testConfiguration = DokkaConfigurationImpl( + "", null, false, emptyList(), emptyList(), emptyMap(), emptyList(), false + ) + ) + + override val renderedContent: String by lazy { + files.contents.getValue("test-page.md") + } +} diff --git a/plugins/gfm/src/test/kotlin/renderers/gfm/GroupWrappingTest.kt b/plugins/gfm/src/test/kotlin/renderers/gfm/GroupWrappingTest.kt new file mode 100644 index 00000000..42839282 --- /dev/null +++ b/plugins/gfm/src/test/kotlin/renderers/gfm/GroupWrappingTest.kt @@ -0,0 +1,76 @@ +package renderers.gfm + +import org.jetbrains.dokka.gfm.CommonmarkRenderer +import org.jetbrains.dokka.pages.TextStyle +import org.junit.jupiter.api.Test +import renderers.* + +class GroupWrappingTest : GfmRenderingOnlyTestBase() { + + @Test + fun notWrapped() { + val page = TestPage { + group { + text("a") + text("b") + } + text("c") + } + + CommonmarkRenderer(context).render(page) + + assert(renderedContent == "//[testPage](test-page.md)\n\nabc") + } + + @Test + fun paragraphWrapped() { + val page = TestPage { + group(styles = setOf(TextStyle.Paragraph)) { + text("a") + text("b") + } + text("c") + } + + CommonmarkRenderer(context).render(page) + + assert(renderedContent == "//[testPage](test-page.md)\n\n\n\nab\n\nc") + } + + @Test + fun blockWrapped() { + val page = TestPage { + group(styles = setOf(TextStyle.Block)) { + text("a") + text("b") + } + text("c") + } + + CommonmarkRenderer(context).render(page) + + assert(renderedContent == "//[testPage](test-page.md)\n\nab \nc") + } + + @Test + fun nested() { + val page = TestPage { + group(styles = setOf(TextStyle.Block)) { + text("a") + group(styles = setOf(TextStyle.Block)) { + group(styles = setOf(TextStyle.Block)) { + text("b") + text("c") + } + } + text("d") + } + } + + CommonmarkRenderer(context).render(page) + +// renderedContent.match(Div("a", Div(Div("bc")), "d")) + assert(renderedContent == "//[testPage](test-page.md)\n\nabc \n \nd \n") + } + +} diff --git a/plugins/gfm/src/test/kotlin/renderers/gfm/SimpleElementsTest.kt b/plugins/gfm/src/test/kotlin/renderers/gfm/SimpleElementsTest.kt new file mode 100644 index 00000000..7464c079 --- /dev/null +++ b/plugins/gfm/src/test/kotlin/renderers/gfm/SimpleElementsTest.kt @@ -0,0 +1,70 @@ +package renderers.gfm + +import org.jetbrains.dokka.gfm.CommonmarkRenderer +import org.junit.jupiter.api.Test +import renderers.TestPage +import org.jetbrains.dokka.base.translators.documentables.* +import org.jetbrains.dokka.pages.TextStyle + +class SimpleElementsTest : GfmRenderingOnlyTestBase() { + + @Test + fun header() { + val page = TestPage { + header(1, "The Hobbit or There and Back Again") + } + val expect = "//[testPage](test-page.md)\n\n\n\n# The Hobbit or There and Back Again \n" + CommonmarkRenderer(context).render(page) + assert(renderedContent == expect) + } + + @Test + fun link() { + val page = TestPage { + link("They are not all accounted for, the lost Seeing Stones.", "http://www.google.com") + } + val expect = "//[testPage](test-page.md)\n\n[They are not all accounted for, the lost Seeing Stones.](http://www.google.com)" + CommonmarkRenderer(context).render(page) + assert(renderedContent == expect) + } + + @Test + fun bold() { + val page = TestPage { + text("That there’s some good in this world, Mr. Frodo… and it’s worth fighting for.", styles = setOf(TextStyle.Bold)) + } + val expect = "//[testPage](test-page.md)\n\n**That there’s some good in this world, Mr. Frodo… and it’s worth fighting for.**" + CommonmarkRenderer(context).render(page) + assert(renderedContent == expect) + } + + @Test + fun italic() { + val page = TestPage { + text("Even the smallest person can change the course of the future.", styles = setOf(TextStyle.Italic)) + } + val expect = "//[testPage](test-page.md)\n\n*Even the smallest person can change the course of the future.*" + CommonmarkRenderer(context).render(page) + assert(renderedContent == expect) + } + + @Test + fun italicAndBold() { + val page = TestPage { + text("There is no curse in Elvish, Entish, or the tongues of Men for this treachery.", styles = setOf(TextStyle.Bold, TextStyle.Italic)) + } + val expect = "//[testPage](test-page.md)\n\n***There is no curse in Elvish, Entish, or the tongues of Men for this treachery.***" + CommonmarkRenderer(context).render(page) + assert(renderedContent == expect) + } + + @Test + fun strikethrough() { + val page = TestPage { + text("A day may come when the courage of men fails… but it is not THIS day", styles = setOf(TextStyle.Strikethrough)) + } + val expect = "//[testPage](test-page.md)\n\n~~A day may come when the courage of men fails… but it is not THIS day~~" + CommonmarkRenderer(context).render(page) + assert(renderedContent == expect) + } +}
\ No newline at end of file diff --git a/plugins/gfm/src/test/kotlin/renderers/gfm/SourceSetDependentHintTest.kt b/plugins/gfm/src/test/kotlin/renderers/gfm/SourceSetDependentHintTest.kt new file mode 100644 index 00000000..e181e3a2 --- /dev/null +++ b/plugins/gfm/src/test/kotlin/renderers/gfm/SourceSetDependentHintTest.kt @@ -0,0 +1,137 @@ +package renderers.gfm + +import org.jetbrains.dokka.DokkaSourceSetID +import org.jetbrains.dokka.Platform +import org.jetbrains.dokka.SourceRootImpl +import org.jetbrains.dokka.gfm.CommonmarkRenderer +import org.jetbrains.dokka.pages.TextStyle +import org.junit.jupiter.api.Test +import renderers.TestPage +import renderers.defaultSourceSet + +class SourceSetDependentHintTest : GfmRenderingOnlyTestBase() { + + private val pl1 = defaultSourceSet.copy( + "root", + "pl1", + DokkaSourceSetID("root", "pl1"), + analysisPlatform = Platform.js, + sourceRoots = listOf(SourceRootImpl("pl1")) + ) + private val pl2 = defaultSourceSet.copy( + "root", + "pl2", + DokkaSourceSetID("root", "pl2"), + analysisPlatform = Platform.jvm, + sourceRoots = listOf(SourceRootImpl("pl1")) + ) + private val pl3 = defaultSourceSet.copy( + "root", + "pl3", + DokkaSourceSetID("root", "pl3"), + analysisPlatform = Platform.native, + sourceRoots = listOf(SourceRootImpl("pl1")) + ) + + @Test + fun platformIndependentCase() { + val page = TestPage { + sourceSetDependentHint(sourceSets = setOf(pl1, pl2, pl3), styles = setOf(TextStyle.Block)) { + text("a") + text("b") + text("c") + } + } + + CommonmarkRenderer(context).render(page) + assert(renderedContent == "//[testPage](test-page.md)\n\n [pl1, pl2, pl3] abc \n \n") + } + + @Test + fun completelyDivergentCase() { + val page = TestPage { + sourceSetDependentHint(sourceSets = setOf(pl1, pl2, pl3), styles = setOf(TextStyle.Block)) { + text("a", sourceSets = setOf(pl1)) + text("b", sourceSets = setOf(pl2)) + text("c", sourceSets = setOf(pl3)) + } + } + + CommonmarkRenderer(context).render(page) + assert(renderedContent == "//[testPage](test-page.md)\n\n [pl1] a \n \n [pl2] b \n \n [pl3] c \n \n") + } + + @Test + fun overlappingCase() { + val page = TestPage { + sourceSetDependentHint(sourceSets = setOf(pl1, pl2), styles = setOf(TextStyle.Block)) { + text("a", sourceSets = setOf(pl1)) + text("b", sourceSets = setOf(pl1, pl2)) + text("c", sourceSets = setOf(pl2)) + } + } + + CommonmarkRenderer(context).render(page) + assert(renderedContent == "//[testPage](test-page.md)\n\n [pl1] ab \n \n [pl2] bc \n \n") + } + + @Test + fun caseThatCanBeSimplified() { + val page = TestPage { + sourceSetDependentHint(sourceSets = setOf(pl1, pl2), styles = setOf(TextStyle.Block)) { + text("a", sourceSets = setOf(pl1, pl2)) + text("b", sourceSets = setOf(pl1)) + text("b", sourceSets = setOf(pl2)) + } + } + + CommonmarkRenderer(context).render(page) + assert(renderedContent == "//[testPage](test-page.md)\n\n [pl1, pl2] ab \n \n") + } + + @Test + fun caseWithGroupBreakingSimplification() { + val page = TestPage { + sourceSetDependentHint(sourceSets = setOf(pl1, pl2), styles = setOf(TextStyle.Block)) { + group(styles = setOf(TextStyle.Block)) { + text("a", sourceSets = setOf(pl1, pl2)) + text("b", sourceSets = setOf(pl1)) + } + text("b", sourceSets = setOf(pl2)) + } + } + + CommonmarkRenderer(context).render(page) + assert(renderedContent == "//[testPage](test-page.md)\n\n [pl1] ab \n \n \n [pl2] a \nb \n \n") + } + + @Test + fun caseWithGroupNotBreakingSimplification() { + val page = TestPage { + sourceSetDependentHint(sourceSets = setOf(pl1, pl2)) { + group { + text("a", sourceSets = setOf(pl1, pl2)) + text("b", sourceSets = setOf(pl1)) + } + text("b", sourceSets = setOf(pl2)) + } + } + + CommonmarkRenderer(context).render(page) + assert(renderedContent == "//[testPage](test-page.md)\n\n [pl1, pl2] ab \n") + } + + @Test + fun partiallyUnifiedCase() { + val page = TestPage { + sourceSetDependentHint(sourceSets = setOf(pl1, pl2, pl3), styles = setOf(TextStyle.Block)) { + text("a", sourceSets = setOf(pl1)) + text("a", sourceSets = setOf(pl2)) + text("b", sourceSets = setOf(pl3)) + } + } + + CommonmarkRenderer(context).render(page) + assert(renderedContent == "//[testPage](test-page.md)\n\n [pl1, pl2] a \n \n [pl3] b \n \n") + } +}
\ No newline at end of file diff --git a/plugins/javadoc/build.gradle.kts b/plugins/javadoc/build.gradle.kts new file mode 100644 index 00000000..85c2be44 --- /dev/null +++ b/plugins/javadoc/build.gradle.kts @@ -0,0 +1,16 @@ +import org.jetbrains.registerDokkaArtifactPublication + +dependencies { + implementation("com.soywiz.korlibs.korte:korte-jvm:1.10.3") + implementation("org.jetbrains.kotlinx:kotlinx-html-jvm:0.6.10") + implementation(project(":plugins:base")) + implementation(project(":plugins:kotlin-as-java")) + testImplementation(project(":plugins:base:test-utils")) + + val coroutines_version: String by project + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version") +} + +registerDokkaArtifactPublication("javadocPlugin") { + artifactId = "javadoc-plugin" +} diff --git a/plugins/javadoc/src/main/kotlin/javadoc/JavadocDocumentableToPageTranslator.kt b/plugins/javadoc/src/main/kotlin/javadoc/JavadocDocumentableToPageTranslator.kt new file mode 100644 index 00000000..840bdc55 --- /dev/null +++ b/plugins/javadoc/src/main/kotlin/javadoc/JavadocDocumentableToPageTranslator.kt @@ -0,0 +1,18 @@ +package javadoc + +import org.jetbrains.dokka.base.signatures.SignatureProvider +import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter +import org.jetbrains.dokka.model.DModule +import org.jetbrains.dokka.pages.ModulePageNode +import org.jetbrains.dokka.pages.RootPageNode +import org.jetbrains.dokka.transformers.documentation.DocumentableToPageTranslator +import org.jetbrains.dokka.utilities.DokkaLogger + +class JavadocDocumentableToPageTranslator( + private val commentsToContentConverter: CommentsToContentConverter, + private val signatureProvider: SignatureProvider, + private val logger: DokkaLogger +) : DocumentableToPageTranslator { + override fun invoke(module: DModule): RootPageNode = + JavadocPageCreator(commentsToContentConverter, signatureProvider, logger).pageForModule(module) +}
\ No newline at end of file diff --git a/plugins/javadoc/src/main/kotlin/javadoc/JavadocPageCreator.kt b/plugins/javadoc/src/main/kotlin/javadoc/JavadocPageCreator.kt new file mode 100644 index 00000000..7420f78e --- /dev/null +++ b/plugins/javadoc/src/main/kotlin/javadoc/JavadocPageCreator.kt @@ -0,0 +1,238 @@ +package javadoc + +import javadoc.pages.* +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.Platform +import org.jetbrains.dokka.base.signatures.SignatureProvider +import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter +import org.jetbrains.dokka.base.transformers.pages.comments.DocTagToContentConverter +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.doc.Description +import org.jetbrains.dokka.model.doc.Index +import org.jetbrains.dokka.model.doc.Param +import org.jetbrains.dokka.model.doc.TagWrapper +import org.jetbrains.dokka.model.properties.PropertyContainer +import org.jetbrains.dokka.model.properties.WithExtraProperties +import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.utilities.DokkaLogger +import kotlin.reflect.KClass + +open class JavadocPageCreator( + commentsToContentConverter: CommentsToContentConverter, + private val signatureProvider: SignatureProvider, + val logger: DokkaLogger +) { + + fun pageForModule(m: DModule): JavadocModulePageNode = + JavadocModulePageNode( + name = m.name.ifEmpty { "root" }, + content = contentForModule(m), + children = m.packages.map { pageForPackage(it) }, + dri = setOf(m.dri) + ) + + fun pageForPackage(p: DPackage) = + JavadocPackagePageNode(p.name, contentForPackage(p), setOf(p.dri), p, + p.classlikes.mapNotNull { pageForClasslike(it) } // TODO: nested classlikes + ) + + fun pageForClasslike(c: DClasslike): JavadocClasslikePageNode? = + c.highestJvmSourceSet?.let { jvm -> + JavadocClasslikePageNode( + name = c.name.orEmpty(), + content = contentForClasslike(c), + dri = setOf(c.dri), + signature = signatureForNode(c, jvm), + description = c.descriptionToContentNodes(), + constructors = (c as? WithConstructors)?.constructors?.mapNotNull { it.toJavadocFunction() }.orEmpty(), + methods = c.functions.mapNotNull { it.toJavadocFunction() }, + entries = (c as? DEnum)?.entries?.map { + JavadocEntryNode( + it.dri, + it.name, + signatureForNode(it, jvm), + it.descriptionToContentNodes(jvm), + PropertyContainer.withAll(it.indexesInDocumentation()) + ) + }.orEmpty(), + classlikes = c.classlikes.mapNotNull { pageForClasslike(it) }, + properties = c.properties.map { + JavadocPropertyNode( + it.dri, + it.name, + signatureForNode(it, jvm), + it.descriptionToContentNodes(jvm), + PropertyContainer.withAll(it.indexesInDocumentation()) + ) + }, + documentable = c, + extra = ((c as? WithExtraProperties<Documentable>)?.extra ?: PropertyContainer.empty()) + c.indexesInDocumentation() + ) + } + + private fun contentForModule(m: DModule): JavadocContentNode = + JavadocContentGroup( + setOf(m.dri), + JavadocContentKind.OverviewSummary, + m.jvmSourceSets.toSet() + ) { + title(m.name, m.brief(), "0.0.1", dri = setOf(m.dri), kind = ContentKind.Main) + leafList(setOf(m.dri), + ContentKind.Packages, JavadocList( + "Packages", "Package", + m.packages.sortedBy { it.name }.map { p -> + RowJavadocListEntry( + LinkJavadocListEntry(p.name, setOf(p.dri), JavadocContentKind.PackageSummary, sourceSets), + p.brief() + ) + } + )) + } + + private fun contentForPackage(p: DPackage): JavadocContentNode = + JavadocContentGroup( + setOf(p.dri), + JavadocContentKind.PackageSummary, + p.jvmSourceSets.toSet() + ) { + title(p.name, p.brief(), "0.0.1", dri = setOf(p.dri), kind = ContentKind.Packages) + val rootList = p.classlikes.groupBy { it::class }.map { (key, value) -> + JavadocList(key.tabTitle, key.colTitle, value.map { c -> + RowJavadocListEntry( + LinkJavadocListEntry(c.name ?: "", setOf(c.dri), JavadocContentKind.Class, sourceSets), + c.brief() + ) + }) + } + rootList(setOf(p.dri), JavadocContentKind.Class, rootList) + } + + private val KClass<out DClasslike>.colTitle: String + get() = when(this) { + DClass::class -> "Class" + DObject::class -> "Object" + DAnnotation::class -> "Annotation" + DEnum::class -> "Enum" + DInterface::class -> "Interface" + else -> "" + } + + private val KClass<out DClasslike>.tabTitle: String + get() = "$colTitle Summary" + + private fun contentForClasslike(c: DClasslike): JavadocContentNode = + JavadocContentGroup( + setOf(c.dri), + JavadocContentKind.Class, + c.jvmSourceSets.toSet() + ) { + title( + c.name.orEmpty(), + c.brief(), + "0.0.1", + parent = c.dri.packageName, + dri = setOf(c.dri), + kind = JavadocContentKind.Class + ) + } + + private fun DFunction.toJavadocFunction() = highestJvmSourceSet?.let { jvm -> + JavadocFunctionNode( + name = name, + dri = dri, + signature = signatureForNode(this, jvm), + brief = brief(jvm), + parameters = parameters.mapNotNull { + val signature = signatureForNode(it, jvm) + signature.modifiers?.let { type -> + JavadocParameterNode( + name = it.name.orEmpty(), + type = type, + description = it.brief(), + typeBound = it.type, + dri = it.dri, + extra = PropertyContainer.withAll(it.indexesInDocumentation()) + ) + } + }, + extra = extra + indexesInDocumentation() + ) + } + + private val Documentable.jvmSourceSets + get() = sourceSets.filter { it.analysisPlatform == Platform.jvm } + + private val Documentable.highestJvmSourceSet + get() = jvmSourceSets.let { sources -> + sources.firstOrNull { it != expectPresentInSet } ?: sources.firstOrNull() + } + + private val firstSentenceRegex = Regex("^((?:[^.?!]|[.!?](?!\\s))*[.!?])") + + private inline fun <reified T : TagWrapper> Documentable.findNodeInDocumentation(sourceSetData: DokkaSourceSet?): T? = + documentation[sourceSetData]?.firstChildOfTypeOrNull<T>() + + private fun Documentable.descriptionToContentNodes(sourceSet: DokkaSourceSet? = highestJvmSourceSet) = + contentNodesFromType<Description>(sourceSet) + + private fun DParameter.paramsToContentNodes(sourceSet: DokkaSourceSet? = highestJvmSourceSet) = + contentNodesFromType<Param>(sourceSet) + + private inline fun <reified T : TagWrapper> Documentable.contentNodesFromType(sourceSet: DokkaSourceSet?) = + findNodeInDocumentation<T>(sourceSet)?.let { + DocTagToContentConverter.buildContent( + it.root, + DCI(setOf(dri), JavadocContentKind.OverviewSummary), + sourceSets.toSet() + ) + }.orEmpty() + + fun List<ContentNode>.nodeForJvm(jvm: DokkaSourceSet): ContentNode = + first { it.sourceSets.contains(jvm) } + + private fun Documentable.brief(sourceSet: DokkaSourceSet? = highestJvmSourceSet): List<ContentNode> = + briefFromContentNodes(descriptionToContentNodes(sourceSet)) + + private fun briefFromContentNodes(description: List<ContentNode>): List<ContentNode> { + val contents = mutableListOf<ContentNode>() + for (node in description) { + if (node is ContentText && firstSentenceRegex.containsMatchIn(node.text)) { + contents.add(node.copy(text = firstSentenceRegex.find(node.text)?.value.orEmpty())) + break + } else { + contents.add(node) + } + } + return contents + } + + private fun DParameter.brief(sourceSet: DokkaSourceSet? = highestJvmSourceSet): List<ContentNode> = + briefFromContentNodes(paramsToContentNodes(sourceSet).dropWhile { it is ContentDRILink }) + + private fun ContentNode.asJavadocNode(): JavadocSignatureContentNode = + (this as ContentGroup).firstChildOfTypeOrNull<JavadocSignatureContentNode>() + ?: throw IllegalStateException("No content for javadoc signature found") + + private fun signatureForNode(documentable: Documentable, sourceSet: DokkaSourceSet): JavadocSignatureContentNode = + signatureProvider.signature(documentable).nodeForJvm(sourceSet).asJavadocNode() + + private fun Documentable.indexesInDocumentation(): JavadocIndexExtra { + val indexes = documentation[highestJvmSourceSet]?.withDescendants()?.filterIsInstance<Index>()?.toList().orEmpty() + return JavadocIndexExtra( + indexes.map { + ContentGroup( + children = DocTagToContentConverter.buildContent( + it, + DCI(setOf(dri), JavadocContentKind.OverviewSummary), + sourceSets.toSet() + ), + dci = DCI(setOf(dri), JavadocContentKind.OverviewSummary), + sourceSets = sourceSets.toSet(), + style = emptySet(), + extra = PropertyContainer.empty() + ) + } + ) + } +} + diff --git a/plugins/javadoc/src/main/kotlin/javadoc/JavadocPlugin.kt b/plugins/javadoc/src/main/kotlin/javadoc/JavadocPlugin.kt new file mode 100644 index 00000000..8283bd78 --- /dev/null +++ b/plugins/javadoc/src/main/kotlin/javadoc/JavadocPlugin.kt @@ -0,0 +1,53 @@ +package org.jetbrains.dokka.javadoc + +import javadoc.JavadocDocumentableToPageTranslator +import javadoc.location.JavadocLocationProviderFactory +import javadoc.renderer.KorteJavadocRenderer +import javadoc.signatures.JavadocSignatureProvider +import org.jetbrains.dokka.CoreExtensions +import org.jetbrains.dokka.base.DokkaBase +import org.jetbrains.dokka.kotlinAsJava.KotlinAsJavaPlugin +import org.jetbrains.dokka.plugability.DokkaPlugin +import org.jetbrains.dokka.plugability.querySingle + +class JavadocPlugin : DokkaPlugin() { + + val dokkaBasePlugin by lazy { plugin<DokkaBase>() } + val kotinAsJavaPlugin by lazy { plugin<KotlinAsJavaPlugin>() } + + val locationProviderFactory by extensionPoint<JavadocLocationProviderFactory>() + + val dokkaJavadocPlugin by extending { + (CoreExtensions.renderer + providing { ctx -> KorteJavadocRenderer(dokkaBasePlugin.querySingle { outputWriter }, ctx, "views") } + override dokkaBasePlugin.htmlRenderer) + } + + val pageTranslator by extending { + CoreExtensions.documentableToPageTranslator providing { context -> + JavadocDocumentableToPageTranslator( + dokkaBasePlugin.querySingle { commentsToContentConverter }, + dokkaBasePlugin.querySingle { signatureProvider }, + context.logger + ) + } override dokkaBasePlugin.documentableToPageTranslator + } + + val javadocLocationProviderFactory by extending { + locationProviderFactory providing { context -> + JavadocLocationProviderFactory(context) + } + } + + val javadocSignatureProvider by extending { + val dokkaBasePlugin = plugin<DokkaBase>() + dokkaBasePlugin.signatureProvider providing { ctx -> + JavadocSignatureProvider( + ctx.single( + dokkaBasePlugin.commentsToContentConverter + ), ctx.logger + ) + } override kotinAsJavaPlugin.javaSignatureProvider + } +} + diff --git a/plugins/javadoc/src/main/kotlin/javadoc/location/JavadocLocationProvider.kt b/plugins/javadoc/src/main/kotlin/javadoc/location/JavadocLocationProvider.kt new file mode 100644 index 00000000..49278b06 --- /dev/null +++ b/plugins/javadoc/src/main/kotlin/javadoc/location/JavadocLocationProvider.kt @@ -0,0 +1,126 @@ +package javadoc.location + +import javadoc.pages.* +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.base.resolvers.local.BaseLocationProvider +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.links.Nullable +import org.jetbrains.dokka.links.parent +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.pages.ContentPage +import org.jetbrains.dokka.pages.PageNode +import org.jetbrains.dokka.pages.RootPageNode +import org.jetbrains.dokka.plugability.DokkaContext +import java.util.* + +class JavadocLocationProvider(pageRoot: RootPageNode, dokkaContext: DokkaContext) : + BaseLocationProvider(dokkaContext) { + + private val pathIndex = IdentityHashMap<PageNode, List<String>>().apply { + fun registerPath(page: PageNode, prefix: List<String> = emptyList()) { + val newPrefix = prefix + page.takeIf { it is JavadocPackagePageNode }?.name.orEmpty() + val path = (prefix + when (page) { + is AllClassesPage -> listOf("allclasses") + is TreeViewPage -> if (page.classes == null) + listOf("overview-tree") + else + listOf("package-tree") + is ContentPage -> if (page.dri.isNotEmpty() && page.dri.first().classNames != null) + listOfNotNull(page.dri.first().classNames) + else if (page is JavadocPackagePageNode) + listOf(page.name, "package-summary") + else + listOf("index") + else -> emptyList() + }).filterNot { it.isEmpty() } + + put(page, path) + page.children.forEach { registerPath(it, newPrefix) } + + } + put(pageRoot, listOf("index")) + pageRoot.children.forEach { registerPath(it) } + } + + private val nodeIndex = HashMap<DRI, PageNode>().apply { + fun registerNode(node: PageNode) { + if (node is ContentPage) put(node.dri.first(), node) + node.children.forEach(::registerNode) + } + registerNode(pageRoot) + } + + private operator fun IdentityHashMap<PageNode, List<String>>.get(dri: DRI) = this[nodeIndex[dri]] + + private fun List<String>.relativeTo(context: List<String>): String { + val contextPath = context.dropLast(1) + val commonPathElements = zip(contextPath).takeWhile { (a, b) -> a == b }.count() + return (List(contextPath.size - commonPathElements) { ".." } + this.drop(commonPathElements)).joinToString("/") + } + + private fun JavadocClasslikePageNode.findAnchorableByDRI(dri: DRI): AnchorableJavadocNode? = + (constructors + methods + entries + properties).firstOrNull { it.dri == dri } + + override fun resolve(dri: DRI, sourceSets: Set<DokkaSourceSet>, context: PageNode?): String { + return nodeIndex[dri]?.let { resolve(it, context) } + ?: nodeIndex[dri.parent]?.let { + val anchor = when (val anchorElement = (it as? JavadocClasslikePageNode)?.findAnchorableByDRI(dri)) { + is JavadocFunctionNode -> anchorElement.getAnchor() + is JavadocEntryNode -> anchorElement.name + is JavadocPropertyNode -> anchorElement.name + else -> anchorForDri(dri) + } + "${resolve(it, context, skipExtension = true)}.html#$anchor" + } + ?: getExternalLocation(dri, sourceSets) + } + + private fun JavadocFunctionNode.getAnchor(): String = + "$name(${parameters.joinToString(",") { + when (val bound = if (it.typeBound is org.jetbrains.dokka.model.Nullable) it.typeBound.inner else it.typeBound) { + is TypeConstructor -> bound.dri.classNames.orEmpty() + is OtherParameter -> bound.name + is PrimitiveJavaType -> bound.name + is UnresolvedBound -> bound.name + is JavaObject -> "Object" + else -> bound.toString() + } + }})" + + fun anchorForFunctionNode(node: JavadocFunctionNode) = node.getAnchor() + + private fun anchorForDri(dri: DRI): String = + dri.callable?.let { callable -> + "${callable.name}(${callable.params.joinToString(",") { + ((it as? Nullable)?.wrapped ?: it).toString() + }})" + } ?: dri.classNames.orEmpty() + + override fun resolve(node: PageNode, context: PageNode?, skipExtension: Boolean): String = + pathIndex[node]?.relativeTo(pathIndex[context].orEmpty())?.let { + if (skipExtension) it.removeSuffix(".html") else it + } ?: run { + throw IllegalStateException("Path for ${node::class.java.canonicalName}:${node.name} not found") + } + + fun resolve(link: LinkJavadocListEntry, contextRoot: PageNode? = null, skipExtension: Boolean = true) = + pathIndex[link.dri.first()]?.let { + when (link.kind) { + JavadocContentKind.Class -> it + JavadocContentKind.OverviewSummary -> it.dropLast(1) + "index" + JavadocContentKind.PackageSummary -> it.dropLast(1) + "package-summary" + JavadocContentKind.AllClasses -> it.dropLast(1) + "allclasses" + JavadocContentKind.OverviewTree -> it.dropLast(1) + "overview-tree" + JavadocContentKind.PackageTree -> it.dropLast(1) + "package-tree" + else -> it + } + }?.relativeTo(pathIndex[contextRoot].orEmpty())?.let { if (skipExtension) "$it.html" else it }.orEmpty() + + override fun resolveRoot(node: PageNode): String { + TODO("Not yet implemented") + } + + override fun ancestors(node: PageNode): List<PageNode> { + TODO("Not yet implemented") + } +}
\ No newline at end of file diff --git a/plugins/javadoc/src/main/kotlin/javadoc/location/JavadocLocationProviderFactory.kt b/plugins/javadoc/src/main/kotlin/javadoc/location/JavadocLocationProviderFactory.kt new file mode 100644 index 00000000..b6bfb48d --- /dev/null +++ b/plugins/javadoc/src/main/kotlin/javadoc/location/JavadocLocationProviderFactory.kt @@ -0,0 +1,11 @@ +package javadoc.location + +import org.jetbrains.dokka.base.resolvers.local.LocationProviderFactory +import org.jetbrains.dokka.pages.RootPageNode +import org.jetbrains.dokka.plugability.DokkaContext + +class JavadocLocationProviderFactory(private val context: DokkaContext) : LocationProviderFactory { + + override fun getLocationProvider(pageNode: RootPageNode) = + JavadocLocationProvider(pageNode, context) +}
\ No newline at end of file diff --git a/plugins/javadoc/src/main/kotlin/javadoc/pages/JavadocContentNodes.kt b/plugins/javadoc/src/main/kotlin/javadoc/pages/JavadocContentNodes.kt new file mode 100644 index 00000000..d83704a6 --- /dev/null +++ b/plugins/javadoc/src/main/kotlin/javadoc/pages/JavadocContentNodes.kt @@ -0,0 +1,173 @@ +package javadoc.pages + +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.model.properties.PropertyContainer +import org.jetbrains.dokka.pages.* + +enum class JavadocContentKind : Kind { + AllClasses, OverviewSummary, PackageSummary, Class, OverviewTree, PackageTree +} + +abstract class JavadocContentNode( + dri: Set<DRI>, + kind: Kind, + override val sourceSets: Set<DokkaSourceSet> +) : ContentNode { + override val dci: DCI = DCI(dri, kind) + override val style: Set<Style> = emptySet() + override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty() + override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentNode = this +} + +interface JavadocList { + val tabTitle: String + val colTitle: String + val children: List<JavadocListEntry> +} + +interface JavadocListEntry { + val stringTag: String +} + +class EmptyNode( + dri: DRI, + kind: Kind, + override val sourceSets: Set<DokkaSourceSet>, + override val extra: PropertyContainer<ContentNode> = PropertyContainer.empty() +) : ContentNode { + override val dci: DCI = DCI(setOf(dri), kind) + override val style: Set<Style> = emptySet() + + override fun withNewExtras(newExtras: PropertyContainer<ContentNode>): ContentNode = + EmptyNode(dci.dri.first(), dci.kind, sourceSets, newExtras) + + override fun hasAnyContent(): Boolean = false +} + +class JavadocContentGroup( + val dri: Set<DRI>, + val kind: Kind, + sourceSets: Set<DokkaSourceSet>, + override val children: List<JavadocContentNode> +) : JavadocContentNode(dri, kind, sourceSets) { + + companion object { + operator fun invoke( + dri: Set<DRI>, + kind: Kind, + sourceSets: Set<DokkaSourceSet>, + block: JavaContentGroupBuilder.() -> Unit + ): JavadocContentGroup = + JavadocContentGroup(dri, kind, sourceSets, JavaContentGroupBuilder(sourceSets).apply(block).list) + } + + override fun hasAnyContent(): Boolean = children.isNotEmpty() +} + +class JavaContentGroupBuilder(val sourceSets: Set<DokkaSourceSet>) { + val list = mutableListOf<JavadocContentNode>() +} + +class TitleNode( + val title: String, + val subtitle: List<ContentNode>, + val version: String, + val parent: String?, + val dri: Set<DRI>, + val kind: Kind, + sourceSets: Set<DokkaSourceSet> +) : JavadocContentNode(dri, kind, sourceSets) { + override fun hasAnyContent(): Boolean = !title.isBlank() || !version.isBlank() || subtitle.isNotEmpty() +} + +fun JavaContentGroupBuilder.title( + title: String, + subtitle: List<ContentNode>, + version: String, + parent: String? = null, + dri: Set<DRI>, + kind: Kind +) { + list.add(TitleNode(title, subtitle, version, parent, dri, kind, sourceSets)) +} + +class RootListNode( + val entries: List<LeafListNode>, + val dri: Set<DRI>, + val kind: Kind, + sourceSets: Set<DokkaSourceSet>, +) : JavadocContentNode(dri, kind, sourceSets) { + override fun hasAnyContent(): Boolean = children.isNotEmpty() +} + +class LeafListNode( + val tabTitle: String, + val colTitle: String, + val entries: List<JavadocListEntry>, + val dri: Set<DRI>, + val kind: Kind, + sourceSets: Set<DokkaSourceSet> +) : JavadocContentNode(dri, kind, sourceSets) { + override fun hasAnyContent(): Boolean = children.isNotEmpty() +} + + +fun JavaContentGroupBuilder.rootList( + dri: Set<DRI>, + kind: Kind, + rootList: List<JavadocList> +) { + val children = rootList.map { + LeafListNode(it.tabTitle, it.colTitle, it.children, dri, kind, sourceSets) + } + list.add(RootListNode(children, dri, kind, sourceSets)) +} + +fun JavaContentGroupBuilder.leafList( + dri: Set<DRI>, + kind: Kind, + leafList: JavadocList +) { + list.add(LeafListNode(leafList.tabTitle, leafList.colTitle, leafList.children, dri, kind, sourceSets)) +} + +fun JavadocList(tabTitle: String, colTitle: String, children: List<JavadocListEntry>) = object : JavadocList { + override val tabTitle = tabTitle + override val colTitle = colTitle + override val children = children +} + +class LinkJavadocListEntry( + val name: String, + val dri: Set<DRI>, + val kind: Kind = ContentKind.Symbol, + val sourceSets: Set<DokkaSourceSet> +) : + JavadocListEntry { + override val stringTag: String + get() = if (builtString == null) + throw IllegalStateException("stringTag for LinkJavadocListEntry accessed before build() call") + else builtString!! + + private var builtString: String? = null + + fun build(body: (String, Set<DRI>, Kind, List<DokkaSourceSet>) -> String) { + builtString = body(name, dri, kind, sourceSets.toList()) + } +} + +data class RowJavadocListEntry(val link: LinkJavadocListEntry, val doc: List<ContentNode>) : JavadocListEntry { + override val stringTag: String = "" +} + +data class JavadocSignatureContentNode( + val dri: DRI, + val kind: Kind = ContentKind.Symbol, + val annotations: ContentNode?, + val modifiers: ContentNode?, + val signatureWithoutModifiers: ContentNode, + val supertypes: ContentNode? +): JavadocContentNode(setOf(dri), kind, signatureWithoutModifiers.sourceSets) { + override fun hasAnyContent(): Boolean = true +} diff --git a/plugins/javadoc/src/main/kotlin/javadoc/pages/JavadocIndexExtra.kt b/plugins/javadoc/src/main/kotlin/javadoc/pages/JavadocIndexExtra.kt new file mode 100644 index 00000000..3ae04cae --- /dev/null +++ b/plugins/javadoc/src/main/kotlin/javadoc/pages/JavadocIndexExtra.kt @@ -0,0 +1,10 @@ +package javadoc.pages + +import org.jetbrains.dokka.model.Documentable +import org.jetbrains.dokka.model.properties.ExtraProperty +import org.jetbrains.dokka.pages.ContentNode + +data class JavadocIndexExtra(val index: List<ContentNode>) : ExtraProperty<Documentable> { + override val key: ExtraProperty.Key<Documentable, *> = JavadocIndexExtra + companion object : ExtraProperty.Key<Documentable, JavadocIndexExtra> +}
\ No newline at end of file diff --git a/plugins/javadoc/src/main/kotlin/javadoc/pages/JavadocPageNodes.kt b/plugins/javadoc/src/main/kotlin/javadoc/pages/JavadocPageNodes.kt new file mode 100644 index 00000000..daa4fda5 --- /dev/null +++ b/plugins/javadoc/src/main/kotlin/javadoc/pages/JavadocPageNodes.kt @@ -0,0 +1,436 @@ +package javadoc.pages + +import com.intellij.psi.PsiClass +import org.jetbrains.dokka.Platform +import org.jetbrains.dokka.analysis.DescriptorDocumentableSource +import org.jetbrains.dokka.analysis.PsiDocumentableSource +import org.jetbrains.dokka.analysis.from +import org.jetbrains.dokka.base.renderers.sourceSets +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.properties.PropertyContainer +import org.jetbrains.dokka.model.properties.WithExtraProperties +import org.jetbrains.dokka.pages.* +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.resolve.DescriptorUtils.getClassDescriptorForType +import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance + +interface JavadocPageNode : ContentPage + +interface WithJavadocExtra<T : Documentable> : WithExtraProperties<T> { + override fun withNewExtras(newExtras: PropertyContainer<T>): T = + throw IllegalStateException("Merging extras is not applicable for javadoc") +} + +class JavadocModulePageNode( + override val name: String, + override val content: JavadocContentNode, + override val children: List<PageNode>, + override val dri: Set<DRI> +) : + RootPageNode(), + JavadocPageNode { + + override val documentable: Documentable? = null + override val embeddedResources: List<String> = emptyList() + override fun modified(name: String, children: List<PageNode>): RootPageNode = + JavadocModulePageNode(name, content, children, dri) + + override fun modified( + name: String, + content: ContentNode, + dri: Set<DRI>, + embeddedResources: List<String>, + children: List<PageNode> + ): ContentPage = JavadocModulePageNode(name, content as JavadocContentNode, children, dri) +} + +class JavadocPackagePageNode( + override val name: String, + override val content: JavadocContentNode, + override val dri: Set<DRI>, + + override val documentable: Documentable? = null, + override val children: List<PageNode> = emptyList(), + override val embeddedResources: List<String> = listOf() +) : JavadocPageNode { + + override fun modified( + name: String, + children: List<PageNode> + ): PageNode = JavadocPackagePageNode( + name, + content, + dri, + documentable, + children, + embeddedResources + ) + + override fun modified( + name: String, + content: ContentNode, + dri: Set<DRI>, + embeddedResources: List<String>, + children: List<PageNode> + ): ContentPage = + JavadocPackagePageNode( + name, + content as JavadocContentNode, + dri, + documentable, + children, + embeddedResources + ) +} + +sealed class AnchorableJavadocNode(open val dri: DRI) + +data class JavadocEntryNode( + override val dri: DRI, + val name: String, + val signature: JavadocSignatureContentNode, + val brief: List<ContentNode>, + override val extra: PropertyContainer<DEnumEntry> = PropertyContainer.empty() +): AnchorableJavadocNode(dri), WithJavadocExtra<DEnumEntry> + +data class JavadocParameterNode( + override val dri: DRI, + val name: String, + val type: ContentNode, + val description: List<ContentNode>, + val typeBound: Bound, + override val extra: PropertyContainer<DParameter> = PropertyContainer.empty() +): AnchorableJavadocNode(dri), WithJavadocExtra<DParameter> + +data class JavadocPropertyNode( + override val dri: DRI, + val name: String, + val signature: JavadocSignatureContentNode, + val brief: List<ContentNode>, + override val extra: PropertyContainer<DProperty> = PropertyContainer.empty() +): AnchorableJavadocNode(dri), WithJavadocExtra<DProperty> + +data class JavadocFunctionNode( + val signature: JavadocSignatureContentNode, + val brief: List<ContentNode>, + val parameters: List<JavadocParameterNode>, + val name: String, + override val dri: DRI, + override val extra: PropertyContainer<DFunction> = PropertyContainer.empty() +): AnchorableJavadocNode(dri), WithJavadocExtra<DFunction> { + val isInherited: Boolean + get() { + val extra = extra[InheritedFunction] + return extra?.inheritedFrom?.keys?.firstOrNull { it.analysisPlatform == Platform.jvm }?.let { jvm -> + extra.isInherited(jvm) + } ?: false + } +} + +class JavadocClasslikePageNode( + override val name: String, + override val content: JavadocContentNode, + override val dri: Set<DRI>, + val signature: JavadocSignatureContentNode, + val description: List<ContentNode>, + val constructors: List<JavadocFunctionNode>, + val methods: List<JavadocFunctionNode>, + val entries: List<JavadocEntryNode>, + val classlikes: List<JavadocClasslikePageNode>, + val properties: List<JavadocPropertyNode>, + override val documentable: Documentable? = null, + override val children: List<PageNode> = emptyList(), + override val embeddedResources: List<String> = listOf(), + override val extra: PropertyContainer<DClasslike> = PropertyContainer.empty(), +) : JavadocPageNode, WithJavadocExtra<DClasslike> { + + val kind: String? = documentable?.kind() + val packageName = dri.first().packageName + + override fun modified( + name: String, + children: List<PageNode> + ): PageNode = JavadocClasslikePageNode( + name, + content, + dri, + signature, + description, + constructors, + methods, + entries, + classlikes, + properties, + documentable, + children, + embeddedResources, + extra + ) + + override fun modified( + name: String, + content: ContentNode, + dri: Set<DRI>, + embeddedResources: List<String>, + children: List<PageNode> + ): ContentPage = + JavadocClasslikePageNode( + name, + content as JavadocContentNode, + dri, + signature, + description, + constructors, + methods, + entries, + classlikes, + properties, + documentable, + children, + embeddedResources, + extra + ) +} + +class AllClassesPage(val classes: List<JavadocClasslikePageNode>) : JavadocPageNode { + val classEntries = + classes.map { LinkJavadocListEntry(it.name, it.dri, ContentKind.Classlikes, it.sourceSets().toSet()) } + + override val name: String = "All Classes" + override val dri: Set<DRI> = setOf(DRI.topLevel) + + override val documentable: Documentable? = null + override val embeddedResources: List<String> = emptyList() + + override val content: ContentNode = + EmptyNode( + DRI.topLevel, + ContentKind.Classlikes, + classes.flatMap { it.sourceSets() }.toSet() + ) + + override fun modified( + name: String, + content: ContentNode, + dri: Set<DRI>, + embeddedResources: List<String>, + children: List<PageNode> + ): ContentPage = TODO() + + override fun modified(name: String, children: List<PageNode>): PageNode = + TODO() + + override val children: List<PageNode> = emptyList() + +} + +class TreeViewPage( + override val name: String, + val packages: List<JavadocPackagePageNode>?, + val classes: List<JavadocClasslikePageNode>?, + override val dri: Set<DRI>, + override val documentable: Documentable?, + val root: PageNode +) : JavadocPageNode { + init { + assert(packages == null || classes == null) + assert(packages != null || classes != null) + } + + private val documentables = root.children.filterIsInstance<ContentPage>().flatMap { node -> + getDocumentableEntries(node) + }.groupBy({ it.first }) { it.second }.map { (l, r) -> l to r.first() }.toMap() + + private val descriptorMap = getDescriptorMap() + private val inheritanceTuple = generateInheritanceTree() + internal val classGraph = inheritanceTuple.first + internal val interfaceGraph = inheritanceTuple.second + + override val children: List<PageNode> = emptyList() + + val title = when (documentable) { + is DPackage -> "$name Class Hierarchy" + else -> "All packages" + } + + val kind = when (documentable) { + is DPackage -> "package" + else -> "main" + } + + override fun modified( + name: String, + content: ContentNode, + dri: Set<DRI>, + embeddedResources: List<String>, + children: List<PageNode> + ): ContentPage = + TreeViewPage( + name, + packages = children.filterIsInstance<JavadocPackagePageNode>().takeIf { it.isNotEmpty() }, + classes = children.filterIsInstance<JavadocClasslikePageNode>().takeIf { it.isNotEmpty() }, + dri = dri, + documentable = documentable, + root = root + ) + + override fun modified(name: String, children: List<PageNode>): PageNode = + TreeViewPage( + name, + packages = children.filterIsInstance<JavadocPackagePageNode>().takeIf { it.isNotEmpty() }, + classes = children.filterIsInstance<JavadocClasslikePageNode>().takeIf { it.isNotEmpty() }, + dri = dri, + documentable = documentable, + root = root + ) + + override val embeddedResources: List<String> = emptyList() + + override val content: ContentNode = EmptyNode( + DRI.topLevel, + ContentKind.Classlikes, + emptySet() + ) + + private fun generateInheritanceTree(): Pair<List<InheritanceNode>, List<InheritanceNode>> { + val mergeMap = mutableMapOf<DRI, InheritanceNode>() + + fun addToMap(info: InheritanceNode, map: MutableMap<DRI, InheritanceNode>) { + if (map.containsKey(info.dri)) + map.computeIfPresent(info.dri) { _, info2 -> + info.copy(children = (info.children + info2.children).distinct()) + }!!.children.forEach { addToMap(it, map) } + else + map[info.dri] = info + } + + fun collect(dri: DRI): InheritanceNode = + InheritanceNode( + dri, + mergeMap[dri]?.children.orEmpty().map { collect(it.dri) }, + mergeMap[dri]?.interfaces.orEmpty(), + mergeMap[dri]?.isInterface ?: false + ) + + fun classTreeRec(node: InheritanceNode): List<InheritanceNode> = if (node.isInterface) { + node.children.flatMap(::classTreeRec) + } else { + listOf(node.copy(children = node.children.flatMap(::classTreeRec))) + } + + fun classTree(node: InheritanceNode) = classTreeRec(node).singleOrNull() + + fun interfaceTreeRec(node: InheritanceNode): List<InheritanceNode> = if (node.isInterface) { + listOf(node.copy(children = node.children.filter { it.isInterface })) + } else { + node.children.flatMap(::interfaceTreeRec) + } + + fun interfaceTree(node: InheritanceNode) = interfaceTreeRec(node).firstOrNull() // TODO.single() + + fun gatherPsiClasses(psi: PsiClass): List<Pair<PsiClass, List<PsiClass>>> = psi.supers.toList().let { l -> + listOf(psi to l) + l.flatMap { gatherPsiClasses(it) } + } + + val psiInheritanceTree = documentables.flatMap { (_, v) -> (v as? WithExpectActual)?.sources?.values.orEmpty() } + .filterIsInstance<PsiDocumentableSource>().mapNotNull { it.psi as? PsiClass }.flatMap(::gatherPsiClasses) + .flatMap { entry -> entry.second.map { it to entry.first } } + .let { + it + it.map { it.second to null } + } + .groupBy({ it.first }) { it.second } + .map { it.key to it.value.filterNotNull().distinct() } + .map { (k, v) -> + InheritanceNode( + DRI.from(k), + v.map { InheritanceNode(DRI.from(it)) }, + k.supers.filter { it.isInterface }.map { DRI.from(it) }, + k.isInterface + ) + + } + + val descriptorInheritanceTree = descriptorMap.flatMap { (_, v) -> + v.typeConstructor.supertypes + .map { getClassDescriptorForType(it) to v } + } + .let { + it + it.map { it.second to null } + } + .groupBy({ it.first }) { it.second } + .map { it.key to it.value.filterNotNull().distinct() } + .map { (k, v) -> + InheritanceNode( + DRI.from(k), + v.map { InheritanceNode(DRI.from(it)) }, + k.typeConstructor.supertypes.map { getClassDescriptorForType(it) } + .mapNotNull { cd -> cd.takeIf { it.kind == ClassKind.INTERFACE }?.let { DRI.from(it) } }, + isInterface = k.kind == ClassKind.INTERFACE + ) + } + + descriptorInheritanceTree.forEach { addToMap(it, mergeMap) } + psiInheritanceTree.forEach { addToMap(it, mergeMap) } + + val rootNodes = mergeMap.entries.filter { + it.key.classNames in setOf("Any", "Object") //TODO: Probably should be matched by DRI, not just className + }.map { + collect(it.value.dri) + } + + return rootNodes.let { Pair(it.mapNotNull(::classTree), it.mapNotNull(::interfaceTree)) } + } + + private fun generateInterfaceGraph() { + documentables.values.filterIsInstance<DInterface>() + } + + private fun getDocumentableEntries(node: ContentPage): List<Pair<DRI, Documentable>> = + listOfNotNull(node.documentable?.let { it.dri to it }) + + node.children.filterIsInstance<ContentPage>().flatMap(::getDocumentableEntries) + + private fun getDescriptorMap(): Map<DRI, ClassDescriptor> { + val map: MutableMap<DRI, ClassDescriptor> = mutableMapOf() + documentables + .mapNotNull { (k, v) -> + v.descriptorForPlatform()?.let { k to it }?.also { (k, v) -> map[k] = v } + }.map { it.second }.forEach { gatherSupertypes(it, map) } + + return map.toMap() + } + + private fun gatherSupertypes(descriptor: ClassDescriptor, map: MutableMap<DRI, ClassDescriptor>) { + map.putIfAbsent(DRI.from(descriptor), descriptor) + descriptor.typeConstructor.supertypes.map { getClassDescriptorForType(it) } + .forEach { gatherSupertypes(it, map) } + } + + private fun Documentable?.descriptorForPlatform(platform: Platform = Platform.jvm) = + (this as? WithExpectActual).descriptorForPlatform(platform) + + private fun WithExpectActual?.descriptorForPlatform(platform: Platform = Platform.jvm) = this?.let { + it.sources.entries.find { it.key.analysisPlatform == platform }?.value?.let { it as? DescriptorDocumentableSource }?.descriptor as? ClassDescriptor + } + + data class InheritanceNode( + val dri: DRI, + val children: List<InheritanceNode> = emptyList(), + val interfaces: List<DRI> = emptyList(), + val isInterface: Boolean = false + ) { + override fun equals(other: Any?): Boolean = other is InheritanceNode && other.dri == dri + override fun hashCode(): Int = dri.hashCode() + } +} + +private fun Documentable.kind(): String? = + when (this) { + is DClass -> "class" + is DEnum -> "enum" + is DAnnotation -> "annotation" + is DObject -> "object" + is DInterface -> "interface" + else -> null + }
\ No newline at end of file diff --git a/plugins/javadoc/src/main/kotlin/javadoc/pages/htmlPreprocessors.kt b/plugins/javadoc/src/main/kotlin/javadoc/pages/htmlPreprocessors.kt new file mode 100644 index 00000000..18096ad4 --- /dev/null +++ b/plugins/javadoc/src/main/kotlin/javadoc/pages/htmlPreprocessors.kt @@ -0,0 +1,68 @@ +package javadoc.pages + +import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.transformers.pages.PageTransformer + +val preprocessors = listOf(ResourcesInstaller, TreeViewInstaller, AllClassesPageInstaller) + +object ResourcesInstaller : PageTransformer { + override fun invoke(input: RootPageNode): RootPageNode = input.modified( + children = input.children + + RendererSpecificResourcePage( + "resourcePack", + emptyList(), + RenderingStrategy.Copy("/static_res") + ) + ) +} + +object TreeViewInstaller : PageTransformer { + override fun invoke(input: RootPageNode): RootPageNode = install(input, input) as RootPageNode + + private fun install(node: PageNode, root: RootPageNode): PageNode = when (node) { + is JavadocModulePageNode -> installOverviewTreeNode(node, root) + is JavadocPackagePageNode -> installPackageTreeNode(node, root) + else -> node + } + + private fun installOverviewTreeNode(node: JavadocModulePageNode, root: RootPageNode): JavadocModulePageNode { + val overviewTree = TreeViewPage( + name = "Class Hierarchy", + packages = node.children<JavadocPackagePageNode>().map { installPackageTreeNode(it, root) }, + classes = null, + dri = node.dri, + documentable = node.documentable, + root = root + ) + + return node.modified(children = node.children.map { node -> + install( + node, + root + ) + } + overviewTree) as JavadocModulePageNode + } + + private fun installPackageTreeNode(node: JavadocPackagePageNode, root: RootPageNode): JavadocPackagePageNode { + val packageTree = TreeViewPage( + name = "${node.name}", + packages = null, + classes = node.children.filterIsInstance<JavadocClasslikePageNode>(), + dri = node.dri, + documentable = node.documentable, + root = root + ) + + return node.modified(children = node.children + packageTree) as JavadocPackagePageNode + } +} + +object AllClassesPageInstaller : PageTransformer { + override fun invoke(input: RootPageNode): RootPageNode { + val classes = (input as JavadocModulePageNode).children.filterIsInstance<JavadocPackagePageNode>().flatMap { + it.children.filterIsInstance<JavadocClasslikePageNode>() + } + + return input.modified(children = input.children + AllClassesPage(classes)) + } +}
\ No newline at end of file diff --git a/plugins/javadoc/src/main/kotlin/javadoc/pages/pages.kt b/plugins/javadoc/src/main/kotlin/javadoc/pages/pages.kt new file mode 100644 index 00000000..0486369a --- /dev/null +++ b/plugins/javadoc/src/main/kotlin/javadoc/pages/pages.kt @@ -0,0 +1,17 @@ +package javadoc.pages + +internal const val jQueryVersion = "3.3.1" +internal const val jQueryMigrateVersion = "3.0.1" + +//class PackageSummary(val page: PageNode) : RendererSpecificPage { +// override val name = "package-summary" +// override val children = emptyList<PageNode>() +// override fun modified(name: String, children: List<PageNode>) = this +// +// override val strategy = RenderingStrategy.Write(content()) +// +// private fun content(): String = pageStart(page.name, "0.0.1", page.name, "../") + // TODO +// topNavbar(page, "???") +// +//} + diff --git a/plugins/javadoc/src/main/kotlin/javadoc/renderer/JavadocContentToHtmlTranslator.kt b/plugins/javadoc/src/main/kotlin/javadoc/renderer/JavadocContentToHtmlTranslator.kt new file mode 100644 index 00000000..ceb6f9ad --- /dev/null +++ b/plugins/javadoc/src/main/kotlin/javadoc/renderer/JavadocContentToHtmlTranslator.kt @@ -0,0 +1,63 @@ +package javadoc.renderer + +import javadoc.location.JavadocLocationProvider +import javadoc.pages.JavadocSignatureContentNode +import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.utilities.formatToEndWithHtml +import org.jetbrains.dokka.utilities.htmlEscape + +internal class JavadocContentToHtmlTranslator( + private val locationProvider: JavadocLocationProvider, + private val context: DokkaContext +) { + + fun htmlForContentNode(node: ContentNode, relative: PageNode?): String = + when (node) { + is ContentGroup -> htmlForContentNodes(node.children, node.style, relative) + is ContentText -> buildText(node) + is ContentDRILink -> buildLink( + locationProvider.resolve(node.address, node.sourceSets, relative), + htmlForContentNodes(node.children, node.style, relative) + ) + is ContentResolvedLink -> buildLink(node.address, htmlForContentNodes(node.children, node.style, relative)) + is ContentCode -> htmlForCode(node.children) + is JavadocSignatureContentNode -> htmlForSignature(node, relative) + else -> "" + } + + fun htmlForContentNodes(list: List<ContentNode>, styles: Set<Style>, relative: PageNode?) = + list.joinToString(separator = "") { htmlForContentNode(it, relative) } + + private fun buildText(node: ContentText): String { + val escapedText = node.text.htmlEscape() + return if (node.style.contains(ContentStyle.InDocumentationAnchor)) { + """<em><a id="$escapedText" class="searchTagResult">${escapedText}</a></em>""" + } else { + escapedText + } + } + + private fun htmlForCode(code: List<ContentNode>): String = code.map { element -> + when (element) { + is ContentText -> element.text + is ContentBreakLine -> "" + else -> run { context.logger.error("Cannot cast $element as ContentText!"); "" } + } + }.joinToString("<br>", """<span class="code">""", "</span>") { it } + + private fun htmlForSignature(node: JavadocSignatureContentNode, relative: PageNode?): String = + listOfNotNull( + node.annotations, + node.modifiers, + node.signatureWithoutModifiers, + node.supertypes + ).joinToString(separator = " ") { htmlForContentNode(it, relative) } + + companion object { + + fun buildLink(address: String, content: String) = + """<a href=${address.formatToEndWithHtml()}>$content</a>""" + + } +}
\ No newline at end of file diff --git a/plugins/javadoc/src/main/kotlin/javadoc/renderer/JavadocContentToTemplateMapTranslator.kt b/plugins/javadoc/src/main/kotlin/javadoc/renderer/JavadocContentToTemplateMapTranslator.kt new file mode 100644 index 00000000..f59fd27e --- /dev/null +++ b/plugins/javadoc/src/main/kotlin/javadoc/renderer/JavadocContentToTemplateMapTranslator.kt @@ -0,0 +1,222 @@ +package javadoc.renderer + +import javadoc.location.JavadocLocationProvider +import javadoc.pages.* +import javadoc.toNormalized +import org.jetbrains.dokka.Platform +import org.jetbrains.dokka.base.resolvers.local.LocationProvider +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.links.sureClassNames +import org.jetbrains.dokka.model.ImplementedInterfaces +import org.jetbrains.dokka.model.InheritedFunction +import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.plugability.DokkaContext +import java.nio.file.Paths + +internal class JavadocContentToTemplateMapTranslator( + private val locationProvider: JavadocLocationProvider, + private val context: DokkaContext, +) { + + fun templateMapForPageNode(node: JavadocPageNode): TemplateMap = + mapOf<String, Any?>( + "docName" to "docName", // todo docname + "pathToRoot" to pathToRoot(node), + "contextRoot" to node, + "kind" to "main", + ) + templateMapForNode(node) + + + private fun templateMapForNode(node: JavadocPageNode): TemplateMap = + when (node) { + is JavadocModulePageNode -> InnerTranslator(node).templateMapForJavadocContentNode(node.content) + is JavadocClasslikePageNode -> InnerTranslator(node).templateMapForClasslikeNode(node) + is JavadocPackagePageNode -> InnerTranslator(node).templateMapForPackagePageNode(node) + is TreeViewPage -> InnerTranslator(node).templateMapForTreeViewPage(node) + is AllClassesPage -> InnerTranslator(node).templateMapForAllClassesPage(node) + else -> emptyMap() + } + + private fun pathToRoot(node: JavadocPageNode): String { + return when(node){ + is JavadocModulePageNode -> "" + else -> run { + val link = locationProvider.resolve(node, skipExtension = true) + val dir = Paths.get(link).parent?.toNormalized().orEmpty() + return dir.split("/").filter { it.isNotEmpty() }.joinToString("/") { ".." }.let { + if (it.isNotEmpty()) "$it/" else it + } + } + } + } + + private inner class InnerTranslator(val contextNode: PageNode) { + + private val htmlTranslator = JavadocContentToHtmlTranslator(locationProvider, context) + + fun templateMapForAllClassesPage(node: AllClassesPage): TemplateMap { + return mapOf( + "title" to "All Classes", + "list" to node.classEntries + ) + } + + fun templateMapForTreeViewPage(node: TreeViewPage): TemplateMap { + return mapOf( + "title" to node.title, + "name" to node.name, + "kind" to node.kind, + "list" to node.packages.orEmpty() + node.classes.orEmpty(), + "classGraph" to node.classGraph, + "interfaceGraph" to node.interfaceGraph + ) + } + + fun templateMapForPackagePageNode(node: JavadocPackagePageNode): TemplateMap { + return mapOf( + "kind" to "package" + ) + templateMapForJavadocContentNode(node.content) + } + + fun templateMapForFunctionNode(node: JavadocFunctionNode): TemplateMap { + return mapOf( + "brief" to htmlForContentNodes(node.brief, contextNode), + "parameters" to node.parameters.map { templateMapForParameterNode(it) }, + "inlineParameters" to node.parameters.joinToString { renderInlineParameter(it) }, + "anchorLink" to locationProvider.anchorForFunctionNode(node), + "signature" to templateMapForSignatureNode(node.signature), + "name" to node.name + ) + } + + fun templateMapForClasslikeNode(node: JavadocClasslikePageNode): TemplateMap = + mapOf( + "constructors" to node.constructors.map { templateMapForFunctionNode(it) }, + "signature" to templateMapForSignatureNode(node.signature), + "methods" to templateMapForClasslikeMethods(node.methods), + "classlikeDocumentation" to htmlForContentNodes(node.description, node), + "entries" to node.entries.map { templateMapForEntryNode(it) }, + "properties" to node.properties.map { templateMapForPropertyNode(it) }, + "classlikes" to node.classlikes.map { templateMapForNestedClasslikeNode(it) }, + "implementedInterfaces" to templateMapForImplementedInterfaces(node).sorted(), + "kind" to node.kind, + "packageName" to node.packageName, + "name" to node.name + ) + templateMapForJavadocContentNode(node.content) + + fun templateMapForSignatureNode(node: JavadocSignatureContentNode): TemplateMap = + mapOf( + "annotations" to node.annotations?.let { htmlForContentNode(it, contextNode) }, + "signatureWithoutModifiers" to htmlForContentNode(node.signatureWithoutModifiers, contextNode), + "modifiers" to node.modifiers?.let { htmlForContentNode(it, contextNode) }, + "supertypes" to node.supertypes?.let { htmlForContentNode(it, contextNode) } + ) + + fun templateMapForJavadocContentNode(node: JavadocContentNode): TemplateMap = + when (node) { + is TitleNode -> templateMapForTitleNode(node) + is JavadocContentGroup -> templateMapForJavadocContentGroup(node) + is LeafListNode -> templateMapForLeafListNode(node) + is RootListNode -> templateMapForRootListNode(node) + else -> emptyMap() + } + + private fun templateMapForParameterNode(node: JavadocParameterNode): TemplateMap = + mapOf( + "description" to htmlForContentNodes(node.description, contextNode), + "name" to node.name, + "type" to htmlForContentNode(node.type, contextNode) + ) + + private fun templateMapForImplementedInterfaces(node: JavadocClasslikePageNode) = + node.extra[ImplementedInterfaces]?.interfaces?.entries?.firstOrNull { it.key.analysisPlatform == Platform.jvm }?.value?.map { it.displayable() } // TODO: REMOVE HARDCODED JVM DEPENDENCY + .orEmpty() + + private fun templateMapForClasslikeMethods(nodes: List<JavadocFunctionNode>): TemplateMap { + val (inherited, own) = nodes.partition { it.isInherited } + return mapOf( + "own" to own.map { templateMapForFunctionNode(it) }, + "inherited" to inherited.map { templateMapForInheritedMethod(it) } + .groupBy { it["inheritedFrom"] as String }.entries.map { + mapOf( + "inheritedFrom" to it.key, + "names" to it.value.map { it["name"] as String }.sorted().joinToString() + ) + } + ) + } + + private fun templateMapForInheritedMethod(node: JavadocFunctionNode): TemplateMap { + val inheritedFrom = node.extra[InheritedFunction]?.inheritedFrom + return mapOf( + "inheritedFrom" to inheritedFrom?.entries?.firstOrNull { it.key.analysisPlatform == Platform.jvm }?.value?.displayable() // TODO: REMOVE HARDCODED JVM DEPENDENCY + .orEmpty(), + "name" to node.name + ) + } + + private fun templateMapForNestedClasslikeNode(node: JavadocClasslikePageNode): TemplateMap { + return mapOf( + "modifiers" to node.signature.modifiers?.let { htmlForContentNode(it, contextNode) }, + "signature" to node.name, + "description" to htmlForContentNodes(node.description, node) + ) + } + + private fun templateMapForPropertyNode(node: JavadocPropertyNode): TemplateMap { + return mapOf( + "modifiers" to node.signature.modifiers?.let { htmlForContentNode(it, contextNode) }, + "signature" to htmlForContentNode(node.signature.signatureWithoutModifiers, contextNode), + "description" to htmlForContentNodes(node.brief, contextNode) + ) + } + + private fun templateMapForEntryNode(node: JavadocEntryNode): TemplateMap { + return mapOf( + "signature" to templateMapForSignatureNode(node.signature), + "brief" to htmlForContentNodes(node.brief, contextNode) + ) + } + + private fun templateMapForTitleNode(node: TitleNode): TemplateMap { + return mapOf( + "title" to node.title, + "subtitle" to htmlForContentNodes(node.subtitle, contextNode), + "version" to node.version, + "packageName" to node.parent + ) + } + + private fun templateMapForJavadocContentGroup(note: JavadocContentGroup): TemplateMap { + return note.children.fold(emptyMap()) { map, child -> + map + templateMapForJavadocContentNode(child) + } + } + + private fun templateMapForLeafListNode(node: LeafListNode): TemplateMap { + return mapOf( + "tabTitle" to node.tabTitle, + "colTitle" to node.colTitle, + "list" to node.entries + ) + } + + private fun templateMapForRootListNode(node: RootListNode): TemplateMap { + return mapOf( + "lists" to node.entries.map { templateMapForLeafListNode(it) } + ) + } + + private fun renderInlineParameter(parameter: JavadocParameterNode): String = + htmlForContentNode(parameter.type, contextNode) + " ${parameter.name}" + + private fun htmlForContentNode(node: ContentNode, relativeNode: PageNode) = + htmlTranslator.htmlForContentNode(node, relativeNode) + + private fun htmlForContentNodes(nodes: List<ContentNode>, relativeNode: PageNode) = + htmlTranslator.htmlForContentNodes(nodes, emptySet(), relativeNode) + } + + private fun DRI.displayable(): String = "${packageName}.${sureClassNames}" +} + diff --git a/plugins/javadoc/src/main/kotlin/javadoc/renderer/KorteJavadocRenderer.kt b/plugins/javadoc/src/main/kotlin/javadoc/renderer/KorteJavadocRenderer.kt new file mode 100644 index 00000000..80d5a2b7 --- /dev/null +++ b/plugins/javadoc/src/main/kotlin/javadoc/renderer/KorteJavadocRenderer.kt @@ -0,0 +1,190 @@ +package javadoc.renderer + +import com.soywiz.korte.* +import javadoc.location.JavadocLocationProvider +import javadoc.pages.* +import javadoc.renderer.JavadocContentToHtmlTranslator.Companion.buildLink +import javadoc.toNormalized +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking +import org.jetbrains.dokka.base.renderers.OutputWriter +import org.jetbrains.dokka.javadoc.JavadocPlugin +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.plugin +import org.jetbrains.dokka.plugability.querySingle +import org.jetbrains.dokka.renderers.Renderer +import org.jetbrains.kotlin.utils.addToStdlib.safeAs +import java.nio.file.Path +import java.nio.file.Paths +import java.time.LocalDate + +typealias TemplateMap = Map<String, Any?> + +class KorteJavadocRenderer(private val outputWriter: OutputWriter, val context: DokkaContext, resourceDir: String) : + Renderer { + private lateinit var locationProvider: JavadocLocationProvider + + private val contentToHtmlTranslator by lazy { + JavadocContentToHtmlTranslator(locationProvider, context) + } + + private val contentToTemplateMapTranslator by lazy { + JavadocContentToTemplateMapTranslator(locationProvider, context) + } + + override fun render(root: RootPageNode) = root.let { preprocessors.fold(root) { r, t -> t.invoke(r) } }.let { newRoot -> + locationProvider = context.plugin<JavadocPlugin>().querySingle { locationProviderFactory }.getLocationProvider(newRoot) + runBlocking(Dispatchers.IO) { + renderModulePageNode(newRoot as JavadocModulePageNode) + SearchScriptsCreator(locationProvider).invoke(newRoot).forEach { renderSpecificPage(it, "") } + } + } + + private fun templateForNode(node: JavadocPageNode) = when (node) { + is JavadocModulePageNode, + is JavadocPackagePageNode -> "tabPage.korte" + is JavadocClasslikePageNode -> "class.korte" + is AllClassesPage -> "listPage.korte" + is TreeViewPage -> "treePage.korte" + else -> "" + } + + private fun CoroutineScope.renderNode(node: PageNode, path: String = "") { + if (node is JavadocPageNode) { + renderJavadocPageNode(node) + } else if (node is RendererSpecificPage) { + renderSpecificPage(node, path) + } + } + + private fun CoroutineScope.renderModulePageNode(node: JavadocModulePageNode) { + val link = "." + val name = "index" + + val contentMap = contentToTemplateMapTranslator.templateMapForPageNode(node) + + writeFromTemplate(outputWriter, "$link/$name".toNormalized(), "tabPage.korte", contentMap.toList()) + node.children.forEach { renderNode(it, link) } + } + + private fun CoroutineScope.renderJavadocPageNode(node: JavadocPageNode) { + val link = locationProvider.resolve(node, skipExtension = true) + val contentMap = contentToTemplateMapTranslator.templateMapForPageNode(node) + writeFromTemplate(outputWriter, link, templateForNode(node), contentMap.toList()) + node.children.forEach { renderNode(it, link.toNormalized()) } + } + + private fun CoroutineScope.renderSpecificPage(node: RendererSpecificPage, path: String) = launch { + when (val strategy = node.strategy) { + is RenderingStrategy.Copy -> outputWriter.writeResources(strategy.from, "") + is RenderingStrategy.Write -> outputWriter.writeHtml(node.name, strategy.text) + is RenderingStrategy.Callback -> outputWriter.writeResources( + path, + strategy.instructions(this@KorteJavadocRenderer, node) + ) + RenderingStrategy.DoNothing -> Unit + } + } + + private fun Pair<String, String>.pairToTag() = + """<th class="colFirst" scope="row">${first}</th><td class="colLast">${second}</td>""" + + private fun DRI.toLink(context: PageNode? = null) = locationProvider.resolve(this, emptySet(), context) + + private suspend fun OutputWriter.writeHtml(path: String, text: String) = write(path, text, "") + private fun CoroutineScope.writeFromTemplate( + writer: OutputWriter, + path: String, + template: String, + args: List<Pair<String, *>> + ) = launch { + val tmp = templateRenderer.render(template, *(args.toTypedArray())) + writer.writeHtml("$path.html", tmp) + } + + private fun getTemplateConfig() = TemplateConfig().also { config -> + listOf( + TeFunction("curDate") { LocalDate.now() }, + TeFunction("jQueryVersion") { "3.1" }, + TeFunction("jQueryMigrateVersion") { "1.2.1" }, + TeFunction("rowColor") { args -> if ((args.first() as Int) % 2 == 0) "altColor" else "rowColor" }, + TeFunction("h1Title") { args -> if ((args.first() as? String) == "package") "title=\"Package\" " else "" }, + TeFunction("createTabRow") { args -> + val (link, doc) = args.first() as RowJavadocListEntry + val contextRoot = args[1] as PageNode? + (buildLink( + locationProvider.resolve(link, contextRoot), + link.name + ) to contentToHtmlTranslator.htmlForContentNodes(doc, emptySet(), contextRoot)).pairToTag().trim() + }, + TeFunction("createListRow") { args -> + val link = args.first() as LinkJavadocListEntry + val contextRoot = args[1] as PageNode? + buildLink( + locationProvider.resolve(link, contextRoot), + link.name + ) + }, + TeFunction("createPackageHierarchy") { args -> + val list = args.first() as List<JavadocPackagePageNode> + list.mapIndexed { i, p -> + val content = if (i + 1 == list.size) "" else ", " + val name = p.name + "<li><a href=\"$name/package-tree.html\">$name</a>$content</li>" + }.joinToString("\n") + }, + TeFunction("renderInheritanceGraph") { args -> + val rootNodes = args.first() as List<TreeViewPage.InheritanceNode> + + fun drawRec(node: TreeViewPage.InheritanceNode): String = + "<li class=\"circle\">" + node.dri.let { dri -> + listOfNotNull( + dri.packageName, + dri.classNames + ).joinToString(".") + node.interfaces.takeUnless { node.isInterface || it.isEmpty() } + ?.let { + " implements " + it.joinToString(", ") { n -> + listOfNotNull( + n.packageName, + buildLink(n.toLink(), n.classNames.orEmpty()) + ).joinToString(".") + } + }.orEmpty() + } + node.children.filterNot { it.isInterface }.takeUnless { it.isEmpty() }?.let { + "<ul>" + it.joinToString("\n", transform = ::drawRec) + "</ul>" + }.orEmpty() + "</li>" + + rootNodes.joinToString { drawRec(it) } + }, + Filter("length") { subject.dynamicLength() }, + TeFunction("hasAnyDescription") { args -> + args.first().safeAs<List<HashMap<String, String>>>() + ?.any { it["description"]?.trim()?.isNotEmpty() ?: false } + } + ).forEach { + when (it) { + is TeFunction -> config.register(it) + is Filter -> config.register(it) + is Tag -> config.register(it) + } + } + } + + private val config = getTemplateConfig() + private val templateRenderer = Templates( + ResourceTemplateProvider( + resourceDir + ), config = config, cache = true + ) + + private class ResourceTemplateProvider(val basePath: String) : TemplateProvider { + override suspend fun get(template: String): String = + javaClass.classLoader.getResourceAsStream("$basePath/$template")?.bufferedReader()?.lines()?.toArray() + ?.joinToString("\n") ?: throw IllegalStateException("Template not found: $basePath/$template") + } + +} diff --git a/plugins/javadoc/src/main/kotlin/javadoc/renderer/SearchScriptsCreator.kt b/plugins/javadoc/src/main/kotlin/javadoc/renderer/SearchScriptsCreator.kt new file mode 100644 index 00000000..28b88909 --- /dev/null +++ b/plugins/javadoc/src/main/kotlin/javadoc/renderer/SearchScriptsCreator.kt @@ -0,0 +1,261 @@ +package javadoc.renderer + +import javadoc.location.JavadocLocationProvider +import javadoc.pages.* +import javadoc.renderer.SearchRecord.Companion.allTypes +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.base.renderers.sourceSets +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.Documentable +import org.jetbrains.dokka.model.InheritedFunction +import org.jetbrains.dokka.model.doc.Index +import org.jetbrains.dokka.model.properties.WithExtraProperties +import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.utilities.formatToEndWithHtml +import org.jetbrains.dokka.utilities.htmlEscape +import java.lang.StringBuilder + +class SearchScriptsCreator(private val locationProvider: JavadocLocationProvider) { + + fun invoke(input: RootPageNode): List<RendererSpecificPage> { + val data = when (input) { + is JavadocModulePageNode -> processModules(listOf(input)) + else -> SearchData() + } + val serializer = SearchRecordJsonSerializer() + + val modules = RendererSpecificResourcePage( + name = "module-search-index.js", + children = emptyList(), + strategy = RenderingStrategy.Write(serializer.serialize(data.moduleRecords, "moduleSearchIndex")) + ) + + val packages = RendererSpecificResourcePage( + name = "package-search-index.js", + children = emptyList(), + strategy = RenderingStrategy.Write(serializer.serialize(data.packageRecords, "packageSearchIndex")) + ) + + val types = RendererSpecificResourcePage( + name = "type-search-index.js", + children = emptyList(), + strategy = RenderingStrategy.Write(serializer.serialize(data.typeRecords, "typeSearchIndex")) + ) + + val members = RendererSpecificResourcePage( + name = "member-search-index.js", + children = emptyList(), + strategy = RenderingStrategy.Write(serializer.serialize(data.memberRecords, "memberSearchIndex")) + ) + + val indexes = RendererSpecificResourcePage( + name = "tag-search-index.js", + children = emptyList(), + strategy = RenderingStrategy.Write(serializer.serialize(data.searchIndexes, "tagSearchIndex")) + ) + + return listOf(modules, packages, types, members, indexes) + } + + private fun processModules(input: List<JavadocModulePageNode>): SearchData { + val modules = SearchData(moduleRecords = input.map { SearchRecord(l = it.name, url = locationProvider.resolve(it).formatToEndWithHtml()) }) + val processablePackages = input.flatMap { it.children.filterIsInstance<JavadocPackagePageNode>() } + return processPackages(processablePackages, modules) + } + + private fun processPackages(input: List<JavadocPackagePageNode>, accumulator: SearchData): SearchData { + val packages = input.map { SearchRecord(l = it.name, url = locationProvider.resolve(it).formatToEndWithHtml()) } + SearchRecord.allPackages + val types = input.flatMap { + it.children.filterIsInstance<JavadocClasslikePageNode>().map { classlike -> it to classlike } + } + val updated = accumulator.copy(packageRecords = packages) + return processTypes(types, updated) + } + + private fun processTypes( + input: List<Pair<JavadocPackagePageNode, JavadocClasslikePageNode>>, + accumulator: SearchData + ): SearchData { + val types = input.map { + SearchRecord( + p = it.first.name, + l = it.second.name, + url = locationProvider.resolve(it.second).formatToEndWithHtml() + ) + } + allTypes + val updated = accumulator.copy(typeRecords = types) + return processMembers(input, updated).copy(searchIndexes = indexSearchForClasslike(input)) + } + + private fun processMembers( + input: List<Pair<JavadocPackagePageNode, JavadocClasslikePageNode>>, + accumulator: SearchData + ): SearchData { + val functions = input.flatMap { + (it.second.constructors + it.second.methods).withoutInherited().map { function -> + SearchRecordCreator.function( + packageName = it.first.name, + classlikeName = it.second.name, + input = function, + url = locationProvider.resolve(function.dri, it.first.sourceSets()) + ) + } + } + + val properties = input.flatMap { + it.second.properties.map { property -> + SearchRecordCreator.property( + packageName = it.first.name, + classlikeName = it.second.name, + property, + locationProvider.resolve(property.dri, it.first.sourceSets()) + ) + } + } + + val entries = input.flatMap { + it.second.entries.map { entry -> + SearchRecordCreator.entry( + packageName = it.first.name, + classlikeName = it.second.name, + entry, + locationProvider.resolve(entry.dri, it.first.sourceSets()) + ) + } + } + + return accumulator.copy(memberRecords = functions + properties + entries) + } + + private fun indexSearchForClasslike( + input: List<Pair<JavadocPackagePageNode, JavadocClasslikePageNode>>, + ): List<SearchRecord> { + val indexesForClasslike = input.flatMap { + val indexes = it.second.indexes() + indexes.map { index -> + val label = renderNode(index) + SearchRecord( + p = it.first.name, + c = it.second.name, + l = label, + url = resolveUrlForSearchIndex(it.second.dri.first(), it.second.sourceSets(), label) + ) + } + } + + val indexesForMemberNodes = input.flatMap { packageWithClasslike -> + (packageWithClasslike.second.constructors + + packageWithClasslike.second.methods.withoutInherited() + + packageWithClasslike.second.properties + + packageWithClasslike.second.entries + ).map { it to it.indexes() } + .flatMap { entryWithIndex -> + entryWithIndex.second.map { + val label = renderNode(it) + SearchRecord( + p = packageWithClasslike.first.name, + c = packageWithClasslike.second.name, + l = label, + url = resolveUrlForSearchIndex( + entryWithIndex.first.dri, + packageWithClasslike.second.sourceSets(), + label + ) + ) + } + } + } + + return indexesForClasslike + indexesForMemberNodes + } + + private fun <T : Documentable> WithJavadocExtra<T>.indexes(): List<ContentNode> = extra[JavadocIndexExtra]?.index.orEmpty() + + private fun List<JavadocFunctionNode>.withoutInherited(): List<JavadocFunctionNode> = filter { !it.isInherited } + + private fun resolveUrlForSearchIndex(dri: DRI, sourceSets: Set<DokkaConfiguration.DokkaSourceSet>, label: String): String = + locationProvider.resolve(dri, sourceSets).formatToEndWithHtml() + "#" + label +} + +private data class SearchRecord( + val p: String? = null, + val c: String? = null, + val l: String, + val url: String? = null +) { + companion object { + val allPackages = SearchRecord(l = "All packages", url = "index.html") + val allTypes = SearchRecord(l = "All classes", url = "allclasses.html") + } +} + +private object SearchRecordCreator { + fun function( + packageName: String, + classlikeName: String, + input: JavadocFunctionNode, + url: String + ): SearchRecord = + SearchRecord( + p = packageName, + c = classlikeName, + l = input.name + input.parameters.joinToString( + prefix = "(", + postfix = ")" + ) { renderNode(it.type) }, + url = url.formatToEndWithHtml() + ) + + fun property( + packageName: String, + classlikeName: String, + input: JavadocPropertyNode, + url: String + ): SearchRecord = + SearchRecord( + p = packageName, + c = classlikeName, + l = input.name, + url = url.formatToEndWithHtml() + ) + + fun entry(packageName: String, classlikeName: String, input: JavadocEntryNode, url: String): SearchRecord = + SearchRecord( + p = packageName, + c = classlikeName, + l = input.name, + url = url.formatToEndWithHtml() + ) +} + +private data class SearchData( + val moduleRecords: List<SearchRecord> = emptyList(), + val packageRecords: List<SearchRecord> = emptyList(), + val typeRecords: List<SearchRecord> = emptyList(), + val memberRecords: List<SearchRecord> = emptyList(), + val searchIndexes: List<SearchRecord> = emptyList() +) + +private class SearchRecordJsonSerializer { + fun serialize(record: SearchRecord): String { + val serialized = StringBuilder() + serialized.append("{") + with(record) { + if (p != null) serialized.append("\"p\":\"$p\",") + if (c != null) serialized.append("\"c\":\"$c\",") + serialized.append("\"l\":\"$l\"") + if (url != null) serialized.append(",\"url\":\"$url\"") + } + serialized.append("}") + return serialized.toString() + } + + fun serialize(records: List<SearchRecord>, variable: String): String = + "var " + variable + " = " + records.joinToString(prefix = "[", postfix = "]") { serialize(it) } +} + +private fun renderNode(node: ContentNode): String = + when (node) { + is ContentText -> node.text + else -> node.children.joinToString(separator = "") { renderNode(it) } + }
\ No newline at end of file diff --git a/plugins/javadoc/src/main/kotlin/javadoc/signatures/JavadocSignatureProvider.kt b/plugins/javadoc/src/main/kotlin/javadoc/signatures/JavadocSignatureProvider.kt new file mode 100644 index 00000000..f9bee318 --- /dev/null +++ b/plugins/javadoc/src/main/kotlin/javadoc/signatures/JavadocSignatureProvider.kt @@ -0,0 +1,201 @@ +package javadoc.signatures + +import javadoc.translators.documentables.JavadocPageContentBuilder +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.base.signatures.JvmSignatureUtils +import org.jetbrains.dokka.base.signatures.SignatureProvider +import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter +import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder +import org.jetbrains.dokka.kotlinAsJava.signatures.JavaSignatureUtils +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.links.sureClassNames +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.properties.PropertyContainer +import org.jetbrains.dokka.pages.ContentKind +import org.jetbrains.dokka.pages.ContentNode +import org.jetbrains.dokka.utilities.DokkaLogger + +class JavadocSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLogger) : SignatureProvider, + JvmSignatureUtils by JavaSignatureUtils { + + private val contentBuilder = JavadocPageContentBuilder(ctcc, this, logger) + + private val ignoredVisibilities = setOf(JavaVisibility.Default) + + private val ignoredModifiers = + setOf(KotlinModifier.Open, JavaModifier.Empty, KotlinModifier.Empty, KotlinModifier.Sealed) + + override fun signature(documentable: Documentable): List<ContentNode> = when (documentable) { + is DFunction -> signature(documentable) + is DProperty -> signature(documentable) + is DClasslike -> signature(documentable) + is DEnumEntry -> signature(documentable) + is DTypeParameter -> signature(documentable) + is DParameter -> signature(documentable) + else -> throw NotImplementedError( + "Cannot generate signature for ${documentable::class.qualifiedName} ${documentable.name}" + ) + } + + private fun signature(c: DClasslike): List<ContentNode> = + javadocSignature(c) { + annotations { + annotationsBlock(c) + } + modifiers { + text(c.visibility[it]?.takeIf { it !in ignoredVisibilities }?.name?.plus(" ") ?: "") + + if (c is DClass) { + text(c.modifier[it]?.takeIf { it !in ignoredModifiers }?.name?.plus(" ") ?: "") + text(c.modifiers()[it]?.toSignatureString() ?: "") + } + + when (c) { + is DClass -> text("class") + is DInterface -> text("interface") + is DEnum -> text("enum") + is DObject -> text("class") + is DAnnotation -> text("@interface") + } + } + signatureWithoutModifiers { + link(c.name!!, c.dri) + if (c is WithGenerics) { + list(c.generics, prefix = "<", suffix = ">") { + +buildSignature(it) + } + } + } + supertypes { + if (c is WithSupertypes) { + c.supertypes.map { (p, dris) -> + val (classes, interfaces) = dris.partition { it.kind == JavaClassKindTypes.CLASS } + list(classes, prefix = " extends ", sourceSets = setOf(p)) { + link(it.dri.sureClassNames, it.dri, sourceSets = setOf(p)) + } + list(interfaces, prefix = " implements ", sourceSets = setOf(p)){ + link(it.dri.sureClassNames, it.dri, sourceSets = setOf(p)) + } + } + } + } + } + + private fun signature(f: DFunction): List<ContentNode> = + javadocSignature(f) { + annotations { + annotationsBlock(f) + } + modifiers { + text(f.modifier[it]?.takeIf { it !in ignoredModifiers }?.name?.plus(" ") ?: "") + text(f.modifiers()[it]?.toSignatureString() ?: "") + list(f.generics, prefix = "<", suffix = "> ") { + +buildSignature(it) + } + signatureForProjection(f.type) + } + signatureWithoutModifiers { + link(f.name, f.dri) + text("(") + list(f.parameters) { + annotationsInline(it) + text(it.modifiers()[it]?.toSignatureString().orEmpty()) + signatureForProjection(it.type) + text(Typography.nbsp.toString()) + text(it.name.orEmpty()) + } + text(")") + } + } + + private fun signature(p: DProperty): List<ContentNode> = + javadocSignature(p) { + annotations { + annotationsBlock(p) + } + modifiers { + text(p.visibility[it]?.takeIf { it !in ignoredVisibilities }?.name?.plus(" ") ?: "") + text(p.modifier[it]?.name + " ") + text(p.modifiers()[it]?.toSignatureString() ?: "") + signatureForProjection(p.type) + } + signatureWithoutModifiers { + link(p.name, p.dri) + } + } + + private fun signature(e: DEnumEntry): List<ContentNode> = + javadocSignature(e) { + annotations { + annotationsBlock(e) + } + modifiers { + text(e.modifiers()[it]?.toSignatureString() ?: "") + } + signatureWithoutModifiers { + link(e.name, e.dri) + } + } + + private fun signature(t: DTypeParameter): List<ContentNode> = + javadocSignature(t) { + annotations { + annotationsBlock(t) + } + signatureWithoutModifiers { + text(t.name) + } + supertypes { + list(t.bounds, prefix = "extends ") { + signatureForProjection(it) + } + } + } + + private fun signature(p: DParameter): List<ContentNode> = + javadocSignature(p) { + modifiers { + signatureForProjection(p.type) + } + signatureWithoutModifiers { + link(p.name.orEmpty(), p.dri) + } + } + + private fun javadocSignature( + d: Documentable, + extra: PropertyContainer<ContentNode> = PropertyContainer.empty(), + block: JavadocPageContentBuilder.JavadocContentBuilder.(DokkaConfiguration.DokkaSourceSet) -> Unit + ): List<ContentNode> = + d.sourceSets.map { sourceSet -> + contentBuilder.contentFor(d, ContentKind.Main) { + with(contentBuilder) { + javadocGroup(d.dri, d.sourceSets, extra) { + block(sourceSet) + } + } + } + } + + private fun PageContentBuilder.DocumentableContentBuilder.signatureForProjection(p: Projection): Unit = when (p) { + is OtherParameter -> link(p.name, p.declarationDRI) + is TypeConstructor -> group { + link(p.dri.classNames.orEmpty(), p.dri) + list(p.projections, prefix = "<", suffix = ">") { + signatureForProjection(it) + } + } + is Variance -> group { + text(p.kind.toString() + " ") + signatureForProjection(p.inner) + } + is Star -> text("?") + is Nullable -> signatureForProjection(p.inner) + is JavaObject, is Dynamic -> link("Object", DRI("java.lang", "Object")) + is Void -> text("void") + is PrimitiveJavaType -> text(p.name) + is UnresolvedBound -> text(p.name) + } + + private fun DRI.fqName(): String = "${packageName.orEmpty()}.${classNames.orEmpty()}" +} diff --git a/plugins/javadoc/src/main/kotlin/javadoc/translators/documentables/JavadocPageContentBuilder.kt b/plugins/javadoc/src/main/kotlin/javadoc/translators/documentables/JavadocPageContentBuilder.kt new file mode 100644 index 00000000..17b474b0 --- /dev/null +++ b/plugins/javadoc/src/main/kotlin/javadoc/translators/documentables/JavadocPageContentBuilder.kt @@ -0,0 +1,80 @@ +package javadoc.translators.documentables + +import javadoc.pages.JavadocSignatureContentNode +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.base.signatures.SignatureProvider +import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter +import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.properties.PropertyContainer +import org.jetbrains.dokka.pages.ContentKind +import org.jetbrains.dokka.pages.ContentNode +import org.jetbrains.dokka.utilities.DokkaLogger +import java.lang.IllegalStateException + +class JavadocPageContentBuilder( + commentsConverter: CommentsToContentConverter, + signatureProvider: SignatureProvider, + logger: DokkaLogger +) : PageContentBuilder(commentsConverter, signatureProvider, logger) { + + fun PageContentBuilder.DocumentableContentBuilder.javadocGroup( + dri: DRI = mainDRI.first(), + sourceSets: Set<DokkaConfiguration.DokkaSourceSet> = mainSourcesetData, + extra: PropertyContainer<ContentNode> = mainExtra, + block: JavadocContentBuilder.() -> Unit + ) { + +JavadocContentBuilder( + mainDri = dri, + mainExtra = extra, + mainSourceSet = sourceSets, + ).apply(block).build() + } + + open inner class JavadocContentBuilder( + private val mainDri: DRI, + private val mainExtra: PropertyContainer<ContentNode>, + private val mainSourceSet: Set<DokkaConfiguration.DokkaSourceSet>, + ) { + var annotations: ContentNode? = null + var modifiers: ContentNode? = null + var signatureWithoutModifiers: ContentNode? = null + var supertypes: ContentNode? = null + + fun annotations(block: PageContentBuilder.DocumentableContentBuilder.() -> Unit) { + val built = buildContentForBlock(block) + if(built.hasAnyContent()) annotations = built + } + + fun modifiers(block: PageContentBuilder.DocumentableContentBuilder.() -> Unit) { + val built = buildContentForBlock(block) + if(built.hasAnyContent()) modifiers = built + } + + fun signatureWithoutModifiers(block: PageContentBuilder.DocumentableContentBuilder.() -> Unit) { + signatureWithoutModifiers = buildContentForBlock(block) + } + + fun supertypes(block: PageContentBuilder.DocumentableContentBuilder.() -> Unit) { + val built = buildContentForBlock(block) + if(built.hasAnyContent()) supertypes = built + } + + private fun buildContentForBlock(block: PageContentBuilder.DocumentableContentBuilder.() -> Unit) = + contentFor( + dri = mainDri, + sourceSets = mainSourceSet, + kind = ContentKind.Symbol, + extra = mainExtra, + block = block + ) + + fun build(): JavadocSignatureContentNode = JavadocSignatureContentNode( + dri = mainDri, + annotations = annotations, + modifiers = modifiers, + signatureWithoutModifiers = signatureWithoutModifiers ?: throw IllegalStateException("JavadocSignatureContentNode should have at least a signature"), + supertypes = supertypes + ) + } +}
\ No newline at end of file diff --git a/plugins/javadoc/src/main/kotlin/javadoc/utils.kt b/plugins/javadoc/src/main/kotlin/javadoc/utils.kt new file mode 100644 index 00000000..94f7c8b4 --- /dev/null +++ b/plugins/javadoc/src/main/kotlin/javadoc/utils.kt @@ -0,0 +1,8 @@ +package javadoc + +import java.nio.file.Path +import java.nio.file.Paths + +internal fun Path.toNormalized() = this.normalize().toFile().toString() + +internal fun String.toNormalized() = Paths.get(this).toNormalized() diff --git a/plugins/javadoc/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin b/plugins/javadoc/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin new file mode 100644 index 00000000..791b6696 --- /dev/null +++ b/plugins/javadoc/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin @@ -0,0 +1 @@ +org.jetbrains.dokka.javadoc.JavadocPlugin diff --git a/plugins/javadoc/src/main/resources/static_res/jquery/external/jquery/jquery.js b/plugins/javadoc/src/main/resources/static_res/jquery/external/jquery/jquery.js new file mode 100644 index 00000000..9b5206bc --- /dev/null +++ b/plugins/javadoc/src/main/resources/static_res/jquery/external/jquery/jquery.js @@ -0,0 +1,10364 @@ +/*! + * jQuery JavaScript Library v3.3.1 + * https://jquery.com/ + * + * Includes Sizzle.js + * https://sizzlejs.com/ + * + * Copyright JS Foundation and other contributors + * Released under the MIT license + * https://jquery.org/license + * + * Date: 2018-01-20T17:24Z + */ +( function( global, factory ) { + + "use strict"; + + if ( typeof module === "object" && typeof module.exports === "object" ) { + + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get jQuery. + // For environments that do not have a `window` with a `document` + // (such as Node.js), expose a factory as module.exports. + // This accentuates the need for the creation of a real `window`. + // e.g. var jQuery = require("jquery")(window); + // See ticket #14549 for more info. + module.exports = global.document ? + factory( global, true ) : + function( w ) { + if ( !w.document ) { + throw new Error( "jQuery requires a window with a document" ); + } + return factory( w ); + }; + } else { + factory( global ); + } + +// Pass this if window is not defined yet +} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { + +// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 +// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode +// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common +// enough that all such attempts are guarded in a try block. +"use strict"; + +var arr = []; + +var document = window.document; + +var getProto = Object.getPrototypeOf; + +var slice = arr.slice; + +var concat = arr.concat; + +var push = arr.push; + +var indexOf = arr.indexOf; + +var class2type = {}; + +var toString = class2type.toString; + +var hasOwn = class2type.hasOwnProperty; + +var fnToString = hasOwn.toString; + +var ObjectFunctionString = fnToString.call( Object ); + +var support = {}; + +var isFunction = function isFunction( obj ) { + + // Support: Chrome <=57, Firefox <=52 + // In some browsers, typeof returns "function" for HTML <object> elements + // (i.e., `typeof document.createElement( "object" ) === "function"`). + // We don't want to classify *any* DOM node as a function. + return typeof obj === "function" && typeof obj.nodeType !== "number"; + }; + + +var isWindow = function isWindow( obj ) { + return obj != null && obj === obj.window; + }; + + + + + var preservedScriptAttributes = { + type: true, + src: true, + noModule: true + }; + + function DOMEval( code, doc, node ) { + doc = doc || document; + + var i, + script = doc.createElement( "script" ); + + script.text = code; + if ( node ) { + for ( i in preservedScriptAttributes ) { + if ( node[ i ] ) { + script[ i ] = node[ i ]; + } + } + } + doc.head.appendChild( script ).parentNode.removeChild( script ); + } + + +function toType( obj ) { + if ( obj == null ) { + return obj + ""; + } + + // Support: Android <=2.3 only (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call( obj ) ] || "object" : + typeof obj; +} +/* global Symbol */ +// Defining this global in .eslintrc.json would create a danger of using the global +// unguarded in another place, it seems safer to define global only for this module + + + +var + version = "3.3.1", + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }, + + // Support: Android <=4.0 only + // Make sure we trim BOM and NBSP + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g; + +jQuery.fn = jQuery.prototype = { + + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + + // Return all the elements in a clean array + if ( num == null ) { + return slice.call( this ); + } + + // Return just the one element from the set + return num < 0 ? this[ num + this.length ] : this[ num ]; + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + each: function( callback ) { + return jQuery.each( this, callback ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map( this, function( elem, i ) { + return callback.call( elem, i, elem ); + } ) ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: arr.sort, + splice: arr.splice +}; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[ 0 ] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // Skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !isFunction( target ) ) { + target = {}; + } + + // Extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } + + for ( ; i < length; i++ ) { + + // Only deal with non-null/undefined values + if ( ( options = arguments[ i ] ) != null ) { + + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject( copy ) || + ( copyIsArray = Array.isArray( copy ) ) ) ) { + + if ( copyIsArray ) { + copyIsArray = false; + clone = src && Array.isArray( src ) ? src : []; + + } else { + clone = src && jQuery.isPlainObject( src ) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend( { + + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + isPlainObject: function( obj ) { + var proto, Ctor; + + // Detect obvious negatives + // Use toString instead of jQuery.type to catch host objects + if ( !obj || toString.call( obj ) !== "[object Object]" ) { + return false; + } + + proto = getProto( obj ); + + // Objects with no prototype (e.g., `Object.create( null )`) are plain + if ( !proto ) { + return true; + } + + // Objects with prototype are plain iff they were constructed by a global Object function + Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; + return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; + }, + + isEmptyObject: function( obj ) { + + /* eslint-disable no-unused-vars */ + // See https://github.com/eslint/eslint/issues/6125 + var name; + + for ( name in obj ) { + return false; + } + return true; + }, + + // Evaluates a script in a global context + globalEval: function( code ) { + DOMEval( code ); + }, + + each: function( obj, callback ) { + var length, i = 0; + + if ( isArrayLike( obj ) ) { + length = obj.length; + for ( ; i < length; i++ ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } else { + for ( i in obj ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } + + return obj; + }, + + // Support: Android <=4.0 only + trim: function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArrayLike( Object( arr ) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + return arr == null ? -1 : indexOf.call( arr, elem, i ); + }, + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; + + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var length, value, + i = 0, + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArrayLike( elems ) ) { + length = elems.length; + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +} ); + +if ( typeof Symbol === "function" ) { + jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; +} + +// Populate the class2type map +jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), +function( i, name ) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +} ); + +function isArrayLike( obj ) { + + // Support: real iOS 8.2 only (not reproducible in simulator) + // `in` check used to prevent JIT error (gh-2145) + // hasOwn isn't used here due to false negatives + // regarding Nodelist length in IE + var length = !!obj && "length" in obj && obj.length, + type = toType( obj ); + + if ( isFunction( obj ) || isWindow( obj ) ) { + return false; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +var Sizzle = +/*! + * Sizzle CSS Selector Engine v2.3.3 + * https://sizzlejs.com/ + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2016-08-08 + */ +(function( window ) { + +var i, + support, + Expr, + getText, + isXML, + tokenize, + compile, + select, + outermostContext, + sortInput, + hasDuplicate, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + 1 * new Date(), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, + + // Instance methods + hasOwn = ({}).hasOwnProperty, + arr = [], + pop = arr.pop, + push_native = arr.push, + push = arr.push, + slice = arr.slice, + // Use a stripped-down indexOf as it's faster than native + // https://jsperf.com/thor-indexof-vs-for/5 + indexOf = function( list, elem ) { + var i = 0, + len = list.length; + for ( ; i < len; i++ ) { + if ( list[i] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + + // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = "(?:\\\\.|[\\w-]|[^\0-\\xa0])+", + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + + "*\\]", + + pseudos = ":(" + identifier + ")(?:\\((" + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rwhitespace = new RegExp( whitespace + "+", "g" ), + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), + + rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + identifier + ")" ), + "CLASS": new RegExp( "^\\.(" + identifier + ")" ), + "TAG": new RegExp( "^(" + identifier + "|[*])" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + + // CSS escapes + // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), + funescape = function( _, escaped, escapedWhitespace ) { + var high = "0x" + escaped - 0x10000; + // NaN means non-codepoint + // Support: Firefox<24 + // Workaround erroneous numeric interpretation of +"0x" + return high !== high || escapedWhitespace ? + escaped : + high < 0 ? + // BMP codepoint + String.fromCharCode( high + 0x10000 ) : + // Supplemental Plane codepoint (surrogate pair) + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }, + + // CSS string/identifier serialization + // https://drafts.csswg.org/cssom/#common-serializing-idioms + rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g, + fcssescape = function( ch, asCodePoint ) { + if ( asCodePoint ) { + + // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER + if ( ch === "\0" ) { + return "\uFFFD"; + } + + // Control characters and (dependent upon position) numbers get escaped as code points + return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; + } + + // Other potentially-special ASCII characters get backslash-escaped + return "\\" + ch; + }, + + // Used for iframes + // See setDocument() + // Removing the function wrapper causes a "Permission Denied" + // error in IE + unloadHandler = function() { + setDocument(); + }, + + disabledAncestor = addCombinator( + function( elem ) { + return elem.disabled === true && ("form" in elem || "label" in elem); + }, + { dir: "parentNode", next: "legend" } + ); + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + (arr = slice.call( preferredDoc.childNodes )), + preferredDoc.childNodes + ); + // Support: Android<4.0 + // Detect silently failing push.apply + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + push_native.apply( target, slice.call(els) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + // Can't trust NodeList.length + while ( (target[j++] = els[i++]) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var m, i, elem, nid, match, groups, newSelector, + newContext = context && context.ownerDocument, + + // nodeType defaults to 9, since context defaults to document + nodeType = context ? context.nodeType : 9; + + results = results || []; + + // Return early from calls with invalid selector or context + if ( typeof selector !== "string" || !selector || + nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { + + return results; + } + + // Try to shortcut find operations (as opposed to filters) in HTML documents + if ( !seed ) { + + if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { + setDocument( context ); + } + context = context || document; + + if ( documentIsHTML ) { + + // If the selector is sufficiently simple, try using a "get*By*" DOM method + // (excepting DocumentFragment context, where the methods don't exist) + if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { + + // ID selector + if ( (m = match[1]) ) { + + // Document context + if ( nodeType === 9 ) { + if ( (elem = context.getElementById( m )) ) { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + + // Element context + } else { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( newContext && (elem = newContext.getElementById( m )) && + contains( context, elem ) && + elem.id === m ) { + + results.push( elem ); + return results; + } + } + + // Type selector + } else if ( match[2] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Class selector + } else if ( (m = match[3]) && support.getElementsByClassName && + context.getElementsByClassName ) { + + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // Take advantage of querySelectorAll + if ( support.qsa && + !compilerCache[ selector + " " ] && + (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { + + if ( nodeType !== 1 ) { + newContext = context; + newSelector = selector; + + // qSA looks outside Element context, which is not what we want + // Thanks to Andrew Dupont for this workaround technique + // Support: IE <=8 + // Exclude object elements + } else if ( context.nodeName.toLowerCase() !== "object" ) { + + // Capture the context ID, setting it first if necessary + if ( (nid = context.getAttribute( "id" )) ) { + nid = nid.replace( rcssescape, fcssescape ); + } else { + context.setAttribute( "id", (nid = expando) ); + } + + // Prefix every selector in the list + groups = tokenize( selector ); + i = groups.length; + while ( i-- ) { + groups[i] = "#" + nid + " " + toSelector( groups[i] ); + } + newSelector = groups.join( "," ); + + // Expand context for sibling selectors + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || + context; + } + + if ( newSelector ) { + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch ( qsaError ) { + } finally { + if ( nid === expando ) { + context.removeAttribute( "id" ); + } + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {function(string, object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return (cache[ key + " " ] = value); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created element and returns a boolean result + */ +function assert( fn ) { + var el = document.createElement("fieldset"); + + try { + return !!fn( el ); + } catch (e) { + return false; + } finally { + // Remove from its parent by default + if ( el.parentNode ) { + el.parentNode.removeChild( el ); + } + // release memory in IE + el = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split("|"), + i = arr.length; + + while ( i-- ) { + Expr.attrHandle[ arr[i] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + a.sourceIndex - b.sourceIndex; + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( (cur = cur.nextSibling) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for :enabled/:disabled + * @param {Boolean} disabled true for :disabled; false for :enabled + */ +function createDisabledPseudo( disabled ) { + + // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable + return function( elem ) { + + // Only certain elements can match :enabled or :disabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled + if ( "form" in elem ) { + + // Check for inherited disabledness on relevant non-disabled elements: + // * listed form-associated elements in a disabled fieldset + // https://html.spec.whatwg.org/multipage/forms.html#category-listed + // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled + // * option elements in a disabled optgroup + // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled + // All such elements have a "form" property. + if ( elem.parentNode && elem.disabled === false ) { + + // Option elements defer to a parent optgroup if present + if ( "label" in elem ) { + if ( "label" in elem.parentNode ) { + return elem.parentNode.disabled === disabled; + } else { + return elem.disabled === disabled; + } + } + + // Support: IE 6 - 11 + // Use the isDisabled shortcut property to check for disabled fieldset ancestors + return elem.isDisabled === disabled || + + // Where there is no isDisabled, check manually + /* jshint -W018 */ + elem.isDisabled !== !disabled && + disabledAncestor( elem ) === disabled; + } + + return elem.disabled === disabled; + + // Try to winnow out elements that can't be disabled before trusting the disabled property. + // Some victims get caught in our net (label, legend, menu, track), but it shouldn't + // even exist on them, let alone have a boolean value. + } else if ( "label" in elem ) { + return elem.disabled === disabled; + } + + // Remaining elements are neither :enabled nor :disabled + return false; + }; +} + +/** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ +function createPositionalPseudo( fn ) { + return markFunction(function( argument ) { + argument = +argument; + return markFunction(function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ (j = matchIndexes[i]) ] ) { + seed[j] = !(matches[j] = seed[j]); + } + } + }); + }); +} + +/** + * Checks a node for validity as a Sizzle context + * @param {Element|Object=} context + * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value + */ +function testContext( context ) { + return context && typeof context.getElementsByTagName !== "undefined" && context; +} + +// Expose support vars for convenience +support = Sizzle.support = {}; + +/** + * Detects XML nodes + * @param {Element|Object} elem An element or a document + * @returns {Boolean} True iff elem is a non-HTML XML node + */ +isXML = Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = elem && (elem.ownerDocument || elem).documentElement; + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var hasCompare, subWindow, + doc = node ? node.ownerDocument || node : preferredDoc; + + // Return early if doc is invalid or already selected + if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Update global variables + document = doc; + docElem = document.documentElement; + documentIsHTML = !isXML( document ); + + // Support: IE 9-11, Edge + // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) + if ( preferredDoc !== document && + (subWindow = document.defaultView) && subWindow.top !== subWindow ) { + + // Support: IE 11, Edge + if ( subWindow.addEventListener ) { + subWindow.addEventListener( "unload", unloadHandler, false ); + + // Support: IE 9 - 10 only + } else if ( subWindow.attachEvent ) { + subWindow.attachEvent( "onunload", unloadHandler ); + } + } + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties + // (excepting IE8 booleans) + support.attributes = assert(function( el ) { + el.className = "i"; + return !el.getAttribute("className"); + }); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert(function( el ) { + el.appendChild( document.createComment("") ); + return !el.getElementsByTagName("*").length; + }); + + // Support: IE<9 + support.getElementsByClassName = rnative.test( document.getElementsByClassName ); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programmatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert(function( el ) { + docElem.appendChild( el ).id = expando; + return !document.getElementsByName || !document.getElementsByName( expando ).length; + }); + + // ID filter and find + if ( support.getById ) { + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute("id") === attrId; + }; + }; + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var elem = context.getElementById( id ); + return elem ? [ elem ] : []; + } + }; + } else { + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== "undefined" && + elem.getAttributeNode("id"); + return node && node.value === attrId; + }; + }; + + // Support: IE 6 - 7 only + // getElementById is not reliable as a find shortcut + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var node, i, elems, + elem = context.getElementById( id ); + + if ( elem ) { + + // Verify the id attribute + node = elem.getAttributeNode("id"); + if ( node && node.value === id ) { + return [ elem ]; + } + + // Fall back on getElementsByName + elems = context.getElementsByName( id ); + i = 0; + while ( (elem = elems[i++]) ) { + node = elem.getAttributeNode("id"); + if ( node && node.value === id ) { + return [ elem ]; + } + } + } + + return []; + } + }; + } + + // Tag + Expr.find["TAG"] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( tag ); + + // DocumentFragment nodes don't have gEBTN + } else if ( support.qsa ) { + return context.querySelectorAll( tag ); + } + } : + + function( tag, context ) { + var elem, + tmp = [], + i = 0, + // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( (elem = results[i++]) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Class + Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See https://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( (support.qsa = rnative.test( document.querySelectorAll )) ) { + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert(function( el ) { + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // https://bugs.jquery.com/ticket/12359 + docElem.appendChild( el ).innerHTML = "<a id='" + expando + "'></a>" + + "<select id='" + expando + "-\r\\' msallowcapture=''>" + + "<option selected=''></option></select>"; + + // Support: IE8, Opera 11-12.16 + // Nothing should be selected when empty strings follow ^= or $= or *= + // The test attribute must be unknown in Opera but "safe" for WinRT + // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if ( el.querySelectorAll("[msallowcapture^='']").length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !el.querySelectorAll("[selected]").length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ + if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { + rbuggyQSA.push("~="); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !el.querySelectorAll(":checked").length ) { + rbuggyQSA.push(":checked"); + } + + // Support: Safari 8+, iOS 8+ + // https://bugs.webkit.org/show_bug.cgi?id=136851 + // In-page `selector#id sibling-combinator selector` fails + if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) { + rbuggyQSA.push(".#.+[+~]"); + } + }); + + assert(function( el ) { + el.innerHTML = "<a href='' disabled='disabled'></a>" + + "<select disabled='disabled'><option/></select>"; + + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + var input = document.createElement("input"); + input.setAttribute( "type", "hidden" ); + el.appendChild( input ).setAttribute( "name", "D" ); + + // Support: IE8 + // Enforce case-sensitivity of name attribute + if ( el.querySelectorAll("[name=d]").length ) { + rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( el.querySelectorAll(":enabled").length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Support: IE9-11+ + // IE's :disabled selector does not pick up the children of disabled fieldsets + docElem.appendChild( el ).disabled = true; + if ( el.querySelectorAll(":disabled").length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Opera 10-11 does not throw on post-comma invalid pseudos + el.querySelectorAll("*,:x"); + rbuggyQSA.push(",.*:"); + }); + } + + if ( (support.matchesSelector = rnative.test( (matches = docElem.matches || + docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector) )) ) { + + assert(function( el ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( el, "*" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( el, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + }); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); + + /* Contains + ---------------------------------------------------------------------- */ + hasCompare = rnative.test( docElem.compareDocumentPosition ); + + // Element contains another + // Purposefully self-exclusive + // As in, an element does not contain itself + contains = hasCompare || rnative.test( docElem.contains ) ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + )); + } : + function( a, b ) { + if ( b ) { + while ( (b = b.parentNode) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + /* Sorting + ---------------------------------------------------------------------- */ + + // Document order sorting + sortOrder = hasCompare ? + function( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + // Sort on method existence if only one input has compareDocumentPosition + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; + if ( compare ) { + return compare; + } + + // Calculate position if both inputs belong to the same document + compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? + a.compareDocumentPosition( b ) : + + // Otherwise we know they are disconnected + 1; + + // Disconnected nodes + if ( compare & 1 || + (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { + + // Choose the first element that is related to our preferred document + if ( a === document || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { + return -1; + } + if ( b === document || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } : + function( a, b ) { + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Parentless nodes are either documents or disconnected + if ( !aup || !bup ) { + return a === document ? -1 : + b === document ? 1 : + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( (cur = cur.parentNode) ) { + ap.unshift( cur ); + } + cur = b; + while ( (cur = cur.parentNode) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[i] === bp[i] ) { + i++; + } + + return i ? + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[i], bp[i] ) : + + // Otherwise nodes in our document sort first + ap[i] === preferredDoc ? -1 : + bp[i] === preferredDoc ? 1 : + 0; + }; + + return document; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + // Make sure that attribute selectors are quoted + expr = expr.replace( rattributeQuotes, "='$1']" ); + + if ( support.matchesSelector && documentIsHTML && + !compilerCache[ expr + " " ] && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch (e) {} + } + + return Sizzle( expr, document, null, [ elem ] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + // Set document vars if needed + if ( ( context.ownerDocument || context ) !== document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + var fn = Expr.attrHandle[ name.toLowerCase() ], + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined; + + return val !== undefined ? + val : + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + (val = elem.getAttributeNode(name)) && val.specified ? + val.value : + null; +}; + +Sizzle.escape = function( sel ) { + return (sel + "").replace( rcssescape, fcssescape ); +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + j = 0, + i = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice( 0 ); + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( (elem = results[i++]) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; + + return results; +}; + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + // If no nodeType, this is expected to be an array + while ( (node = elem[i++]) ) { + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent for elements + // innerText usage removed for consistency of new lines (jQuery #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + // Do not include comment or processing instruction nodes + + return ret; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[1] = match[1].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape ); + + if ( match[2] === "~=" ) { + match[3] = " " + match[3] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[1] = match[1].toLowerCase(); + + if ( match[1].slice( 0, 3 ) === "nth" ) { + // nth-* requires argument + if ( !match[3] ) { + Sizzle.error( match[0] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); + match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); + + // other types prohibit arguments + } else if ( match[3] ) { + Sizzle.error( match[0] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[6] && match[2]; + + if ( matchExpr["CHILD"].test( match[0] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[3] ) { + match[2] = match[4] || match[5] || ""; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + // Get excess from tokenize (recursively) + (excess = tokenize( unquoted, true )) && + // advance to the next closing parenthesis + (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { + + // excess is a negative index + match[0] = match[0].slice( 0, excess ); + match[2] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeNameSelector ) { + var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { return true; } : + function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && + classCache( className, function( elem ) { + return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" ); + }); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + }; + }, + + "CHILD": function( type, what, argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, context, xml ) { + var cache, uniqueCache, outerCache, node, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType, + diff = false; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( (node = node[ dir ]) ) { + if ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) { + + return false; + } + } + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + + // Seek `elem` from a previously-cached index + + // ...in a gzip-friendly way + node = parent; + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex && cache[ 2 ]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( (node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + (diff = nodeIndex = 0) || start.pop()) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + } else { + // Use previously-cached element index if available + if ( useCache ) { + // ...in a gzip-friendly way + node = elem; + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex; + } + + // xml :nth-child(...) + // or :nth-last-child(...) or :nth(-last)?-of-type(...) + if ( diff === false ) { + // Use the same loop as above to seek `elem` from the start + while ( (node = ++nodeIndex && node && node[ dir ] || + (diff = nodeIndex = 0) || start.pop()) ) { + + if ( ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) && + ++diff ) { + + // Cache the index of each encountered element + if ( useCache ) { + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + uniqueCache[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction(function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf( seed, matched[i] ); + seed[ idx ] = !( matches[ idx ] = matched[i] ); + } + }) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + // Potentially complex pseudos + "not": markFunction(function( selector ) { + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction(function( seed, matches, context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( (elem = unmatched[i]) ) { + seed[i] = !(matches[i] = elem); + } + } + }) : + function( elem, context, xml ) { + input[0] = elem; + matcher( input, null, xml, results ); + // Don't keep the element (issue #299) + input[0] = null; + return !results.pop(); + }; + }), + + "has": markFunction(function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + }), + + "contains": markFunction(function( text ) { + text = text.replace( runescape, funescape ); + return function( elem ) { + return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; + }; + }), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + // lang value must be a valid identifier + if ( !ridentifier.test(lang || "") ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( (elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); + return false; + }; + }), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); + }, + + // Boolean properties + "enabled": createDisabledPseudo( false ), + "disabled": createDisabledPseudo( true ), + + "checked": function( elem ) { + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); + }, + + "selected": function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), + // but not by others (comment: 8; processing instruction: 7; etc.) + // nodeType < 6 works because attributes (2) do not appear as children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeType < 6 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos["empty"]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + + // Support: IE<8 + // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" + ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" ); + }, + + // Position-in-collection + "first": createPositionalPseudo(function() { + return [ 0 ]; + }), + + "last": createPositionalPseudo(function( matchIndexes, length ) { + return [ length - 1 ]; + }), + + "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + }), + + "even": createPositionalPseudo(function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "odd": createPositionalPseudo(function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }) + } +}; + +Expr.pseudos["nth"] = Expr.pseudos["eq"]; + +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +// Easy API for creating new setFilters +function setFilters() {} +setFilters.prototype = Expr.filters = Expr.pseudos; +Expr.setFilters = new setFilters(); + +tokenize = Sizzle.tokenize = function( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || (match = rcomma.exec( soFar )) ) { + if ( match ) { + // Don't consume trailing commas as valid + soFar = soFar.slice( match[0].length ) || soFar; + } + groups.push( (tokens = []) ); + } + + matched = false; + + // Combinators + if ( (match = rcombinators.exec( soFar )) ) { + matched = match.shift(); + tokens.push({ + value: matched, + // Cast descendant combinators to space + type: match[0].replace( rtrim, " " ) + }); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || + (match = preFilters[ type ]( match ))) ) { + matched = match.shift(); + tokens.push({ + value: matched, + type: type, + matches: match + }); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +}; + +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[i].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + skip = combinator.next, + key = skip || dir, + checkNonElements = base && key === "parentNode", + doneName = done++; + + return combinator.first ? + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + return false; + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var oldCache, uniqueCache, outerCache, + newCache = [ dirruns, doneName ]; + + // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching + if ( xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || (elem[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ elem.uniqueID ] || (outerCache[ elem.uniqueID ] = {}); + + if ( skip && skip === elem.nodeName.toLowerCase() ) { + elem = elem[ dir ] || elem; + } else if ( (oldCache = uniqueCache[ key ]) && + oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { + + // Assign to newCache so results back-propagate to previous elements + return (newCache[ 2 ] = oldCache[ 2 ]); + } else { + // Reuse newcache so results back-propagate to previous elements + uniqueCache[ key ] = newCache; + + // A match means we're done; a fail means we have to keep checking + if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { + return true; + } + } + } + } + } + return false; + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[i]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[0]; +} + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[i], results ); + } + return results; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( (elem = unmatched[i]) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction(function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( (elem = temp[i]) ) { + matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) ) { + // Restore matcherIn since elem is not yet a final match + temp.push( (matcherIn[i] = elem) ); + } + } + postFinder( null, (matcherOut = []), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) && + (temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) { + + seed[temp] = !(results[temp] = elem); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + }); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[0].type ], + implicitRelative = leadingRelative || Expr.relative[" "], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + (checkContext = context).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + // Avoid hanging onto element (issue #299) + checkContext = null; + return ret; + } ]; + + for ( ; i < len; i++ ) { + if ( (matcher = Expr.relative[ tokens[i].type ]) ) { + matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; + } else { + matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[j].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) + ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, outermost ) { + var elem, j, matcher, + matchedCount = 0, + i = "0", + unmatched = seed && [], + setMatched = [], + contextBackup = outermostContext, + // We must always have either seed elements or outermost context + elems = seed || byElement && Expr.find["TAG"]( "*", outermost ), + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), + len = elems.length; + + if ( outermost ) { + outermostContext = context === document || context || outermost; + } + + // Add elements passing elementMatchers directly to results + // Support: IE<9, Safari + // Tolerate NodeList properties (IE: "length"; Safari: <number>) matching elements by id + for ( ; i !== len && (elem = elems[i]) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + if ( !context && elem.ownerDocument !== document ) { + setDocument( elem ); + xml = !documentIsHTML; + } + while ( (matcher = elementMatchers[j++]) ) { + if ( matcher( elem, context || document, xml) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + // They will have gone through all possible matchers + if ( (elem = !matcher && elem) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // `i` is now the count of elements visited above, and adding it to `matchedCount` + // makes the latter nonnegative. + matchedCount += i; + + // Apply set filters to unmatched elements + // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` + // equals `i`), unless we didn't visit _any_ elements in the above loop because we have + // no element matchers and no seed. + // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that + // case, which will result in a "00" `matchedCount` that differs from `i` but is also + // numerically zero. + if ( bySet && i !== matchedCount ) { + j = 0; + while ( (matcher = setMatchers[j++]) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !(unmatched[i] || setMatched[i]) ) { + setMatched[i] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + // Generate a function of recursive functions that can be used to check each element + if ( !match ) { + match = tokenize( selector ); + } + i = match.length; + while ( i-- ) { + cached = matcherFromTokens( match[i] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); + + // Save selector and tokenization + cached.selector = selector; + } + return cached; +}; + +/** + * A low-level selection function that works with Sizzle's compiled + * selector functions + * @param {String|Function} selector A selector or a pre-compiled + * selector function built with Sizzle.compile + * @param {Element} context + * @param {Array} [results] + * @param {Array} [seed] A set of elements to match against + */ +select = Sizzle.select = function( selector, context, results, seed ) { + var i, tokens, token, type, find, + compiled = typeof selector === "function" && selector, + match = !seed && tokenize( (selector = compiled.selector || selector) ); + + results = results || []; + + // Try to minimize operations if there is only one selector in the list and no seed + // (the latter of which guarantees us context) + if ( match.length === 1 ) { + + // Reduce context if the leading compound selector is an ID + tokens = match[0] = match[0].slice( 0 ); + if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && + context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[1].type ] ) { + + context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; + if ( !context ) { + return results; + + // Precompiled matchers will still verify ancestry, so step up a level + } else if ( compiled ) { + context = context.parentNode; + } + + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[i]; + + // Abort if we hit a combinator + if ( Expr.relative[ (type = token.type) ] ) { + break; + } + if ( (find = Expr.find[ type ]) ) { + // Search, expanding context for leading sibling combinators + if ( (seed = find( + token.matches[0].replace( runescape, funescape ), + rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context + )) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; + } + + break; + } + } + } + } + + // Compile and execute a filtering function if one is not provided + // Provide `match` to avoid retokenization if we modified the selector above + ( compiled || compile( selector, match ) )( + seed, + context, + !documentIsHTML, + results, + !context || rsibling.test( selector ) && testContext( context.parentNode ) || context + ); + return results; +}; + +// One-time assignments + +// Sort stability +support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; + +// Support: Chrome 14-35+ +// Always assume duplicates if they aren't passed to the comparison function +support.detectDuplicates = !!hasDuplicate; + +// Initialize against the default document +setDocument(); + +// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) +// Detached nodes confoundingly follow *each other* +support.sortDetached = assert(function( el ) { + // Should return 1, but returns 4 (following) + return el.compareDocumentPosition( document.createElement("fieldset") ) & 1; +}); + +// Support: IE<8 +// Prevent attribute/property "interpolation" +// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !assert(function( el ) { + el.innerHTML = "<a href='#'></a>"; + return el.firstChild.getAttribute("href") === "#" ; +}) ) { + addHandle( "type|href|height|width", function( elem, name, isXML ) { + if ( !isXML ) { + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); + } + }); +} + +// Support: IE<9 +// Use defaultValue in place of getAttribute("value") +if ( !support.attributes || !assert(function( el ) { + el.innerHTML = "<input/>"; + el.firstChild.setAttribute( "value", "" ); + return el.firstChild.getAttribute( "value" ) === ""; +}) ) { + addHandle( "value", function( elem, name, isXML ) { + if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } + }); +} + +// Support: IE<9 +// Use getAttributeNode to fetch booleans when getAttribute lies +if ( !assert(function( el ) { + return el.getAttribute("disabled") == null; +}) ) { + addHandle( booleans, function( elem, name, isXML ) { + var val; + if ( !isXML ) { + return elem[ name ] === true ? name.toLowerCase() : + (val = elem.getAttributeNode( name )) && val.specified ? + val.value : + null; + } + }); +} + +return Sizzle; + +})( window ); + + + +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; + +// Deprecated +jQuery.expr[ ":" ] = jQuery.expr.pseudos; +jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; +jQuery.escapeSelector = Sizzle.escape; + + + + +var dir = function( elem, dir, until ) { + var matched = [], + truncate = until !== undefined; + + while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { + if ( elem.nodeType === 1 ) { + if ( truncate && jQuery( elem ).is( until ) ) { + break; + } + matched.push( elem ); + } + } + return matched; +}; + + +var siblings = function( n, elem ) { + var matched = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + matched.push( n ); + } + } + + return matched; +}; + + +var rneedsContext = jQuery.expr.match.needsContext; + + + +function nodeName( elem, name ) { + + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + +}; +var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); + + + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + return !!qualifier.call( elem, i, elem ) !== not; + } ); + } + + // Single element + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + } ); + } + + // Arraylike of elements (jQuery, arguments, Array) + if ( typeof qualifier !== "string" ) { + return jQuery.grep( elements, function( elem ) { + return ( indexOf.call( qualifier, elem ) > -1 ) !== not; + } ); + } + + // Filtered directly for both simple and complex selectors + return jQuery.filter( qualifier, elements, not ); +} + +jQuery.filter = function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + if ( elems.length === 1 && elem.nodeType === 1 ) { + return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : []; + } + + return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + } ) ); +}; + +jQuery.fn.extend( { + find: function( selector ) { + var i, ret, + len = this.length, + self = this; + + if ( typeof selector !== "string" ) { + return this.pushStack( jQuery( selector ).filter( function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + } ) ); + } + + ret = this.pushStack( [] ); + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + return len > 1 ? jQuery.uniqueSort( ret ) : ret; + }, + filter: function( selector ) { + return this.pushStack( winnow( this, selector || [], false ) ); + }, + not: function( selector ) { + return this.pushStack( winnow( this, selector || [], true ) ); + }, + is: function( selector ) { + return !!winnow( + this, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + } +} ); + + +// Initialize a jQuery object + + +// A central reference to the root jQuery(document) +var rootjQuery, + + // A simple way to check for HTML strings + // Prioritize #id over <tag> to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + // Shortcut simple #id case for speed + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, + + init = jQuery.fn.init = function( selector, context, root ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Method init() accepts an alternate rootjQuery + // so migrate can support jQuery.sub (gh-2101) + root = root || rootjQuery; + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector[ 0 ] === "<" && + selector[ selector.length - 1 ] === ">" && + selector.length >= 3 ) { + + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && ( match[ 1 ] || !context ) ) { + + // HANDLE: $(html) -> $(array) + if ( match[ 1 ] ) { + context = context instanceof jQuery ? context[ 0 ] : context; + + // Option to run scripts is true for back-compat + // Intentionally let the error be thrown if parseHTML is not present + jQuery.merge( this, jQuery.parseHTML( + match[ 1 ], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + + // Properties of context are called as methods if possible + if ( isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[ 2 ] ); + + if ( elem ) { + + // Inject the element directly into the jQuery object + this[ 0 ] = elem; + this.length = 1; + } + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || root ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this[ 0 ] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( isFunction( selector ) ) { + return root.ready !== undefined ? + root.ready( selector ) : + + // Execute immediately if ready is not present + selector( jQuery ); + } + + return jQuery.makeArray( selector, this ); + }; + +// Give the init function the jQuery prototype for later instantiation +init.prototype = jQuery.fn; + +// Initialize central reference +rootjQuery = jQuery( document ); + + +var rparentsprev = /^(?:parents|prev(?:Until|All))/, + + // Methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend( { + has: function( target ) { + var targets = jQuery( target, this ), + l = targets.length; + + return this.filter( function() { + var i = 0; + for ( ; i < l; i++ ) { + if ( jQuery.contains( this, targets[ i ] ) ) { + return true; + } + } + } ); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + matched = [], + targets = typeof selectors !== "string" && jQuery( selectors ); + + // Positional selectors never match, since there's no _selection_ context + if ( !rneedsContext.test( selectors ) ) { + for ( ; i < l; i++ ) { + for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { + + // Always skip document fragments + if ( cur.nodeType < 11 && ( targets ? + targets.index( cur ) > -1 : + + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector( cur, selectors ) ) ) { + + matched.push( cur ); + break; + } + } + } + } + + return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); + }, + + // Determine the position of an element within the set + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; + } + + // Index in selector + if ( typeof elem === "string" ) { + return indexOf.call( jQuery( elem ), this[ 0 ] ); + } + + // Locate the position of the desired element + return indexOf.call( this, + + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[ 0 ] : elem + ); + }, + + add: function( selector, context ) { + return this.pushStack( + jQuery.uniqueSort( + jQuery.merge( this.get(), jQuery( selector, context ) ) + ) + ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter( selector ) + ); + } +} ); + +function sibling( cur, dir ) { + while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {} + return cur; +} + +jQuery.each( { + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return siblings( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return siblings( elem.firstChild ); + }, + contents: function( elem ) { + if ( nodeName( elem, "iframe" ) ) { + return elem.contentDocument; + } + + // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only + // Treat the template element as a regular one in browsers that + // don't support it. + if ( nodeName( elem, "template" ) ) { + elem = elem.content || elem; + } + + return jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var matched = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + matched = jQuery.filter( selector, matched ); + } + + if ( this.length > 1 ) { + + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + jQuery.uniqueSort( matched ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + matched.reverse(); + } + } + + return this.pushStack( matched ); + }; +} ); +var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g ); + + + +// Convert String-formatted options into Object-formatted ones +function createOptions( options ) { + var object = {}; + jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) { + object[ flag ] = true; + } ); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + createOptions( options ) : + jQuery.extend( {}, options ); + + var // Flag to know if list is currently firing + firing, + + // Last fire value for non-forgettable lists + memory, + + // Flag to know if list was already fired + fired, + + // Flag to prevent firing + locked, + + // Actual callback list + list = [], + + // Queue of execution data for repeatable lists + queue = [], + + // Index of currently firing callback (modified by add/remove as needed) + firingIndex = -1, + + // Fire callbacks + fire = function() { + + // Enforce single-firing + locked = locked || options.once; + + // Execute callbacks for all pending executions, + // respecting firingIndex overrides and runtime changes + fired = firing = true; + for ( ; queue.length; firingIndex = -1 ) { + memory = queue.shift(); + while ( ++firingIndex < list.length ) { + + // Run callback and check for early termination + if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && + options.stopOnFalse ) { + + // Jump to end and forget the data so .add doesn't re-fire + firingIndex = list.length; + memory = false; + } + } + } + + // Forget the data if we're done with it + if ( !options.memory ) { + memory = false; + } + + firing = false; + + // Clean up if we're done firing for good + if ( locked ) { + + // Keep an empty list if we have data for future add calls + if ( memory ) { + list = []; + + // Otherwise, this object is spent + } else { + list = ""; + } + } + }, + + // Actual Callbacks object + self = { + + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + + // If we have memory from a past run, we should fire after adding + if ( memory && !firing ) { + firingIndex = list.length - 1; + queue.push( memory ); + } + + ( function add( args ) { + jQuery.each( args, function( _, arg ) { + if ( isFunction( arg ) ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && toType( arg ) !== "string" ) { + + // Inspect recursively + add( arg ); + } + } ); + } )( arguments ); + + if ( memory && !firing ) { + fire(); + } + } + return this; + }, + + // Remove a callback from the list + remove: function() { + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + + // Handle firing indexes + if ( index <= firingIndex ) { + firingIndex--; + } + } + } ); + return this; + }, + + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? + jQuery.inArray( fn, list ) > -1 : + list.length > 0; + }, + + // Remove all callbacks from the list + empty: function() { + if ( list ) { + list = []; + } + return this; + }, + + // Disable .fire and .add + // Abort any current/pending executions + // Clear all callbacks and values + disable: function() { + locked = queue = []; + list = memory = ""; + return this; + }, + disabled: function() { + return !list; + }, + + // Disable .fire + // Also disable .add unless we have memory (since it would have no effect) + // Abort any pending executions + lock: function() { + locked = queue = []; + if ( !memory && !firing ) { + list = memory = ""; + } + return this; + }, + locked: function() { + return !!locked; + }, + + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( !locked ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + queue.push( args ); + if ( !firing ) { + fire(); + } + } + return this; + }, + + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; + + +function Identity( v ) { + return v; +} +function Thrower( ex ) { + throw ex; +} + +function adoptValue( value, resolve, reject, noValue ) { + var method; + + try { + + // Check for promise aspect first to privilege synchronous behavior + if ( value && isFunction( ( method = value.promise ) ) ) { + method.call( value ).done( resolve ).fail( reject ); + + // Other thenables + } else if ( value && isFunction( ( method = value.then ) ) ) { + method.call( value, resolve, reject ); + + // Other non-thenables + } else { + + // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer: + // * false: [ value ].slice( 0 ) => resolve( value ) + // * true: [ value ].slice( 1 ) => resolve() + resolve.apply( undefined, [ value ].slice( noValue ) ); + } + + // For Promises/A+, convert exceptions into rejections + // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in + // Deferred#then to conditionally suppress rejection. + } catch ( value ) { + + // Support: Android 4.0 only + // Strict mode functions invoked without .call/.apply get global-object context + reject.apply( undefined, [ value ] ); + } +} + +jQuery.extend( { + + Deferred: function( func ) { + var tuples = [ + + // action, add listener, callbacks, + // ... .then handlers, argument index, [final state] + [ "notify", "progress", jQuery.Callbacks( "memory" ), + jQuery.Callbacks( "memory" ), 2 ], + [ "resolve", "done", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 0, "resolved" ], + [ "reject", "fail", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 1, "rejected" ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + "catch": function( fn ) { + return promise.then( null, fn ); + }, + + // Keep pipe for back-compat + pipe: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + + return jQuery.Deferred( function( newDefer ) { + jQuery.each( tuples, function( i, tuple ) { + + // Map tuples (progress, done, fail) to arguments (done, fail, progress) + var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ]; + + // deferred.progress(function() { bind to newDefer or newDefer.notify }) + // deferred.done(function() { bind to newDefer or newDefer.resolve }) + // deferred.fail(function() { bind to newDefer or newDefer.reject }) + deferred[ tuple[ 1 ] ]( function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && isFunction( returned.promise ) ) { + returned.promise() + .progress( newDefer.notify ) + .done( newDefer.resolve ) + .fail( newDefer.reject ); + } else { + newDefer[ tuple[ 0 ] + "With" ]( + this, + fn ? [ returned ] : arguments + ); + } + } ); + } ); + fns = null; + } ).promise(); + }, + then: function( onFulfilled, onRejected, onProgress ) { + var maxDepth = 0; + function resolve( depth, deferred, handler, special ) { + return function() { + var that = this, + args = arguments, + mightThrow = function() { + var returned, then; + + // Support: Promises/A+ section 2.3.3.3.3 + // https://promisesaplus.com/#point-59 + // Ignore double-resolution attempts + if ( depth < maxDepth ) { + return; + } + + returned = handler.apply( that, args ); + + // Support: Promises/A+ section 2.3.1 + // https://promisesaplus.com/#point-48 + if ( returned === deferred.promise() ) { + throw new TypeError( "Thenable self-resolution" ); + } + + // Support: Promises/A+ sections 2.3.3.1, 3.5 + // https://promisesaplus.com/#point-54 + // https://promisesaplus.com/#point-75 + // Retrieve `then` only once + then = returned && + + // Support: Promises/A+ section 2.3.4 + // https://promisesaplus.com/#point-64 + // Only check objects and functions for thenability + ( typeof returned === "object" || + typeof returned === "function" ) && + returned.then; + + // Handle a returned thenable + if ( isFunction( then ) ) { + + // Special processors (notify) just wait for resolution + if ( special ) { + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ) + ); + + // Normal processors (resolve) also hook into progress + } else { + + // ...and disregard older resolution values + maxDepth++; + + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ), + resolve( maxDepth, deferred, Identity, + deferred.notifyWith ) + ); + } + + // Handle all other returned values + } else { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Identity ) { + that = undefined; + args = [ returned ]; + } + + // Process the value(s) + // Default process is resolve + ( special || deferred.resolveWith )( that, args ); + } + }, + + // Only normal processors (resolve) catch and reject exceptions + process = special ? + mightThrow : + function() { + try { + mightThrow(); + } catch ( e ) { + + if ( jQuery.Deferred.exceptionHook ) { + jQuery.Deferred.exceptionHook( e, + process.stackTrace ); + } + + // Support: Promises/A+ section 2.3.3.3.4.1 + // https://promisesaplus.com/#point-61 + // Ignore post-resolution exceptions + if ( depth + 1 >= maxDepth ) { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Thrower ) { + that = undefined; + args = [ e ]; + } + + deferred.rejectWith( that, args ); + } + } + }; + + // Support: Promises/A+ section 2.3.3.3.1 + // https://promisesaplus.com/#point-57 + // Re-resolve promises immediately to dodge false rejection from + // subsequent errors + if ( depth ) { + process(); + } else { + + // Call an optional hook to record the stack, in case of exception + // since it's otherwise lost when execution goes async + if ( jQuery.Deferred.getStackHook ) { + process.stackTrace = jQuery.Deferred.getStackHook(); + } + window.setTimeout( process ); + } + }; + } + + return jQuery.Deferred( function( newDefer ) { + + // progress_handlers.add( ... ) + tuples[ 0 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onProgress ) ? + onProgress : + Identity, + newDefer.notifyWith + ) + ); + + // fulfilled_handlers.add( ... ) + tuples[ 1 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onFulfilled ) ? + onFulfilled : + Identity + ) + ); + + // rejected_handlers.add( ... ) + tuples[ 2 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onRejected ) ? + onRejected : + Thrower + ) + ); + } ).promise(); + }, + + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 5 ]; + + // promise.progress = list.add + // promise.done = list.add + // promise.fail = list.add + promise[ tuple[ 1 ] ] = list.add; + + // Handle state + if ( stateString ) { + list.add( + function() { + + // state = "resolved" (i.e., fulfilled) + // state = "rejected" + state = stateString; + }, + + // rejected_callbacks.disable + // fulfilled_callbacks.disable + tuples[ 3 - i ][ 2 ].disable, + + // rejected_handlers.disable + // fulfilled_handlers.disable + tuples[ 3 - i ][ 3 ].disable, + + // progress_callbacks.lock + tuples[ 0 ][ 2 ].lock, + + // progress_handlers.lock + tuples[ 0 ][ 3 ].lock + ); + } + + // progress_handlers.fire + // fulfilled_handlers.fire + // rejected_handlers.fire + list.add( tuple[ 3 ].fire ); + + // deferred.notify = function() { deferred.notifyWith(...) } + // deferred.resolve = function() { deferred.resolveWith(...) } + // deferred.reject = function() { deferred.rejectWith(...) } + deferred[ tuple[ 0 ] ] = function() { + deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); + return this; + }; + + // deferred.notifyWith = list.fireWith + // deferred.resolveWith = list.fireWith + // deferred.rejectWith = list.fireWith + deferred[ tuple[ 0 ] + "With" ] = list.fireWith; + } ); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( singleValue ) { + var + + // count of uncompleted subordinates + remaining = arguments.length, + + // count of unprocessed arguments + i = remaining, + + // subordinate fulfillment data + resolveContexts = Array( i ), + resolveValues = slice.call( arguments ), + + // the master Deferred + master = jQuery.Deferred(), + + // subordinate callback factory + updateFunc = function( i ) { + return function( value ) { + resolveContexts[ i ] = this; + resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; + if ( !( --remaining ) ) { + master.resolveWith( resolveContexts, resolveValues ); + } + }; + }; + + // Single- and empty arguments are adopted like Promise.resolve + if ( remaining <= 1 ) { + adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject, + !remaining ); + + // Use .then() to unwrap secondary thenables (cf. gh-3000) + if ( master.state() === "pending" || + isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { + + return master.then(); + } + } + + // Multiple arguments are aggregated like Promise.all array elements + while ( i-- ) { + adoptValue( resolveValues[ i ], updateFunc( i ), master.reject ); + } + + return master.promise(); + } +} ); + + +// These usually indicate a programmer mistake during development, +// warn about them ASAP rather than swallowing them by default. +var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/; + +jQuery.Deferred.exceptionHook = function( error, stack ) { + + // Support: IE 8 - 9 only + // Console exists when dev tools are open, which can happen at any time + if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) { + window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack ); + } +}; + + + + +jQuery.readyException = function( error ) { + window.setTimeout( function() { + throw error; + } ); +}; + + + + +// The deferred used on DOM ready +var readyList = jQuery.Deferred(); + +jQuery.fn.ready = function( fn ) { + + readyList + .then( fn ) + + // Wrap jQuery.readyException in a function so that the lookup + // happens at the time of error handling instead of callback + // registration. + .catch( function( error ) { + jQuery.readyException( error ); + } ); + + return this; +}; + +jQuery.extend( { + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + } +} ); + +jQuery.ready.then = readyList.then; + +// The ready event handler and self cleanup method +function completed() { + document.removeEventListener( "DOMContentLoaded", completed ); + window.removeEventListener( "load", completed ); + jQuery.ready(); +} + +// Catch cases where $(document).ready() is called +// after the browser event has already occurred. +// Support: IE <=9 - 10 only +// Older IE sometimes signals "interactive" too soon +if ( document.readyState === "complete" || + ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { + + // Handle it asynchronously to allow scripts the opportunity to delay ready + window.setTimeout( jQuery.ready ); + +} else { + + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed ); +} + + + + +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function +var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + len = elems.length, + bulk = key == null; + + // Sets many values + if ( toType( key ) === "object" ) { + chainable = true; + for ( i in key ) { + access( elems, fn, i, key[ i ], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < len; i++ ) { + fn( + elems[ i ], key, raw ? + value : + value.call( elems[ i ], i, fn( elems[ i ], key ) ) + ); + } + } + } + + if ( chainable ) { + return elems; + } + + // Gets + if ( bulk ) { + return fn.call( elems ); + } + + return len ? fn( elems[ 0 ], key ) : emptyGet; +}; + + +// Matches dashed string for camelizing +var rmsPrefix = /^-ms-/, + rdashAlpha = /-([a-z])/g; + +// Used by camelCase as callback to replace() +function fcamelCase( all, letter ) { + return letter.toUpperCase(); +} + +// Convert dashed to camelCase; used by the css and data modules +// Support: IE <=9 - 11, Edge 12 - 15 +// Microsoft forgot to hump their vendor prefix (#9572) +function camelCase( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); +} +var acceptData = function( owner ) { + + // Accepts only: + // - Node + // - Node.ELEMENT_NODE + // - Node.DOCUMENT_NODE + // - Object + // - Any + return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); +}; + + + + +function Data() { + this.expando = jQuery.expando + Data.uid++; +} + +Data.uid = 1; + +Data.prototype = { + + cache: function( owner ) { + + // Check if the owner object already has a cache + var value = owner[ this.expando ]; + + // If not, create one + if ( !value ) { + value = {}; + + // We can accept data for non-element nodes in modern browsers, + // but we should not, see #8335. + // Always return an empty object. + if ( acceptData( owner ) ) { + + // If it is a node unlikely to be stringify-ed or looped over + // use plain assignment + if ( owner.nodeType ) { + owner[ this.expando ] = value; + + // Otherwise secure it in a non-enumerable property + // configurable must be true to allow the property to be + // deleted when data is removed + } else { + Object.defineProperty( owner, this.expando, { + value: value, + configurable: true + } ); + } + } + } + + return value; + }, + set: function( owner, data, value ) { + var prop, + cache = this.cache( owner ); + + // Handle: [ owner, key, value ] args + // Always use camelCase key (gh-2257) + if ( typeof data === "string" ) { + cache[ camelCase( data ) ] = value; + + // Handle: [ owner, { properties } ] args + } else { + + // Copy the properties one-by-one to the cache object + for ( prop in data ) { + cache[ camelCase( prop ) ] = data[ prop ]; + } + } + return cache; + }, + get: function( owner, key ) { + return key === undefined ? + this.cache( owner ) : + + // Always use camelCase key (gh-2257) + owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ]; + }, + access: function( owner, key, value ) { + + // In cases where either: + // + // 1. No key was specified + // 2. A string key was specified, but no value provided + // + // Take the "read" path and allow the get method to determine + // which value to return, respectively either: + // + // 1. The entire cache object + // 2. The data stored at the key + // + if ( key === undefined || + ( ( key && typeof key === "string" ) && value === undefined ) ) { + + return this.get( owner, key ); + } + + // When the key is not a string, or both a key and value + // are specified, set or extend (existing objects) with either: + // + // 1. An object of properties + // 2. A key and value + // + this.set( owner, key, value ); + + // Since the "set" path can have two possible entry points + // return the expected data based on which path was taken[*] + return value !== undefined ? value : key; + }, + remove: function( owner, key ) { + var i, + cache = owner[ this.expando ]; + + if ( cache === undefined ) { + return; + } + + if ( key !== undefined ) { + + // Support array or space separated string of keys + if ( Array.isArray( key ) ) { + + // If key is an array of keys... + // We always set camelCase keys, so remove that. + key = key.map( camelCase ); + } else { + key = camelCase( key ); + + // If a key with the spaces exists, use it. + // Otherwise, create an array by matching non-whitespace + key = key in cache ? + [ key ] : + ( key.match( rnothtmlwhite ) || [] ); + } + + i = key.length; + + while ( i-- ) { + delete cache[ key[ i ] ]; + } + } + + // Remove the expando if there's no more data + if ( key === undefined || jQuery.isEmptyObject( cache ) ) { + + // Support: Chrome <=35 - 45 + // Webkit & Blink performance suffers when deleting properties + // from DOM nodes, so set to undefined instead + // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted) + if ( owner.nodeType ) { + owner[ this.expando ] = undefined; + } else { + delete owner[ this.expando ]; + } + } + }, + hasData: function( owner ) { + var cache = owner[ this.expando ]; + return cache !== undefined && !jQuery.isEmptyObject( cache ); + } +}; +var dataPriv = new Data(); + +var dataUser = new Data(); + + + +// Implementation Summary +// +// 1. Enforce API surface and semantic compatibility with 1.9.x branch +// 2. Improve the module's maintainability by reducing the storage +// paths to a single mechanism. +// 3. Use the same single mechanism to support "private" and "user" data. +// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) +// 5. Avoid exposing implementation details on user objects (eg. expando properties) +// 6. Provide a clear path for implementation upgrade to WeakMap in 2014 + +var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, + rmultiDash = /[A-Z]/g; + +function getData( data ) { + if ( data === "true" ) { + return true; + } + + if ( data === "false" ) { + return false; + } + + if ( data === "null" ) { + return null; + } + + // Only convert to a number if it doesn't change the string + if ( data === +data + "" ) { + return +data; + } + + if ( rbrace.test( data ) ) { + return JSON.parse( data ); + } + + return data; +} + +function dataAttr( elem, key, data ) { + var name; + + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase(); + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = getData( data ); + } catch ( e ) {} + + // Make sure we set the data so it isn't changed later + dataUser.set( elem, key, data ); + } else { + data = undefined; + } + } + return data; +} + +jQuery.extend( { + hasData: function( elem ) { + return dataUser.hasData( elem ) || dataPriv.hasData( elem ); + }, + + data: function( elem, name, data ) { + return dataUser.access( elem, name, data ); + }, + + removeData: function( elem, name ) { + dataUser.remove( elem, name ); + }, + + // TODO: Now that all calls to _data and _removeData have been replaced + // with direct calls to dataPriv methods, these can be deprecated. + _data: function( elem, name, data ) { + return dataPriv.access( elem, name, data ); + }, + + _removeData: function( elem, name ) { + dataPriv.remove( elem, name ); + } +} ); + +jQuery.fn.extend( { + data: function( key, value ) { + var i, name, data, + elem = this[ 0 ], + attrs = elem && elem.attributes; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = dataUser.get( elem ); + + if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { + i = attrs.length; + while ( i-- ) { + + // Support: IE 11 only + // The attrs elements can be null (#14894) + if ( attrs[ i ] ) { + name = attrs[ i ].name; + if ( name.indexOf( "data-" ) === 0 ) { + name = camelCase( name.slice( 5 ) ); + dataAttr( elem, name, data[ name ] ); + } + } + } + dataPriv.set( elem, "hasDataAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each( function() { + dataUser.set( this, key ); + } ); + } + + return access( this, function( value ) { + var data; + + // The calling jQuery object (element matches) is not empty + // (and therefore has an element appears at this[ 0 ]) and the + // `value` parameter was not undefined. An empty jQuery object + // will result in `undefined` for elem = this[ 0 ] which will + // throw an exception if an attempt to read a data cache is made. + if ( elem && value === undefined ) { + + // Attempt to get data from the cache + // The key will always be camelCased in Data + data = dataUser.get( elem, key ); + if ( data !== undefined ) { + return data; + } + + // Attempt to "discover" the data in + // HTML5 custom data-* attrs + data = dataAttr( elem, key ); + if ( data !== undefined ) { + return data; + } + + // We tried really hard, but the data doesn't exist. + return; + } + + // Set the data... + this.each( function() { + + // We always store the camelCased key + dataUser.set( this, key, value ); + } ); + }, null, value, arguments.length > 1, null, true ); + }, + + removeData: function( key ) { + return this.each( function() { + dataUser.remove( this, key ); + } ); + } +} ); + + +jQuery.extend( { + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = dataPriv.get( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || Array.isArray( data ) ) { + queue = dataPriv.access( elem, type, jQuery.makeArray( data ) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // Clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // Not public - generate a queueHooks object, or return the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return dataPriv.get( elem, key ) || dataPriv.access( elem, key, { + empty: jQuery.Callbacks( "once memory" ).add( function() { + dataPriv.remove( elem, [ type + "queue", key ] ); + } ) + } ); + } +} ); + +jQuery.fn.extend( { + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[ 0 ], type ); + } + + return data === undefined ? + this : + this.each( function() { + var queue = jQuery.queue( this, type, data ); + + // Ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + } ); + }, + dequeue: function( type ) { + return this.each( function() { + jQuery.dequeue( this, type ); + } ); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while ( i-- ) { + tmp = dataPriv.get( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +} ); +var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; + +var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); + + +var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; + +var isHiddenWithinTree = function( elem, el ) { + + // isHiddenWithinTree might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + + // Inline style trumps all + return elem.style.display === "none" || + elem.style.display === "" && + + // Otherwise, check computed style + // Support: Firefox <=43 - 45 + // Disconnected elements can have computed display: none, so first confirm that elem is + // in the document. + jQuery.contains( elem.ownerDocument, elem ) && + + jQuery.css( elem, "display" ) === "none"; + }; + +var swap = function( elem, options, callback, args ) { + var ret, name, + old = {}; + + // Remember the old values, and insert the new ones + for ( name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + ret = callback.apply( elem, args || [] ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + + return ret; +}; + + + + +function adjustCSS( elem, prop, valueParts, tween ) { + var adjusted, scale, + maxIterations = 20, + currentValue = tween ? + function() { + return tween.cur(); + } : + function() { + return jQuery.css( elem, prop, "" ); + }, + initial = currentValue(), + unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), + + // Starting value computation is required for potential unit mismatches + initialInUnit = ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && + rcssNum.exec( jQuery.css( elem, prop ) ); + + if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { + + // Support: Firefox <=54 + // Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144) + initial = initial / 2; + + // Trust units reported by jQuery.css + unit = unit || initialInUnit[ 3 ]; + + // Iteratively approximate from a nonzero starting point + initialInUnit = +initial || 1; + + while ( maxIterations-- ) { + + // Evaluate and update our best guess (doubling guesses that zero out). + // Finish if the scale equals or crosses 1 (making the old*new product non-positive). + jQuery.style( elem, prop, initialInUnit + unit ); + if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) { + maxIterations = 0; + } + initialInUnit = initialInUnit / scale; + + } + + initialInUnit = initialInUnit * 2; + jQuery.style( elem, prop, initialInUnit + unit ); + + // Make sure we update the tween properties later on + valueParts = valueParts || []; + } + + if ( valueParts ) { + initialInUnit = +initialInUnit || +initial || 0; + + // Apply relative offset (+=/-=) if specified + adjusted = valueParts[ 1 ] ? + initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : + +valueParts[ 2 ]; + if ( tween ) { + tween.unit = unit; + tween.start = initialInUnit; + tween.end = adjusted; + } + } + return adjusted; +} + + +var defaultDisplayMap = {}; + +function getDefaultDisplay( elem ) { + var temp, + doc = elem.ownerDocument, + nodeName = elem.nodeName, + display = defaultDisplayMap[ nodeName ]; + + if ( display ) { + return display; + } + + temp = doc.body.appendChild( doc.createElement( nodeName ) ); + display = jQuery.css( temp, "display" ); + + temp.parentNode.removeChild( temp ); + + if ( display === "none" ) { + display = "block"; + } + defaultDisplayMap[ nodeName ] = display; + + return display; +} + +function showHide( elements, show ) { + var display, elem, + values = [], + index = 0, + length = elements.length; + + // Determine new display value for elements that need to change + for ( ; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + + display = elem.style.display; + if ( show ) { + + // Since we force visibility upon cascade-hidden elements, an immediate (and slow) + // check is required in this first loop unless we have a nonempty display value (either + // inline or about-to-be-restored) + if ( display === "none" ) { + values[ index ] = dataPriv.get( elem, "display" ) || null; + if ( !values[ index ] ) { + elem.style.display = ""; + } + } + if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) { + values[ index ] = getDefaultDisplay( elem ); + } + } else { + if ( display !== "none" ) { + values[ index ] = "none"; + + // Remember what we're overwriting + dataPriv.set( elem, "display", display ); + } + } + } + + // Set the display of the elements in a second loop to avoid constant reflow + for ( index = 0; index < length; index++ ) { + if ( values[ index ] != null ) { + elements[ index ].style.display = values[ index ]; + } + } + + return elements; +} + +jQuery.fn.extend( { + show: function() { + return showHide( this, true ); + }, + hide: function() { + return showHide( this ); + }, + toggle: function( state ) { + if ( typeof state === "boolean" ) { + return state ? this.show() : this.hide(); + } + + return this.each( function() { + if ( isHiddenWithinTree( this ) ) { + jQuery( this ).show(); + } else { + jQuery( this ).hide(); + } + } ); + } +} ); +var rcheckableType = ( /^(?:checkbox|radio)$/i ); + +var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]+)/i ); + +var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i ); + + + +// We have to close these tags to support XHTML (#13200) +var wrapMap = { + + // Support: IE <=9 only + option: [ 1, "<select multiple='multiple'>", "</select>" ], + + // XHTML parsers do not magically insert elements in the + // same way that tag soup parsers do. So we cannot shorten + // this by omitting <tbody> or other required elements. + thead: [ 1, "<table>", "</table>" ], + col: [ 2, "<table><colgroup>", "</colgroup></table>" ], + tr: [ 2, "<table><tbody>", "</tbody></table>" ], + td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ], + + _default: [ 0, "", "" ] +}; + +// Support: IE <=9 only +wrapMap.optgroup = wrapMap.option; + +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + + +function getAll( context, tag ) { + + // Support: IE <=9 - 11 only + // Use typeof to avoid zero-argument method invocation on host objects (#15151) + var ret; + + if ( typeof context.getElementsByTagName !== "undefined" ) { + ret = context.getElementsByTagName( tag || "*" ); + + } else if ( typeof context.querySelectorAll !== "undefined" ) { + ret = context.querySelectorAll( tag || "*" ); + + } else { + ret = []; + } + + if ( tag === undefined || tag && nodeName( context, tag ) ) { + return jQuery.merge( [ context ], ret ); + } + + return ret; +} + + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + dataPriv.set( + elems[ i ], + "globalEval", + !refElements || dataPriv.get( refElements[ i ], "globalEval" ) + ); + } +} + + +var rhtml = /<|&#?\w+;/; + +function buildFragment( elems, context, scripts, selection, ignored ) { + var elem, tmp, tag, wrap, contains, j, + fragment = context.createDocumentFragment(), + nodes = [], + i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( toType( elem ) === "object" ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); + + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; + + // Descend through wrappers to the right content + j = wrap[ 0 ]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, tmp.childNodes ); + + // Remember the top-level container + tmp = fragment.firstChild; + + // Ensure the created nodes are orphaned (#12392) + tmp.textContent = ""; + } + } + } + + // Remove wrapper from fragment + fragment.textContent = ""; + + i = 0; + while ( ( elem = nodes[ i++ ] ) ) { + + // Skip elements already in the context collection (trac-4087) + if ( selection && jQuery.inArray( elem, selection ) > -1 ) { + if ( ignored ) { + ignored.push( elem ); + } + continue; + } + + contains = jQuery.contains( elem.ownerDocument, elem ); + + // Append to fragment + tmp = getAll( fragment.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( contains ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( ( elem = tmp[ j++ ] ) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + return fragment; +} + + +( function() { + var fragment = document.createDocumentFragment(), + div = fragment.appendChild( document.createElement( "div" ) ), + input = document.createElement( "input" ); + + // Support: Android 4.0 - 4.3 only + // Check state lost if the name is set (#11217) + // Support: Windows Web Apps (WWA) + // `name` and `type` must use .setAttribute for WWA (#14901) + input.setAttribute( "type", "radio" ); + input.setAttribute( "checked", "checked" ); + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + + // Support: Android <=4.1 only + // Older WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE <=11 only + // Make sure textarea (and checkbox) defaultValue is properly cloned + div.innerHTML = "<textarea>x</textarea>"; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; +} )(); +var documentElement = document.documentElement; + + + +var + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +// Support: IE <=9 only +// See #13393 for more info +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +function on( elem, types, selector, data, fn, one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + on( elem, type, selector, data, types[ type ], one ); + } + return elem; + } + + if ( data == null && fn == null ) { + + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return elem; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return elem.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + } ); +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + + var handleObjIn, eventHandle, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.get( elem ); + + // Don't attach events to noData or text/comment nodes (but allow plain objects) + if ( !elemData ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Ensure that invalid selectors throw exceptions at attach time + // Evaluate against documentElement in case elem is a non-element node (e.g., document) + if ( selector ) { + jQuery.find.matchesSelector( documentElement, selector ); + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !( events = elemData.events ) ) { + events = elemData.events = {}; + } + if ( !( eventHandle = elemData.handle ) ) { + eventHandle = elemData.handle = function( e ) { + + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? + jQuery.event.dispatch.apply( elem, arguments ) : undefined; + }; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend( { + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join( "." ) + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !( handlers = events[ type ] ) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener if the special events handler returns false + if ( !special.setup || + special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var j, origCount, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); + + if ( !elemData || !( events = elemData.events ) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[ 2 ] && + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || + selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || + special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove data and the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + dataPriv.remove( elem, "handle events" ); + } + }, + + dispatch: function( nativeEvent ) { + + // Make a writable jQuery.Event from the native event object + var event = jQuery.event.fix( nativeEvent ); + + var i, j, ret, matched, handleObj, handlerQueue, + args = new Array( arguments.length ), + handlers = ( dataPriv.get( this, "events" ) || {} )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[ 0 ] = event; + + for ( i = 1; i < arguments.length; i++ ) { + args[ i ] = arguments[ i ]; + } + + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( ( handleObj = matched.handlers[ j++ ] ) && + !event.isImmediatePropagationStopped() ) { + + // Triggered event must either 1) have no namespace, or 2) have namespace(s) + // a subset or equal to those in the bound event (both can have no namespace). + if ( !event.rnamespace || event.rnamespace.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || + handleObj.handler ).apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( ( event.result = ret ) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var i, handleObj, sel, matchedHandlers, matchedSelectors, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + if ( delegateCount && + + // Support: IE <=9 + // Black-hole SVG <use> instance trees (trac-13180) + cur.nodeType && + + // Support: Firefox <=42 + // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861) + // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click + // Support: IE 11 only + // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343) + !( event.type === "click" && event.button >= 1 ) ) { + + for ( ; cur !== this; cur = cur.parentNode || this ) { + + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) { + matchedHandlers = []; + matchedSelectors = {}; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matchedSelectors[ sel ] === undefined ) { + matchedSelectors[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) > -1 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matchedSelectors[ sel ] ) { + matchedHandlers.push( handleObj ); + } + } + if ( matchedHandlers.length ) { + handlerQueue.push( { elem: cur, handlers: matchedHandlers } ); + } + } + } + } + + // Add the remaining (directly-bound) handlers + cur = this; + if ( delegateCount < handlers.length ) { + handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } ); + } + + return handlerQueue; + }, + + addProp: function( name, hook ) { + Object.defineProperty( jQuery.Event.prototype, name, { + enumerable: true, + configurable: true, + + get: isFunction( hook ) ? + function() { + if ( this.originalEvent ) { + return hook( this.originalEvent ); + } + } : + function() { + if ( this.originalEvent ) { + return this.originalEvent[ name ]; + } + }, + + set: function( value ) { + Object.defineProperty( this, name, { + enumerable: true, + configurable: true, + writable: true, + value: value + } ); + } + } ); + }, + + fix: function( originalEvent ) { + return originalEvent[ jQuery.expando ] ? + originalEvent : + new jQuery.Event( originalEvent ); + }, + + special: { + load: { + + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + focus: { + + // Fire native event if possible so blur/focus sequence is correct + trigger: function() { + if ( this !== safeActiveElement() && this.focus ) { + this.focus(); + return false; + } + }, + delegateType: "focusin" + }, + blur: { + trigger: function() { + if ( this === safeActiveElement() && this.blur ) { + this.blur(); + return false; + } + }, + delegateType: "focusout" + }, + click: { + + // For checkbox, fire native event so checked state will be right + trigger: function() { + if ( this.type === "checkbox" && this.click && nodeName( this, "input" ) ) { + this.click(); + return false; + } + }, + + // For cross-browser consistency, don't fire native .click() on links + _default: function( event ) { + return nodeName( event.target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Support: Firefox 20+ + // Firefox doesn't alert if the returnValue field is not set. + if ( event.result !== undefined && event.originalEvent ) { + event.originalEvent.returnValue = event.result; + } + } + } + } +}; + +jQuery.removeEvent = function( elem, type, handle ) { + + // This "if" is needed for plain objects + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle ); + } +}; + +jQuery.Event = function( src, props ) { + + // Allow instantiation without the 'new' keyword + if ( !( this instanceof jQuery.Event ) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = src.defaultPrevented || + src.defaultPrevented === undefined && + + // Support: Android <=2.3 only + src.returnValue === false ? + returnTrue : + returnFalse; + + // Create target properties + // Support: Safari <=6 - 7 only + // Target should not be a text node (#504, #13143) + this.target = ( src.target && src.target.nodeType === 3 ) ? + src.target.parentNode : + src.target; + + this.currentTarget = src.currentTarget; + this.relatedTarget = src.relatedTarget; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || Date.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + constructor: jQuery.Event, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + isSimulated: false, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + + if ( e && !this.isSimulated ) { + e.preventDefault(); + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopPropagation(); + } + }, + stopImmediatePropagation: function() { + var e = this.originalEvent; + + this.isImmediatePropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopImmediatePropagation(); + } + + this.stopPropagation(); + } +}; + +// Includes all common event props including KeyEvent and MouseEvent specific props +jQuery.each( { + altKey: true, + bubbles: true, + cancelable: true, + changedTouches: true, + ctrlKey: true, + detail: true, + eventPhase: true, + metaKey: true, + pageX: true, + pageY: true, + shiftKey: true, + view: true, + "char": true, + charCode: true, + key: true, + keyCode: true, + button: true, + buttons: true, + clientX: true, + clientY: true, + offsetX: true, + offsetY: true, + pointerId: true, + pointerType: true, + screenX: true, + screenY: true, + targetTouches: true, + toElement: true, + touches: true, + + which: function( event ) { + var button = event.button; + + // Add which for key events + if ( event.which == null && rkeyEvent.test( event.type ) ) { + return event.charCode != null ? event.charCode : event.keyCode; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + if ( !event.which && button !== undefined && rmouseEvent.test( event.type ) ) { + if ( button & 1 ) { + return 1; + } + + if ( button & 2 ) { + return 3; + } + + if ( button & 4 ) { + return 2; + } + + return 0; + } + + return event.which; + } +}, jQuery.event.addProp ); + +// Create mouseenter/leave events using mouseover/out and event-time checks +// so that event delegation works in jQuery. +// Do the same for pointerenter/pointerleave and pointerover/pointerout +// +// Support: Safari 7 only +// Safari sends mouseenter too often; see: +// https://bugs.chromium.org/p/chromium/issues/detail?id=470258 +// for the description of the bug (it existed in older Chrome versions as well). +jQuery.each( { + mouseenter: "mouseover", + mouseleave: "mouseout", + pointerenter: "pointerover", + pointerleave: "pointerout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mouseenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +} ); + +jQuery.fn.extend( { + + on: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn ); + }, + one: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? + handleObj.origType + "." + handleObj.namespace : + handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each( function() { + jQuery.event.remove( this, types, fn, selector ); + } ); + } +} ); + + +var + + /* eslint-disable max-len */ + + // See https://github.com/eslint/eslint/issues/3229 + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi, + + /* eslint-enable */ + + // Support: IE <=10 - 11, Edge 12 - 13 only + // In IE/Edge using regex groups here causes severe slowdowns. + // See https://connect.microsoft.com/IE/feedback/details/1736512/ + rnoInnerhtml = /<script|<style|<link/i, + + // checked="checked" or checked + rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, + rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g; + +// Prefer a tbody over its parent table for containing new rows +function manipulationTarget( elem, content ) { + if ( nodeName( elem, "table" ) && + nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) { + + return jQuery( elem ).children( "tbody" )[ 0 ] || elem; + } + + return elem; +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) { + elem.type = elem.type.slice( 5 ); + } else { + elem.removeAttribute( "type" ); + } + + return elem; +} + +function cloneCopyEvent( src, dest ) { + var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events; + + if ( dest.nodeType !== 1 ) { + return; + } + + // 1. Copy private data: events, handlers, etc. + if ( dataPriv.hasData( src ) ) { + pdataOld = dataPriv.access( src ); + pdataCur = dataPriv.set( dest, pdataOld ); + events = pdataOld.events; + + if ( events ) { + delete pdataCur.handle; + pdataCur.events = {}; + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + } + + // 2. Copy user data + if ( dataUser.hasData( src ) ) { + udataOld = dataUser.access( src ); + udataCur = jQuery.extend( {}, udataOld ); + + dataUser.set( dest, udataCur ); + } +} + +// Fix IE bugs, see support tests +function fixInput( src, dest ) { + var nodeName = dest.nodeName.toLowerCase(); + + // Fails to persist the checked state of a cloned checkbox or radio button. + if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + dest.checked = src.checked; + + // Fails to return the selected option to the default selected state when cloning options + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +function domManip( collection, args, callback, ignored ) { + + // Flatten any nested arrays + args = concat.apply( [], args ); + + var fragment, first, scripts, hasScripts, node, doc, + i = 0, + l = collection.length, + iNoClone = l - 1, + value = args[ 0 ], + valueIsFunction = isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( valueIsFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return collection.each( function( index ) { + var self = collection.eq( index ); + if ( valueIsFunction ) { + args[ 0 ] = value.call( this, index, self.html() ); + } + domManip( self, args, callback, ignored ); + } ); + } + + if ( l ) { + fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + // Require either new content or an interest in ignored elements to invoke the callback + if ( first || ignored ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item + // instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( collection[ i ], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !dataPriv.access( node, "globalEval" ) && + jQuery.contains( doc, node ) ) { + + if ( node.src && ( node.type || "" ).toLowerCase() !== "module" ) { + + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl ) { + jQuery._evalUrl( node.src ); + } + } else { + DOMEval( node.textContent.replace( rcleanScript, "" ), doc, node ); + } + } + } + } + } + } + + return collection; +} + +function remove( elem, selector, keepData ) { + var node, + nodes = selector ? jQuery.filter( selector, elem ) : elem, + i = 0; + + for ( ; ( node = nodes[ i ] ) != null; i++ ) { + if ( !keepData && node.nodeType === 1 ) { + jQuery.cleanData( getAll( node ) ); + } + + if ( node.parentNode ) { + if ( keepData && jQuery.contains( node.ownerDocument, node ) ) { + setGlobalEval( getAll( node, "script" ) ); + } + node.parentNode.removeChild( node ); + } + } + + return elem; +} + +jQuery.extend( { + htmlPrefilter: function( html ) { + return html.replace( rxhtmlTag, "<$1></$2>" ); + }, + + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var i, l, srcElements, destElements, + clone = elem.cloneNode( true ), + inPage = jQuery.contains( elem.ownerDocument, elem ); + + // Fix IE cloning issues + if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && + !jQuery.isXMLDoc( elem ) ) { + + // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + fixInput( srcElements[ i ], destElements[ i ] ); + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + cloneCopyEvent( srcElements[ i ], destElements[ i ] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + // Return the cloned set + return clone; + }, + + cleanData: function( elems ) { + var data, elem, type, + special = jQuery.event.special, + i = 0; + + for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { + if ( acceptData( elem ) ) { + if ( ( data = elem[ dataPriv.expando ] ) ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataPriv.expando ] = undefined; + } + if ( elem[ dataUser.expando ] ) { + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataUser.expando ] = undefined; + } + } + } + } +} ); + +jQuery.fn.extend( { + detach: function( selector ) { + return remove( this, selector, true ); + }, + + remove: function( selector ) { + return remove( this, selector ); + }, + + text: function( value ) { + return access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().each( function() { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + this.textContent = value; + } + } ); + }, null, value, arguments.length ); + }, + + append: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + } ); + }, + + prepend: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + } ); + }, + + before: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + } ); + }, + + after: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + } ); + }, + + empty: function() { + var elem, + i = 0; + + for ( ; ( elem = this[ i ] ) != null; i++ ) { + if ( elem.nodeType === 1 ) { + + // Prevent memory leaks + jQuery.cleanData( getAll( elem, false ) ); + + // Remove any remaining nodes + elem.textContent = ""; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function() { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + } ); + }, + + html: function( value ) { + return access( this, function( value ) { + var elem = this[ 0 ] || {}, + i = 0, + l = this.length; + + if ( value === undefined && elem.nodeType === 1 ) { + return elem.innerHTML; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { + + value = jQuery.htmlPrefilter( value ); + + try { + for ( ; i < l; i++ ) { + elem = this[ i ] || {}; + + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch ( e ) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var ignored = []; + + // Make the changes, replacing each non-ignored context element with the new content + return domManip( this, arguments, function( elem ) { + var parent = this.parentNode; + + if ( jQuery.inArray( this, ignored ) < 0 ) { + jQuery.cleanData( getAll( this ) ); + if ( parent ) { + parent.replaceChild( elem, this ); + } + } + + // Force callback invocation + }, ignored ); + } +} ); + +jQuery.each( { + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1, + i = 0; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone( true ); + jQuery( insert[ i ] )[ original ]( elems ); + + // Support: Android <=4.0 only, PhantomJS 1 only + // .get() because push.apply(_, arraylike) throws on ancient WebKit + push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +} ); +var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); + +var getStyles = function( elem ) { + + // Support: IE <=11 only, Firefox <=30 (#15098, #14150) + // IE throws on elements created in popups + // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" + var view = elem.ownerDocument.defaultView; + + if ( !view || !view.opener ) { + view = window; + } + + return view.getComputedStyle( elem ); + }; + +var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" ); + + + +( function() { + + // Executing both pixelPosition & boxSizingReliable tests require only one layout + // so they're executed at the same time to save the second computation. + function computeStyleTests() { + + // This is a singleton, we need to execute it only once + if ( !div ) { + return; + } + + container.style.cssText = "position:absolute;left:-11111px;width:60px;" + + "margin-top:1px;padding:0;border:0"; + div.style.cssText = + "position:relative;display:block;box-sizing:border-box;overflow:scroll;" + + "margin:auto;border:1px;padding:1px;" + + "width:60%;top:1%"; + documentElement.appendChild( container ).appendChild( div ); + + var divStyle = window.getComputedStyle( div ); + pixelPositionVal = divStyle.top !== "1%"; + + // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44 + reliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12; + + // Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3 + // Some styles come back with percentage values, even though they shouldn't + div.style.right = "60%"; + pixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36; + + // Support: IE 9 - 11 only + // Detect misreporting of content dimensions for box-sizing:border-box elements + boxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36; + + // Support: IE 9 only + // Detect overflow:scroll screwiness (gh-3699) + div.style.position = "absolute"; + scrollboxSizeVal = div.offsetWidth === 36 || "absolute"; + + documentElement.removeChild( container ); + + // Nullify the div so it wouldn't be stored in the memory and + // it will also be a sign that checks already performed + div = null; + } + + function roundPixelMeasures( measure ) { + return Math.round( parseFloat( measure ) ); + } + + var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal, + reliableMarginLeftVal, + container = document.createElement( "div" ), + div = document.createElement( "div" ); + + // Finish early in limited (non-browser) environments + if ( !div.style ) { + return; + } + + // Support: IE <=9 - 11 only + // Style of cloned element affects source element cloned (#8908) + div.style.backgroundClip = "content-box"; + div.cloneNode( true ).style.backgroundClip = ""; + support.clearCloneStyle = div.style.backgroundClip === "content-box"; + + jQuery.extend( support, { + boxSizingReliable: function() { + computeStyleTests(); + return boxSizingReliableVal; + }, + pixelBoxStyles: function() { + computeStyleTests(); + return pixelBoxStylesVal; + }, + pixelPosition: function() { + computeStyleTests(); + return pixelPositionVal; + }, + reliableMarginLeft: function() { + computeStyleTests(); + return reliableMarginLeftVal; + }, + scrollboxSize: function() { + computeStyleTests(); + return scrollboxSizeVal; + } + } ); +} )(); + + +function curCSS( elem, name, computed ) { + var width, minWidth, maxWidth, ret, + + // Support: Firefox 51+ + // Retrieving style before computed somehow + // fixes an issue with getting wrong values + // on detached elements + style = elem.style; + + computed = computed || getStyles( elem ); + + // getPropertyValue is needed for: + // .css('filter') (IE 9 only, #12537) + // .css('--customProperty) (#3144) + if ( computed ) { + ret = computed.getPropertyValue( name ) || computed[ name ]; + + if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { + ret = jQuery.style( elem, name ); + } + + // A tribute to the "awesome hack by Dean Edwards" + // Android Browser returns percentage for some values, + // but width seems to be reliably pixels. + // This is against the CSSOM draft spec: + // https://drafts.csswg.org/cssom/#resolved-values + if ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) { + + // Remember the original values + width = style.width; + minWidth = style.minWidth; + maxWidth = style.maxWidth; + + // Put in the new values to get a computed value out + style.minWidth = style.maxWidth = style.width = ret; + ret = computed.width; + + // Revert the changed values + style.width = width; + style.minWidth = minWidth; + style.maxWidth = maxWidth; + } + } + + return ret !== undefined ? + + // Support: IE <=9 - 11 only + // IE returns zIndex value as an integer. + ret + "" : + ret; +} + + +function addGetHookIf( conditionFn, hookFn ) { + + // Define the hook, we'll check on the first run if it's really needed. + return { + get: function() { + if ( conditionFn() ) { + + // Hook not needed (or it's not possible to use it due + // to missing dependency), remove it. + delete this.get; + return; + } + + // Hook needed; redefine it so that the support test is not executed again. + return ( this.get = hookFn ).apply( this, arguments ); + } + }; +} + + +var + + // Swappable if display is none or starts with table + // except "table", "table-cell", or "table-caption" + // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + rdisplayswap = /^(none|table(?!-c[ea]).+)/, + rcustomProp = /^--/, + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + cssNormalTransform = { + letterSpacing: "0", + fontWeight: "400" + }, + + cssPrefixes = [ "Webkit", "Moz", "ms" ], + emptyStyle = document.createElement( "div" ).style; + +// Return a css property mapped to a potentially vendor prefixed property +function vendorPropName( name ) { + + // Shortcut for names that are not vendor prefixed + if ( name in emptyStyle ) { + return name; + } + + // Check for vendor prefixed names + var capName = name[ 0 ].toUpperCase() + name.slice( 1 ), + i = cssPrefixes.length; + + while ( i-- ) { + name = cssPrefixes[ i ] + capName; + if ( name in emptyStyle ) { + return name; + } + } +} + +// Return a property mapped along what jQuery.cssProps suggests or to +// a vendor prefixed property. +function finalPropName( name ) { + var ret = jQuery.cssProps[ name ]; + if ( !ret ) { + ret = jQuery.cssProps[ name ] = vendorPropName( name ) || name; + } + return ret; +} + +function setPositiveNumber( elem, value, subtract ) { + + // Any relative (+/-) values have already been + // normalized at this point + var matches = rcssNum.exec( value ); + return matches ? + + // Guard against undefined "subtract", e.g., when used as in cssHooks + Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) : + value; +} + +function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) { + var i = dimension === "width" ? 1 : 0, + extra = 0, + delta = 0; + + // Adjustment may not be necessary + if ( box === ( isBorderBox ? "border" : "content" ) ) { + return 0; + } + + for ( ; i < 4; i += 2 ) { + + // Both box models exclude margin + if ( box === "margin" ) { + delta += jQuery.css( elem, box + cssExpand[ i ], true, styles ); + } + + // If we get here with a content-box, we're seeking "padding" or "border" or "margin" + if ( !isBorderBox ) { + + // Add padding + delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + + // For "border" or "margin", add border + if ( box !== "padding" ) { + delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + + // But still keep track of it otherwise + } else { + extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + + // If we get here with a border-box (content + padding + border), we're seeking "content" or + // "padding" or "margin" + } else { + + // For "content", subtract padding + if ( box === "content" ) { + delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + } + + // For "content" or "padding", subtract border + if ( box !== "margin" ) { + delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } + } + + // Account for positive content-box scroll gutter when requested by providing computedVal + if ( !isBorderBox && computedVal >= 0 ) { + + // offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border + // Assuming integer scroll gutter, subtract the rest and round down + delta += Math.max( 0, Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + computedVal - + delta - + extra - + 0.5 + ) ); + } + + return delta; +} + +function getWidthOrHeight( elem, dimension, extra ) { + + // Start with computed style + var styles = getStyles( elem ), + val = curCSS( elem, dimension, styles ), + isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + valueIsBorderBox = isBorderBox; + + // Support: Firefox <=54 + // Return a confounding non-pixel value or feign ignorance, as appropriate. + if ( rnumnonpx.test( val ) ) { + if ( !extra ) { + return val; + } + val = "auto"; + } + + // Check for style in case a browser which returns unreliable values + // for getComputedStyle silently falls back to the reliable elem.style + valueIsBorderBox = valueIsBorderBox && + ( support.boxSizingReliable() || val === elem.style[ dimension ] ); + + // Fall back to offsetWidth/offsetHeight when value is "auto" + // This happens for inline elements with no explicit setting (gh-3571) + // Support: Android <=4.1 - 4.3 only + // Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602) + if ( val === "auto" || + !parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) { + + val = elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ]; + + // offsetWidth/offsetHeight provide border-box values + valueIsBorderBox = true; + } + + // Normalize "" and auto + val = parseFloat( val ) || 0; + + // Adjust for the element's box model + return ( val + + boxModelAdjustment( + elem, + dimension, + extra || ( isBorderBox ? "border" : "content" ), + valueIsBorderBox, + styles, + + // Provide the current computed size to request scroll gutter calculation (gh-3589) + val + ) + ) + "px"; +} + +jQuery.extend( { + + // Add in style property hooks for overriding the default + // behavior of getting and setting a style property + cssHooks: { + opacity: { + get: function( elem, computed ) { + if ( computed ) { + + // We should always get a number back from opacity + var ret = curCSS( elem, "opacity" ); + return ret === "" ? "1" : ret; + } + } + } + }, + + // Don't automatically add "px" to these possibly-unitless properties + cssNumber: { + "animationIterationCount": true, + "columnCount": true, + "fillOpacity": true, + "flexGrow": true, + "flexShrink": true, + "fontWeight": true, + "lineHeight": true, + "opacity": true, + "order": true, + "orphans": true, + "widows": true, + "zIndex": true, + "zoom": true + }, + + // Add in properties whose names you wish to fix before + // setting or getting the value + cssProps: {}, + + // Get and set the style property on a DOM Node + style: function( elem, name, value, extra ) { + + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + var ret, type, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ), + style = elem.style; + + // Make sure that we're working with the right name. We don't + // want to query the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Gets hook for the prefixed version, then unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // Convert "+=" or "-=" to relative numbers (#7345) + if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) { + value = adjustCSS( elem, name, ret ); + + // Fixes bug #9237 + type = "number"; + } + + // Make sure that null and NaN values aren't set (#7116) + if ( value == null || value !== value ) { + return; + } + + // If a number was passed in, add the unit (except for certain CSS properties) + if ( type === "number" ) { + value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" ); + } + + // background-* props affect original clone's values + if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { + style[ name ] = "inherit"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !( "set" in hooks ) || + ( value = hooks.set( elem, value, extra ) ) !== undefined ) { + + if ( isCustomProp ) { + style.setProperty( name, value ); + } else { + style[ name ] = value; + } + } + + } else { + + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && + ( ret = hooks.get( elem, false, extra ) ) !== undefined ) { + + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }, + + css: function( elem, name, extra, styles ) { + var val, num, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ); + + // Make sure that we're working with the right name. We don't + // want to modify the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Try prefixed name followed by the unprefixed name + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks ) { + val = hooks.get( elem, true, extra ); + } + + // Otherwise, if a way to get the computed value exists, use that + if ( val === undefined ) { + val = curCSS( elem, name, styles ); + } + + // Convert "normal" to computed value + if ( val === "normal" && name in cssNormalTransform ) { + val = cssNormalTransform[ name ]; + } + + // Make numeric if forced or a qualifier was provided and val looks numeric + if ( extra === "" || extra ) { + num = parseFloat( val ); + return extra === true || isFinite( num ) ? num || 0 : val; + } + + return val; + } +} ); + +jQuery.each( [ "height", "width" ], function( i, dimension ) { + jQuery.cssHooks[ dimension ] = { + get: function( elem, computed, extra ) { + if ( computed ) { + + // Certain elements can have dimension info if we invisibly show them + // but it must have a current display style that would benefit + return rdisplayswap.test( jQuery.css( elem, "display" ) ) && + + // Support: Safari 8+ + // Table columns in Safari have non-zero offsetWidth & zero + // getBoundingClientRect().width unless display is changed. + // Support: IE <=11 only + // Running getBoundingClientRect on a disconnected node + // in IE throws an error. + ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? + swap( elem, cssShow, function() { + return getWidthOrHeight( elem, dimension, extra ); + } ) : + getWidthOrHeight( elem, dimension, extra ); + } + }, + + set: function( elem, value, extra ) { + var matches, + styles = getStyles( elem ), + isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + subtract = extra && boxModelAdjustment( + elem, + dimension, + extra, + isBorderBox, + styles + ); + + // Account for unreliable border-box dimensions by comparing offset* to computed and + // faking a content-box to get border and padding (gh-3699) + if ( isBorderBox && support.scrollboxSize() === styles.position ) { + subtract -= Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + parseFloat( styles[ dimension ] ) - + boxModelAdjustment( elem, dimension, "border", false, styles ) - + 0.5 + ); + } + + // Convert to pixels if value adjustment is needed + if ( subtract && ( matches = rcssNum.exec( value ) ) && + ( matches[ 3 ] || "px" ) !== "px" ) { + + elem.style[ dimension ] = value; + value = jQuery.css( elem, dimension ); + } + + return setPositiveNumber( elem, value, subtract ); + } + }; +} ); + +jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft, + function( elem, computed ) { + if ( computed ) { + return ( parseFloat( curCSS( elem, "marginLeft" ) ) || + elem.getBoundingClientRect().left - + swap( elem, { marginLeft: 0 }, function() { + return elem.getBoundingClientRect().left; + } ) + ) + "px"; + } + } +); + +// These hooks are used by animate to expand properties +jQuery.each( { + margin: "", + padding: "", + border: "Width" +}, function( prefix, suffix ) { + jQuery.cssHooks[ prefix + suffix ] = { + expand: function( value ) { + var i = 0, + expanded = {}, + + // Assumes a single number if not a string + parts = typeof value === "string" ? value.split( " " ) : [ value ]; + + for ( ; i < 4; i++ ) { + expanded[ prefix + cssExpand[ i ] + suffix ] = + parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; + } + + return expanded; + } + }; + + if ( prefix !== "margin" ) { + jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; + } +} ); + +jQuery.fn.extend( { + css: function( name, value ) { + return access( this, function( elem, name, value ) { + var styles, len, + map = {}, + i = 0; + + if ( Array.isArray( name ) ) { + styles = getStyles( elem ); + len = name.length; + + for ( ; i < len; i++ ) { + map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); + } + + return map; + } + + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }, name, value, arguments.length > 1 ); + } +} ); + + +function Tween( elem, options, prop, end, easing ) { + return new Tween.prototype.init( elem, options, prop, end, easing ); +} +jQuery.Tween = Tween; + +Tween.prototype = { + constructor: Tween, + init: function( elem, options, prop, end, easing, unit ) { + this.elem = elem; + this.prop = prop; + this.easing = easing || jQuery.easing._default; + this.options = options; + this.start = this.now = this.cur(); + this.end = end; + this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); + }, + cur: function() { + var hooks = Tween.propHooks[ this.prop ]; + + return hooks && hooks.get ? + hooks.get( this ) : + Tween.propHooks._default.get( this ); + }, + run: function( percent ) { + var eased, + hooks = Tween.propHooks[ this.prop ]; + + if ( this.options.duration ) { + this.pos = eased = jQuery.easing[ this.easing ]( + percent, this.options.duration * percent, 0, 1, this.options.duration + ); + } else { + this.pos = eased = percent; + } + this.now = ( this.end - this.start ) * eased + this.start; + + if ( this.options.step ) { + this.options.step.call( this.elem, this.now, this ); + } + + if ( hooks && hooks.set ) { + hooks.set( this ); + } else { + Tween.propHooks._default.set( this ); + } + return this; + } +}; + +Tween.prototype.init.prototype = Tween.prototype; + +Tween.propHooks = { + _default: { + get: function( tween ) { + var result; + + // Use a property on the element directly when it is not a DOM element, + // or when there is no matching style property that exists. + if ( tween.elem.nodeType !== 1 || + tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) { + return tween.elem[ tween.prop ]; + } + + // Passing an empty string as a 3rd parameter to .css will automatically + // attempt a parseFloat and fallback to a string if the parse fails. + // Simple values such as "10px" are parsed to Float; + // complex values such as "rotate(1rad)" are returned as-is. + result = jQuery.css( tween.elem, tween.prop, "" ); + + // Empty strings, null, undefined and "auto" are converted to 0. + return !result || result === "auto" ? 0 : result; + }, + set: function( tween ) { + + // Use step hook for back compat. + // Use cssHook if its there. + // Use .style if available and use plain properties where available. + if ( jQuery.fx.step[ tween.prop ] ) { + jQuery.fx.step[ tween.prop ]( tween ); + } else if ( tween.elem.nodeType === 1 && + ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || + jQuery.cssHooks[ tween.prop ] ) ) { + jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); + } else { + tween.elem[ tween.prop ] = tween.now; + } + } + } +}; + +// Support: IE <=9 only +// Panic based approach to setting things on disconnected nodes +Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { + set: function( tween ) { + if ( tween.elem.nodeType && tween.elem.parentNode ) { + tween.elem[ tween.prop ] = tween.now; + } + } +}; + +jQuery.easing = { + linear: function( p ) { + return p; + }, + swing: function( p ) { + return 0.5 - Math.cos( p * Math.PI ) / 2; + }, + _default: "swing" +}; + +jQuery.fx = Tween.prototype.init; + +// Back compat <1.8 extension point +jQuery.fx.step = {}; + + + + +var + fxNow, inProgress, + rfxtypes = /^(?:toggle|show|hide)$/, + rrun = /queueHooks$/; + +function schedule() { + if ( inProgress ) { + if ( document.hidden === false && window.requestAnimationFrame ) { + window.requestAnimationFrame( schedule ); + } else { + window.setTimeout( schedule, jQuery.fx.interval ); + } + + jQuery.fx.tick(); + } +} + +// Animations created synchronously will run synchronously +function createFxNow() { + window.setTimeout( function() { + fxNow = undefined; + } ); + return ( fxNow = Date.now() ); +} + +// Generate parameters to create a standard animation +function genFx( type, includeWidth ) { + var which, + i = 0, + attrs = { height: type }; + + // If we include width, step value is 1 to do all cssExpand values, + // otherwise step value is 2 to skip over Left and Right + includeWidth = includeWidth ? 1 : 0; + for ( ; i < 4; i += 2 - includeWidth ) { + which = cssExpand[ i ]; + attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; + } + + if ( includeWidth ) { + attrs.opacity = attrs.width = type; + } + + return attrs; +} + +function createTween( value, prop, animation ) { + var tween, + collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ), + index = 0, + length = collection.length; + for ( ; index < length; index++ ) { + if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) { + + // We're done with this property + return tween; + } + } +} + +function defaultPrefilter( elem, props, opts ) { + var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display, + isBox = "width" in props || "height" in props, + anim = this, + orig = {}, + style = elem.style, + hidden = elem.nodeType && isHiddenWithinTree( elem ), + dataShow = dataPriv.get( elem, "fxshow" ); + + // Queue-skipping animations hijack the fx hooks + if ( !opts.queue ) { + hooks = jQuery._queueHooks( elem, "fx" ); + if ( hooks.unqueued == null ) { + hooks.unqueued = 0; + oldfire = hooks.empty.fire; + hooks.empty.fire = function() { + if ( !hooks.unqueued ) { + oldfire(); + } + }; + } + hooks.unqueued++; + + anim.always( function() { + + // Ensure the complete handler is called before this completes + anim.always( function() { + hooks.unqueued--; + if ( !jQuery.queue( elem, "fx" ).length ) { + hooks.empty.fire(); + } + } ); + } ); + } + + // Detect show/hide animations + for ( prop in props ) { + value = props[ prop ]; + if ( rfxtypes.test( value ) ) { + delete props[ prop ]; + toggle = toggle || value === "toggle"; + if ( value === ( hidden ? "hide" : "show" ) ) { + + // Pretend to be hidden if this is a "show" and + // there is still data from a stopped show/hide + if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) { + hidden = true; + + // Ignore all other no-op show/hide data + } else { + continue; + } + } + orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop ); + } + } + + // Bail out if this is a no-op like .hide().hide() + propTween = !jQuery.isEmptyObject( props ); + if ( !propTween && jQuery.isEmptyObject( orig ) ) { + return; + } + + // Restrict "overflow" and "display" styles during box animations + if ( isBox && elem.nodeType === 1 ) { + + // Support: IE <=9 - 11, Edge 12 - 15 + // Record all 3 overflow attributes because IE does not infer the shorthand + // from identically-valued overflowX and overflowY and Edge just mirrors + // the overflowX value there. + opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; + + // Identify a display type, preferring old show/hide data over the CSS cascade + restoreDisplay = dataShow && dataShow.display; + if ( restoreDisplay == null ) { + restoreDisplay = dataPriv.get( elem, "display" ); + } + display = jQuery.css( elem, "display" ); + if ( display === "none" ) { + if ( restoreDisplay ) { + display = restoreDisplay; + } else { + + // Get nonempty value(s) by temporarily forcing visibility + showHide( [ elem ], true ); + restoreDisplay = elem.style.display || restoreDisplay; + display = jQuery.css( elem, "display" ); + showHide( [ elem ] ); + } + } + + // Animate inline elements as inline-block + if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) { + if ( jQuery.css( elem, "float" ) === "none" ) { + + // Restore the original display value at the end of pure show/hide animations + if ( !propTween ) { + anim.done( function() { + style.display = restoreDisplay; + } ); + if ( restoreDisplay == null ) { + display = style.display; + restoreDisplay = display === "none" ? "" : display; + } + } + style.display = "inline-block"; + } + } + } + + if ( opts.overflow ) { + style.overflow = "hidden"; + anim.always( function() { + style.overflow = opts.overflow[ 0 ]; + style.overflowX = opts.overflow[ 1 ]; + style.overflowY = opts.overflow[ 2 ]; + } ); + } + + // Implement show/hide animations + propTween = false; + for ( prop in orig ) { + + // General show/hide setup for this element animation + if ( !propTween ) { + if ( dataShow ) { + if ( "hidden" in dataShow ) { + hidden = dataShow.hidden; + } + } else { + dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } ); + } + + // Store hidden/visible for toggle so `.stop().toggle()` "reverses" + if ( toggle ) { + dataShow.hidden = !hidden; + } + + // Show elements before animating them + if ( hidden ) { + showHide( [ elem ], true ); + } + + /* eslint-disable no-loop-func */ + + anim.done( function() { + + /* eslint-enable no-loop-func */ + + // The final step of a "hide" animation is actually hiding the element + if ( !hidden ) { + showHide( [ elem ] ); + } + dataPriv.remove( elem, "fxshow" ); + for ( prop in orig ) { + jQuery.style( elem, prop, orig[ prop ] ); + } + } ); + } + + // Per-property setup + propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim ); + if ( !( prop in dataShow ) ) { + dataShow[ prop ] = propTween.start; + if ( hidden ) { + propTween.end = propTween.start; + propTween.start = 0; + } + } + } +} + +function propFilter( props, specialEasing ) { + var index, name, easing, value, hooks; + + // camelCase, specialEasing and expand cssHook pass + for ( index in props ) { + name = camelCase( index ); + easing = specialEasing[ name ]; + value = props[ index ]; + if ( Array.isArray( value ) ) { + easing = value[ 1 ]; + value = props[ index ] = value[ 0 ]; + } + + if ( index !== name ) { + props[ name ] = value; + delete props[ index ]; + } + + hooks = jQuery.cssHooks[ name ]; + if ( hooks && "expand" in hooks ) { + value = hooks.expand( value ); + delete props[ name ]; + + // Not quite $.extend, this won't overwrite existing keys. + // Reusing 'index' because we have the correct "name" + for ( index in value ) { + if ( !( index in props ) ) { + props[ index ] = value[ index ]; + specialEasing[ index ] = easing; + } + } + } else { + specialEasing[ name ] = easing; + } + } +} + +function Animation( elem, properties, options ) { + var result, + stopped, + index = 0, + length = Animation.prefilters.length, + deferred = jQuery.Deferred().always( function() { + + // Don't match elem in the :animated selector + delete tick.elem; + } ), + tick = function() { + if ( stopped ) { + return false; + } + var currentTime = fxNow || createFxNow(), + remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), + + // Support: Android 2.3 only + // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497) + temp = remaining / animation.duration || 0, + percent = 1 - temp, + index = 0, + length = animation.tweens.length; + + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( percent ); + } + + deferred.notifyWith( elem, [ animation, percent, remaining ] ); + + // If there's more to do, yield + if ( percent < 1 && length ) { + return remaining; + } + + // If this was an empty animation, synthesize a final progress notification + if ( !length ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + } + + // Resolve the animation and report its conclusion + deferred.resolveWith( elem, [ animation ] ); + return false; + }, + animation = deferred.promise( { + elem: elem, + props: jQuery.extend( {}, properties ), + opts: jQuery.extend( true, { + specialEasing: {}, + easing: jQuery.easing._default + }, options ), + originalProperties: properties, + originalOptions: options, + startTime: fxNow || createFxNow(), + duration: options.duration, + tweens: [], + createTween: function( prop, end ) { + var tween = jQuery.Tween( elem, animation.opts, prop, end, + animation.opts.specialEasing[ prop ] || animation.opts.easing ); + animation.tweens.push( tween ); + return tween; + }, + stop: function( gotoEnd ) { + var index = 0, + + // If we are going to the end, we want to run all the tweens + // otherwise we skip this part + length = gotoEnd ? animation.tweens.length : 0; + if ( stopped ) { + return this; + } + stopped = true; + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( 1 ); + } + + // Resolve when we played the last frame; otherwise, reject + if ( gotoEnd ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + deferred.resolveWith( elem, [ animation, gotoEnd ] ); + } else { + deferred.rejectWith( elem, [ animation, gotoEnd ] ); + } + return this; + } + } ), + props = animation.props; + + propFilter( props, animation.opts.specialEasing ); + + for ( ; index < length; index++ ) { + result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts ); + if ( result ) { + if ( isFunction( result.stop ) ) { + jQuery._queueHooks( animation.elem, animation.opts.queue ).stop = + result.stop.bind( result ); + } + return result; + } + } + + jQuery.map( props, createTween, animation ); + + if ( isFunction( animation.opts.start ) ) { + animation.opts.start.call( elem, animation ); + } + + // Attach callbacks from options + animation + .progress( animation.opts.progress ) + .done( animation.opts.done, animation.opts.complete ) + .fail( animation.opts.fail ) + .always( animation.opts.always ); + + jQuery.fx.timer( + jQuery.extend( tick, { + elem: elem, + anim: animation, + queue: animation.opts.queue + } ) + ); + + return animation; +} + +jQuery.Animation = jQuery.extend( Animation, { + + tweeners: { + "*": [ function( prop, value ) { + var tween = this.createTween( prop, value ); + adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween ); + return tween; + } ] + }, + + tweener: function( props, callback ) { + if ( isFunction( props ) ) { + callback = props; + props = [ "*" ]; + } else { + props = props.match( rnothtmlwhite ); + } + + var prop, + index = 0, + length = props.length; + + for ( ; index < length; index++ ) { + prop = props[ index ]; + Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || []; + Animation.tweeners[ prop ].unshift( callback ); + } + }, + + prefilters: [ defaultPrefilter ], + + prefilter: function( callback, prepend ) { + if ( prepend ) { + Animation.prefilters.unshift( callback ); + } else { + Animation.prefilters.push( callback ); + } + } +} ); + +jQuery.speed = function( speed, easing, fn ) { + var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { + complete: fn || !fn && easing || + isFunction( speed ) && speed, + duration: speed, + easing: fn && easing || easing && !isFunction( easing ) && easing + }; + + // Go to the end state if fx are off + if ( jQuery.fx.off ) { + opt.duration = 0; + + } else { + if ( typeof opt.duration !== "number" ) { + if ( opt.duration in jQuery.fx.speeds ) { + opt.duration = jQuery.fx.speeds[ opt.duration ]; + + } else { + opt.duration = jQuery.fx.speeds._default; + } + } + } + + // Normalize opt.queue - true/undefined/null -> "fx" + if ( opt.queue == null || opt.queue === true ) { + opt.queue = "fx"; + } + + // Queueing + opt.old = opt.complete; + + opt.complete = function() { + if ( isFunction( opt.old ) ) { + opt.old.call( this ); + } + + if ( opt.queue ) { + jQuery.dequeue( this, opt.queue ); + } + }; + + return opt; +}; + +jQuery.fn.extend( { + fadeTo: function( speed, to, easing, callback ) { + + // Show any hidden elements after setting opacity to 0 + return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show() + + // Animate to the value specified + .end().animate( { opacity: to }, speed, easing, callback ); + }, + animate: function( prop, speed, easing, callback ) { + var empty = jQuery.isEmptyObject( prop ), + optall = jQuery.speed( speed, easing, callback ), + doAnimation = function() { + + // Operate on a copy of prop so per-property easing won't be lost + var anim = Animation( this, jQuery.extend( {}, prop ), optall ); + + // Empty animations, or finishing resolves immediately + if ( empty || dataPriv.get( this, "finish" ) ) { + anim.stop( true ); + } + }; + doAnimation.finish = doAnimation; + + return empty || optall.queue === false ? + this.each( doAnimation ) : + this.queue( optall.queue, doAnimation ); + }, + stop: function( type, clearQueue, gotoEnd ) { + var stopQueue = function( hooks ) { + var stop = hooks.stop; + delete hooks.stop; + stop( gotoEnd ); + }; + + if ( typeof type !== "string" ) { + gotoEnd = clearQueue; + clearQueue = type; + type = undefined; + } + if ( clearQueue && type !== false ) { + this.queue( type || "fx", [] ); + } + + return this.each( function() { + var dequeue = true, + index = type != null && type + "queueHooks", + timers = jQuery.timers, + data = dataPriv.get( this ); + + if ( index ) { + if ( data[ index ] && data[ index ].stop ) { + stopQueue( data[ index ] ); + } + } else { + for ( index in data ) { + if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { + stopQueue( data[ index ] ); + } + } + } + + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && + ( type == null || timers[ index ].queue === type ) ) { + + timers[ index ].anim.stop( gotoEnd ); + dequeue = false; + timers.splice( index, 1 ); + } + } + + // Start the next in the queue if the last step wasn't forced. + // Timers currently will call their complete callbacks, which + // will dequeue but only if they were gotoEnd. + if ( dequeue || !gotoEnd ) { + jQuery.dequeue( this, type ); + } + } ); + }, + finish: function( type ) { + if ( type !== false ) { + type = type || "fx"; + } + return this.each( function() { + var index, + data = dataPriv.get( this ), + queue = data[ type + "queue" ], + hooks = data[ type + "queueHooks" ], + timers = jQuery.timers, + length = queue ? queue.length : 0; + + // Enable finishing flag on private data + data.finish = true; + + // Empty the queue first + jQuery.queue( this, type, [] ); + + if ( hooks && hooks.stop ) { + hooks.stop.call( this, true ); + } + + // Look for any active animations, and finish them + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && timers[ index ].queue === type ) { + timers[ index ].anim.stop( true ); + timers.splice( index, 1 ); + } + } + + // Look for any animations in the old queue and finish them + for ( index = 0; index < length; index++ ) { + if ( queue[ index ] && queue[ index ].finish ) { + queue[ index ].finish.call( this ); + } + } + + // Turn off finishing flag + delete data.finish; + } ); + } +} ); + +jQuery.each( [ "toggle", "show", "hide" ], function( i, name ) { + var cssFn = jQuery.fn[ name ]; + jQuery.fn[ name ] = function( speed, easing, callback ) { + return speed == null || typeof speed === "boolean" ? + cssFn.apply( this, arguments ) : + this.animate( genFx( name, true ), speed, easing, callback ); + }; +} ); + +// Generate shortcuts for custom animations +jQuery.each( { + slideDown: genFx( "show" ), + slideUp: genFx( "hide" ), + slideToggle: genFx( "toggle" ), + fadeIn: { opacity: "show" }, + fadeOut: { opacity: "hide" }, + fadeToggle: { opacity: "toggle" } +}, function( name, props ) { + jQuery.fn[ name ] = function( speed, easing, callback ) { + return this.animate( props, speed, easing, callback ); + }; +} ); + +jQuery.timers = []; +jQuery.fx.tick = function() { + var timer, + i = 0, + timers = jQuery.timers; + + fxNow = Date.now(); + + for ( ; i < timers.length; i++ ) { + timer = timers[ i ]; + + // Run the timer and safely remove it when done (allowing for external removal) + if ( !timer() && timers[ i ] === timer ) { + timers.splice( i--, 1 ); + } + } + + if ( !timers.length ) { + jQuery.fx.stop(); + } + fxNow = undefined; +}; + +jQuery.fx.timer = function( timer ) { + jQuery.timers.push( timer ); + jQuery.fx.start(); +}; + +jQuery.fx.interval = 13; +jQuery.fx.start = function() { + if ( inProgress ) { + return; + } + + inProgress = true; + schedule(); +}; + +jQuery.fx.stop = function() { + inProgress = null; +}; + +jQuery.fx.speeds = { + slow: 600, + fast: 200, + + // Default speed + _default: 400 +}; + + +// Based off of the plugin by Clint Helfers, with permission. +// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/ +jQuery.fn.delay = function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = window.setTimeout( next, time ); + hooks.stop = function() { + window.clearTimeout( timeout ); + }; + } ); +}; + + +( function() { + var input = document.createElement( "input" ), + select = document.createElement( "select" ), + opt = select.appendChild( document.createElement( "option" ) ); + + input.type = "checkbox"; + + // Support: Android <=4.3 only + // Default value for a checkbox should be "on" + support.checkOn = input.value !== ""; + + // Support: IE <=11 only + // Must access selectedIndex to make default options select + support.optSelected = opt.selected; + + // Support: IE <=11 only + // An input loses its value after becoming a radio + input = document.createElement( "input" ); + input.value = "t"; + input.type = "radio"; + support.radioValue = input.value === "t"; +} )(); + + +var boolHook, + attrHandle = jQuery.expr.attrHandle; + +jQuery.fn.extend( { + attr: function( name, value ) { + return access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each( function() { + jQuery.removeAttr( this, name ); + } ); + } +} ); + +jQuery.extend( { + attr: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set attributes on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === "undefined" ) { + return jQuery.prop( elem, name, value ); + } + + // Attribute hooks are determined by the lowercase version + // Grab necessary hook if one is defined + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + hooks = jQuery.attrHooks[ name.toLowerCase() ] || + ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined ); + } + + if ( value !== undefined ) { + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return; + } + + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + elem.setAttribute( name, value + "" ); + return value; + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + ret = jQuery.find.attr( elem, name ); + + // Non-existent attributes return null, we normalize to undefined + return ret == null ? undefined : ret; + }, + + attrHooks: { + type: { + set: function( elem, value ) { + if ( !support.radioValue && value === "radio" && + nodeName( elem, "input" ) ) { + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + } + }, + + removeAttr: function( elem, value ) { + var name, + i = 0, + + // Attribute names can contain non-HTML whitespace characters + // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 + attrNames = value && value.match( rnothtmlwhite ); + + if ( attrNames && elem.nodeType === 1 ) { + while ( ( name = attrNames[ i++ ] ) ) { + elem.removeAttribute( name ); + } + } + } +} ); + +// Hooks for boolean attributes +boolHook = { + set: function( elem, value, name ) { + if ( value === false ) { + + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + elem.setAttribute( name, name ); + } + return name; + } +}; + +jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) { + var getter = attrHandle[ name ] || jQuery.find.attr; + + attrHandle[ name ] = function( elem, name, isXML ) { + var ret, handle, + lowercaseName = name.toLowerCase(); + + if ( !isXML ) { + + // Avoid an infinite loop by temporarily removing this function from the getter + handle = attrHandle[ lowercaseName ]; + attrHandle[ lowercaseName ] = ret; + ret = getter( elem, name, isXML ) != null ? + lowercaseName : + null; + attrHandle[ lowercaseName ] = handle; + } + return ret; + }; +} ); + + + + +var rfocusable = /^(?:input|select|textarea|button)$/i, + rclickable = /^(?:a|area)$/i; + +jQuery.fn.extend( { + prop: function( name, value ) { + return access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + return this.each( function() { + delete this[ jQuery.propFix[ name ] || name ]; + } ); + } +} ); + +jQuery.extend( { + prop: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set properties on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + return ( elem[ name ] = value ); + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + return elem[ name ]; + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + + // Support: IE <=9 - 11 only + // elem.tabIndex doesn't always return the + // correct value when it hasn't been explicitly set + // https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + // Use proper attribute retrieval(#12072) + var tabindex = jQuery.find.attr( elem, "tabindex" ); + + if ( tabindex ) { + return parseInt( tabindex, 10 ); + } + + if ( + rfocusable.test( elem.nodeName ) || + rclickable.test( elem.nodeName ) && + elem.href + ) { + return 0; + } + + return -1; + } + } + }, + + propFix: { + "for": "htmlFor", + "class": "className" + } +} ); + +// Support: IE <=11 only +// Accessing the selectedIndex property +// forces the browser to respect setting selected +// on the option +// The getter ensures a default option is selected +// when in an optgroup +// eslint rule "no-unused-expressions" is disabled for this code +// since it considers such accessions noop +if ( !support.optSelected ) { + jQuery.propHooks.selected = { + get: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent && parent.parentNode ) { + parent.parentNode.selectedIndex; + } + return null; + }, + set: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent ) { + parent.selectedIndex; + + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + } + }; +} + +jQuery.each( [ + "tabIndex", + "readOnly", + "maxLength", + "cellSpacing", + "cellPadding", + "rowSpan", + "colSpan", + "useMap", + "frameBorder", + "contentEditable" +], function() { + jQuery.propFix[ this.toLowerCase() ] = this; +} ); + + + + + // Strip and collapse whitespace according to HTML spec + // https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace + function stripAndCollapse( value ) { + var tokens = value.match( rnothtmlwhite ) || []; + return tokens.join( " " ); + } + + +function getClass( elem ) { + return elem.getAttribute && elem.getAttribute( "class" ) || ""; +} + +function classesToArray( value ) { + if ( Array.isArray( value ) ) { + return value; + } + if ( typeof value === "string" ) { + return value.match( rnothtmlwhite ) || []; + } + return []; +} + +jQuery.fn.extend( { + addClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).addClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + classes = classesToArray( value ); + + if ( classes.length ) { + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + if ( cur.indexOf( " " + clazz + " " ) < 0 ) { + cur += clazz + " "; + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + if ( !arguments.length ) { + return this.attr( "class", "" ); + } + + classes = classesToArray( value ); + + if ( classes.length ) { + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + + // This expression is here for better compressibility (see addClass) + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + + // Remove *all* instances + while ( cur.indexOf( " " + clazz + " " ) > -1 ) { + cur = cur.replace( " " + clazz + " ", " " ); + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isValidValue = type === "string" || Array.isArray( value ); + + if ( typeof stateVal === "boolean" && isValidValue ) { + return stateVal ? this.addClass( value ) : this.removeClass( value ); + } + + if ( isFunction( value ) ) { + return this.each( function( i ) { + jQuery( this ).toggleClass( + value.call( this, i, getClass( this ), stateVal ), + stateVal + ); + } ); + } + + return this.each( function() { + var className, i, self, classNames; + + if ( isValidValue ) { + + // Toggle individual class names + i = 0; + self = jQuery( this ); + classNames = classesToArray( value ); + + while ( ( className = classNames[ i++ ] ) ) { + + // Check each className given, space separated list + if ( self.hasClass( className ) ) { + self.removeClass( className ); + } else { + self.addClass( className ); + } + } + + // Toggle whole class name + } else if ( value === undefined || type === "boolean" ) { + className = getClass( this ); + if ( className ) { + + // Store className if set + dataPriv.set( this, "__className__", className ); + } + + // If the element has a class name or if we're passed `false`, + // then remove the whole classname (if there was one, the above saved it). + // Otherwise bring back whatever was previously saved (if anything), + // falling back to the empty string if nothing was stored. + if ( this.setAttribute ) { + this.setAttribute( "class", + className || value === false ? + "" : + dataPriv.get( this, "__className__" ) || "" + ); + } + } + } ); + }, + + hasClass: function( selector ) { + var className, elem, + i = 0; + + className = " " + selector + " "; + while ( ( elem = this[ i++ ] ) ) { + if ( elem.nodeType === 1 && + ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) { + return true; + } + } + + return false; + } +} ); + + + + +var rreturn = /\r/g; + +jQuery.fn.extend( { + val: function( value ) { + var hooks, ret, valueIsFunction, + elem = this[ 0 ]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || + jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && + "get" in hooks && + ( ret = hooks.get( elem, "value" ) ) !== undefined + ) { + return ret; + } + + ret = elem.value; + + // Handle most common string cases + if ( typeof ret === "string" ) { + return ret.replace( rreturn, "" ); + } + + // Handle cases where value is null/undef or number + return ret == null ? "" : ret; + } + + return; + } + + valueIsFunction = isFunction( value ); + + return this.each( function( i ) { + var val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( valueIsFunction ) { + val = value.call( this, i, jQuery( this ).val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + + } else if ( typeof val === "number" ) { + val += ""; + + } else if ( Array.isArray( val ) ) { + val = jQuery.map( val, function( value ) { + return value == null ? "" : value + ""; + } ); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + } ); + } +} ); + +jQuery.extend( { + valHooks: { + option: { + get: function( elem ) { + + var val = jQuery.find.attr( elem, "value" ); + return val != null ? + val : + + // Support: IE <=10 - 11 only + // option.text throws exceptions (#14686, #14858) + // Strip and collapse whitespace + // https://html.spec.whatwg.org/#strip-and-collapse-whitespace + stripAndCollapse( jQuery.text( elem ) ); + } + }, + select: { + get: function( elem ) { + var value, option, i, + options = elem.options, + index = elem.selectedIndex, + one = elem.type === "select-one", + values = one ? null : [], + max = one ? index + 1 : options.length; + + if ( index < 0 ) { + i = max; + + } else { + i = one ? index : 0; + } + + // Loop through all the selected options + for ( ; i < max; i++ ) { + option = options[ i ]; + + // Support: IE <=9 only + // IE8-9 doesn't update selected after form reset (#2551) + if ( ( option.selected || i === index ) && + + // Don't return options that are disabled or in a disabled optgroup + !option.disabled && + ( !option.parentNode.disabled || + !nodeName( option.parentNode, "optgroup" ) ) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + }, + + set: function( elem, value ) { + var optionSet, option, + options = elem.options, + values = jQuery.makeArray( value ), + i = options.length; + + while ( i-- ) { + option = options[ i ]; + + /* eslint-disable no-cond-assign */ + + if ( option.selected = + jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1 + ) { + optionSet = true; + } + + /* eslint-enable no-cond-assign */ + } + + // Force browsers to behave consistently when non-matching value is set + if ( !optionSet ) { + elem.selectedIndex = -1; + } + return values; + } + } + } +} ); + +// Radios and checkboxes getter/setter +jQuery.each( [ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + set: function( elem, value ) { + if ( Array.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 ); + } + } + }; + if ( !support.checkOn ) { + jQuery.valHooks[ this ].get = function( elem ) { + return elem.getAttribute( "value" ) === null ? "on" : elem.value; + }; + } +} ); + + + + +// Return jQuery for attributes-only inclusion + + +support.focusin = "onfocusin" in window; + + +var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + stopPropagationCallback = function( e ) { + e.stopPropagation(); + }; + +jQuery.extend( jQuery.event, { + + trigger: function( event, data, elem, onlyHandlers ) { + + var i, cur, tmp, bubbleType, ontype, handle, special, lastElement, + eventPath = [ elem || document ], + type = hasOwn.call( event, "type" ) ? event.type : event, + namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; + + cur = lastElement = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf( "." ) > -1 ) { + + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split( "." ); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf( ":" ) < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) + event.isTrigger = onlyHandlers ? 2 : 3; + event.namespace = namespaces.join( "." ); + event.rnamespace = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === ( elem.ownerDocument || document ) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { + lastElement = cur; + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( dataPriv.get( cur, "events" ) || {} )[ event.type ] && + dataPriv.get( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && handle.apply && acceptData( cur ) ) { + event.result = handle.apply( cur, data ); + if ( event.result === false ) { + event.preventDefault(); + } + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( ( !special._default || + special._default.apply( eventPath.pop(), data ) === false ) && + acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name as the event. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + + if ( event.isPropagationStopped() ) { + lastElement.addEventListener( type, stopPropagationCallback ); + } + + elem[ type ](); + + if ( event.isPropagationStopped() ) { + lastElement.removeEventListener( type, stopPropagationCallback ); + } + + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + // Piggyback on a donor event to simulate a different one + // Used only for `focus(in | out)` events + simulate: function( type, elem, event ) { + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true + } + ); + + jQuery.event.trigger( e, null, elem ); + } + +} ); + +jQuery.fn.extend( { + + trigger: function( type, data ) { + return this.each( function() { + jQuery.event.trigger( type, data, this ); + } ); + }, + triggerHandler: function( type, data ) { + var elem = this[ 0 ]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } +} ); + + +// Support: Firefox <=44 +// Firefox doesn't have focus(in | out) events +// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 +// +// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1 +// focus(in | out) events fire after focus & blur events, +// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order +// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857 +if ( !support.focusin ) { + jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler on the document while someone wants focusin/focusout + var handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + var doc = this.ownerDocument || this, + attaches = dataPriv.access( doc, fix ); + + if ( !attaches ) { + doc.addEventListener( orig, handler, true ); + } + dataPriv.access( doc, fix, ( attaches || 0 ) + 1 ); + }, + teardown: function() { + var doc = this.ownerDocument || this, + attaches = dataPriv.access( doc, fix ) - 1; + + if ( !attaches ) { + doc.removeEventListener( orig, handler, true ); + dataPriv.remove( doc, fix ); + + } else { + dataPriv.access( doc, fix, attaches ); + } + } + }; + } ); +} +var location = window.location; + +var nonce = Date.now(); + +var rquery = ( /\?/ ); + + + +// Cross-browser xml parsing +jQuery.parseXML = function( data ) { + var xml; + if ( !data || typeof data !== "string" ) { + return null; + } + + // Support: IE 9 - 11 only + // IE throws on parseFromString with invalid input. + try { + xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" ); + } catch ( e ) { + xml = undefined; + } + + if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; +}; + + +var + rbracket = /\[\]$/, + rCRLF = /\r?\n/g, + rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, + rsubmittable = /^(?:input|select|textarea|keygen)/i; + +function buildParams( prefix, obj, traditional, add ) { + var name; + + if ( Array.isArray( obj ) ) { + + // Serialize array item. + jQuery.each( obj, function( i, v ) { + if ( traditional || rbracket.test( prefix ) ) { + + // Treat each array item as a scalar. + add( prefix, v ); + + } else { + + // Item is non-scalar (array or object), encode its numeric index. + buildParams( + prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]", + v, + traditional, + add + ); + } + } ); + + } else if ( !traditional && toType( obj ) === "object" ) { + + // Serialize object item. + for ( name in obj ) { + buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); + } + + } else { + + // Serialize scalar item. + add( prefix, obj ); + } +} + +// Serialize an array of form elements or a set of +// key/values into a query string +jQuery.param = function( a, traditional ) { + var prefix, + s = [], + add = function( key, valueOrFunction ) { + + // If value is a function, invoke it and use its return value + var value = isFunction( valueOrFunction ) ? + valueOrFunction() : + valueOrFunction; + + s[ s.length ] = encodeURIComponent( key ) + "=" + + encodeURIComponent( value == null ? "" : value ); + }; + + // If an array was passed in, assume that it is an array of form elements. + if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { + + // Serialize the form elements + jQuery.each( a, function() { + add( this.name, this.value ); + } ); + + } else { + + // If traditional, encode the "old" way (the way 1.3.2 or older + // did it), otherwise encode params recursively. + for ( prefix in a ) { + buildParams( prefix, a[ prefix ], traditional, add ); + } + } + + // Return the resulting serialization + return s.join( "&" ); +}; + +jQuery.fn.extend( { + serialize: function() { + return jQuery.param( this.serializeArray() ); + }, + serializeArray: function() { + return this.map( function() { + + // Can add propHook for "elements" to filter or add form elements + var elements = jQuery.prop( this, "elements" ); + return elements ? jQuery.makeArray( elements ) : this; + } ) + .filter( function() { + var type = this.type; + + // Use .is( ":disabled" ) so that fieldset[disabled] works + return this.name && !jQuery( this ).is( ":disabled" ) && + rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && + ( this.checked || !rcheckableType.test( type ) ); + } ) + .map( function( i, elem ) { + var val = jQuery( this ).val(); + + if ( val == null ) { + return null; + } + + if ( Array.isArray( val ) ) { + return jQuery.map( val, function( val ) { + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ); + } + + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ).get(); + } +} ); + + +var + r20 = /%20/g, + rhash = /#.*$/, + rantiCache = /([?&])_=[^&]*/, + rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, + + // #7653, #8125, #8152: local protocol detection + rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, + rnoContent = /^(?:GET|HEAD)$/, + rprotocol = /^\/\//, + + /* Prefilters + * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) + * 2) These are called: + * - BEFORE asking for a transport + * - AFTER param serialization (s.data is a string if s.processData is true) + * 3) key is the dataType + * 4) the catchall symbol "*" can be used + * 5) execution will start with transport dataType and THEN continue down to "*" if needed + */ + prefilters = {}, + + /* Transports bindings + * 1) key is the dataType + * 2) the catchall symbol "*" can be used + * 3) selection will start with transport dataType and THEN go to "*" if needed + */ + transports = {}, + + // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression + allTypes = "*/".concat( "*" ), + + // Anchor tag for parsing the document origin + originAnchor = document.createElement( "a" ); + originAnchor.href = location.href; + +// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport +function addToPrefiltersOrTransports( structure ) { + + // dataTypeExpression is optional and defaults to "*" + return function( dataTypeExpression, func ) { + + if ( typeof dataTypeExpression !== "string" ) { + func = dataTypeExpression; + dataTypeExpression = "*"; + } + + var dataType, + i = 0, + dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || []; + + if ( isFunction( func ) ) { + + // For each dataType in the dataTypeExpression + while ( ( dataType = dataTypes[ i++ ] ) ) { + + // Prepend if requested + if ( dataType[ 0 ] === "+" ) { + dataType = dataType.slice( 1 ) || "*"; + ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func ); + + // Otherwise append + } else { + ( structure[ dataType ] = structure[ dataType ] || [] ).push( func ); + } + } + } + }; +} + +// Base inspection function for prefilters and transports +function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { + + var inspected = {}, + seekingTransport = ( structure === transports ); + + function inspect( dataType ) { + var selected; + inspected[ dataType ] = true; + jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { + var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); + if ( typeof dataTypeOrTransport === "string" && + !seekingTransport && !inspected[ dataTypeOrTransport ] ) { + + options.dataTypes.unshift( dataTypeOrTransport ); + inspect( dataTypeOrTransport ); + return false; + } else if ( seekingTransport ) { + return !( selected = dataTypeOrTransport ); + } + } ); + return selected; + } + + return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" ); +} + +// A special extend for ajax options +// that takes "flat" options (not to be deep extended) +// Fixes #9887 +function ajaxExtend( target, src ) { + var key, deep, + flatOptions = jQuery.ajaxSettings.flatOptions || {}; + + for ( key in src ) { + if ( src[ key ] !== undefined ) { + ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; + } + } + if ( deep ) { + jQuery.extend( true, target, deep ); + } + + return target; +} + +/* Handles responses to an ajax request: + * - finds the right dataType (mediates between content-type and expected dataType) + * - returns the corresponding response + */ +function ajaxHandleResponses( s, jqXHR, responses ) { + + var ct, type, finalDataType, firstDataType, + contents = s.contents, + dataTypes = s.dataTypes; + + // Remove auto dataType and get content-type in the process + while ( dataTypes[ 0 ] === "*" ) { + dataTypes.shift(); + if ( ct === undefined ) { + ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" ); + } + } + + // Check if we're dealing with a known content-type + if ( ct ) { + for ( type in contents ) { + if ( contents[ type ] && contents[ type ].test( ct ) ) { + dataTypes.unshift( type ); + break; + } + } + } + + // Check to see if we have a response for the expected dataType + if ( dataTypes[ 0 ] in responses ) { + finalDataType = dataTypes[ 0 ]; + } else { + + // Try convertible dataTypes + for ( type in responses ) { + if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) { + finalDataType = type; + break; + } + if ( !firstDataType ) { + firstDataType = type; + } + } + + // Or just use first one + finalDataType = finalDataType || firstDataType; + } + + // If we found a dataType + // We add the dataType to the list if needed + // and return the corresponding response + if ( finalDataType ) { + if ( finalDataType !== dataTypes[ 0 ] ) { + dataTypes.unshift( finalDataType ); + } + return responses[ finalDataType ]; + } +} + +/* Chain conversions given the request and the original response + * Also sets the responseXXX fields on the jqXHR instance + */ +function ajaxConvert( s, response, jqXHR, isSuccess ) { + var conv2, current, conv, tmp, prev, + converters = {}, + + // Work with a copy of dataTypes in case we need to modify it for conversion + dataTypes = s.dataTypes.slice(); + + // Create converters map with lowercased keys + if ( dataTypes[ 1 ] ) { + for ( conv in s.converters ) { + converters[ conv.toLowerCase() ] = s.converters[ conv ]; + } + } + + current = dataTypes.shift(); + + // Convert to each sequential dataType + while ( current ) { + + if ( s.responseFields[ current ] ) { + jqXHR[ s.responseFields[ current ] ] = response; + } + + // Apply the dataFilter if provided + if ( !prev && isSuccess && s.dataFilter ) { + response = s.dataFilter( response, s.dataType ); + } + + prev = current; + current = dataTypes.shift(); + + if ( current ) { + + // There's only work to do if current dataType is non-auto + if ( current === "*" ) { + + current = prev; + + // Convert response if prev dataType is non-auto and differs from current + } else if ( prev !== "*" && prev !== current ) { + + // Seek a direct converter + conv = converters[ prev + " " + current ] || converters[ "* " + current ]; + + // If none found, seek a pair + if ( !conv ) { + for ( conv2 in converters ) { + + // If conv2 outputs current + tmp = conv2.split( " " ); + if ( tmp[ 1 ] === current ) { + + // If prev can be converted to accepted input + conv = converters[ prev + " " + tmp[ 0 ] ] || + converters[ "* " + tmp[ 0 ] ]; + if ( conv ) { + + // Condense equivalence converters + if ( conv === true ) { + conv = converters[ conv2 ]; + + // Otherwise, insert the intermediate dataType + } else if ( converters[ conv2 ] !== true ) { + current = tmp[ 0 ]; + dataTypes.unshift( tmp[ 1 ] ); + } + break; + } + } + } + } + + // Apply converter (if not an equivalence) + if ( conv !== true ) { + + // Unless errors are allowed to bubble, catch and return them + if ( conv && s.throws ) { + response = conv( response ); + } else { + try { + response = conv( response ); + } catch ( e ) { + return { + state: "parsererror", + error: conv ? e : "No conversion from " + prev + " to " + current + }; + } + } + } + } + } + } + + return { state: "success", data: response }; +} + +jQuery.extend( { + + // Counter for holding the number of active queries + active: 0, + + // Last-Modified header cache for next request + lastModified: {}, + etag: {}, + + ajaxSettings: { + url: location.href, + type: "GET", + isLocal: rlocalProtocol.test( location.protocol ), + global: true, + processData: true, + async: true, + contentType: "application/x-www-form-urlencoded; charset=UTF-8", + + /* + timeout: 0, + data: null, + dataType: null, + username: null, + password: null, + cache: null, + throws: false, + traditional: false, + headers: {}, + */ + + accepts: { + "*": allTypes, + text: "text/plain", + html: "text/html", + xml: "application/xml, text/xml", + json: "application/json, text/javascript" + }, + + contents: { + xml: /\bxml\b/, + html: /\bhtml/, + json: /\bjson\b/ + }, + + responseFields: { + xml: "responseXML", + text: "responseText", + json: "responseJSON" + }, + + // Data converters + // Keys separate source (or catchall "*") and destination types with a single space + converters: { + + // Convert anything to text + "* text": String, + + // Text to html (true = no transformation) + "text html": true, + + // Evaluate text as a json expression + "text json": JSON.parse, + + // Parse text as xml + "text xml": jQuery.parseXML + }, + + // For options that shouldn't be deep extended: + // you can add your own custom options here if + // and when you create one that shouldn't be + // deep extended (see ajaxExtend) + flatOptions: { + url: true, + context: true + } + }, + + // Creates a full fledged settings object into target + // with both ajaxSettings and settings fields. + // If target is omitted, writes into ajaxSettings. + ajaxSetup: function( target, settings ) { + return settings ? + + // Building a settings object + ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : + + // Extending ajaxSettings + ajaxExtend( jQuery.ajaxSettings, target ); + }, + + ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), + ajaxTransport: addToPrefiltersOrTransports( transports ), + + // Main method + ajax: function( url, options ) { + + // If url is an object, simulate pre-1.5 signature + if ( typeof url === "object" ) { + options = url; + url = undefined; + } + + // Force options to be an object + options = options || {}; + + var transport, + + // URL without anti-cache param + cacheURL, + + // Response headers + responseHeadersString, + responseHeaders, + + // timeout handle + timeoutTimer, + + // Url cleanup var + urlAnchor, + + // Request state (becomes false upon send and true upon completion) + completed, + + // To know if global events are to be dispatched + fireGlobals, + + // Loop variable + i, + + // uncached part of the url + uncached, + + // Create the final options object + s = jQuery.ajaxSetup( {}, options ), + + // Callbacks context + callbackContext = s.context || s, + + // Context for global events is callbackContext if it is a DOM node or jQuery collection + globalEventContext = s.context && + ( callbackContext.nodeType || callbackContext.jquery ) ? + jQuery( callbackContext ) : + jQuery.event, + + // Deferreds + deferred = jQuery.Deferred(), + completeDeferred = jQuery.Callbacks( "once memory" ), + + // Status-dependent callbacks + statusCode = s.statusCode || {}, + + // Headers (they are sent all at once) + requestHeaders = {}, + requestHeadersNames = {}, + + // Default abort message + strAbort = "canceled", + + // Fake xhr + jqXHR = { + readyState: 0, + + // Builds headers hashtable if needed + getResponseHeader: function( key ) { + var match; + if ( completed ) { + if ( !responseHeaders ) { + responseHeaders = {}; + while ( ( match = rheaders.exec( responseHeadersString ) ) ) { + responseHeaders[ match[ 1 ].toLowerCase() ] = match[ 2 ]; + } + } + match = responseHeaders[ key.toLowerCase() ]; + } + return match == null ? null : match; + }, + + // Raw string + getAllResponseHeaders: function() { + return completed ? responseHeadersString : null; + }, + + // Caches the header + setRequestHeader: function( name, value ) { + if ( completed == null ) { + name = requestHeadersNames[ name.toLowerCase() ] = + requestHeadersNames[ name.toLowerCase() ] || name; + requestHeaders[ name ] = value; + } + return this; + }, + + // Overrides response content-type header + overrideMimeType: function( type ) { + if ( completed == null ) { + s.mimeType = type; + } + return this; + }, + + // Status-dependent callbacks + statusCode: function( map ) { + var code; + if ( map ) { + if ( completed ) { + + // Execute the appropriate callbacks + jqXHR.always( map[ jqXHR.status ] ); + } else { + + // Lazy-add the new callbacks in a way that preserves old ones + for ( code in map ) { + statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; + } + } + } + return this; + }, + + // Cancel the request + abort: function( statusText ) { + var finalText = statusText || strAbort; + if ( transport ) { + transport.abort( finalText ); + } + done( 0, finalText ); + return this; + } + }; + + // Attach deferreds + deferred.promise( jqXHR ); + + // Add protocol if not provided (prefilters might expect it) + // Handle falsy url in the settings object (#10093: consistency with old signature) + // We also use the url parameter if available + s.url = ( ( url || s.url || location.href ) + "" ) + .replace( rprotocol, location.protocol + "//" ); + + // Alias method option to type as per ticket #12004 + s.type = options.method || options.type || s.method || s.type; + + // Extract dataTypes list + s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ]; + + // A cross-domain request is in order when the origin doesn't match the current origin. + if ( s.crossDomain == null ) { + urlAnchor = document.createElement( "a" ); + + // Support: IE <=8 - 11, Edge 12 - 15 + // IE throws exception on accessing the href property if url is malformed, + // e.g. http://example.com:80x/ + try { + urlAnchor.href = s.url; + + // Support: IE <=8 - 11 only + // Anchor's host property isn't correctly set when s.url is relative + urlAnchor.href = urlAnchor.href; + s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !== + urlAnchor.protocol + "//" + urlAnchor.host; + } catch ( e ) { + + // If there is an error parsing the URL, assume it is crossDomain, + // it can be rejected by the transport if it is invalid + s.crossDomain = true; + } + } + + // Convert data if not already a string + if ( s.data && s.processData && typeof s.data !== "string" ) { + s.data = jQuery.param( s.data, s.traditional ); + } + + // Apply prefilters + inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); + + // If request was aborted inside a prefilter, stop there + if ( completed ) { + return jqXHR; + } + + // We can fire global events as of now if asked to + // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118) + fireGlobals = jQuery.event && s.global; + + // Watch for a new set of requests + if ( fireGlobals && jQuery.active++ === 0 ) { + jQuery.event.trigger( "ajaxStart" ); + } + + // Uppercase the type + s.type = s.type.toUpperCase(); + + // Determine if request has content + s.hasContent = !rnoContent.test( s.type ); + + // Save the URL in case we're toying with the If-Modified-Since + // and/or If-None-Match header later on + // Remove hash to simplify url manipulation + cacheURL = s.url.replace( rhash, "" ); + + // More options handling for requests with no content + if ( !s.hasContent ) { + + // Remember the hash so we can put it back + uncached = s.url.slice( cacheURL.length ); + + // If data is available and should be processed, append data to url + if ( s.data && ( s.processData || typeof s.data === "string" ) ) { + cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data; + + // #9682: remove data so that it's not used in an eventual retry + delete s.data; + } + + // Add or update anti-cache param if needed + if ( s.cache === false ) { + cacheURL = cacheURL.replace( rantiCache, "$1" ); + uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce++ ) + uncached; + } + + // Put hash and anti-cache on the URL that will be requested (gh-1732) + s.url = cacheURL + uncached; + + // Change '%20' to '+' if this is encoded form body content (gh-2658) + } else if ( s.data && s.processData && + ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) { + s.data = s.data.replace( r20, "+" ); + } + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + if ( jQuery.lastModified[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] ); + } + if ( jQuery.etag[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] ); + } + } + + // Set the correct header, if data is being sent + if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { + jqXHR.setRequestHeader( "Content-Type", s.contentType ); + } + + // Set the Accepts header for the server, depending on the dataType + jqXHR.setRequestHeader( + "Accept", + s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ? + s.accepts[ s.dataTypes[ 0 ] ] + + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : + s.accepts[ "*" ] + ); + + // Check for headers option + for ( i in s.headers ) { + jqXHR.setRequestHeader( i, s.headers[ i ] ); + } + + // Allow custom headers/mimetypes and early abort + if ( s.beforeSend && + ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) { + + // Abort if not done already and return + return jqXHR.abort(); + } + + // Aborting is no longer a cancellation + strAbort = "abort"; + + // Install callbacks on deferreds + completeDeferred.add( s.complete ); + jqXHR.done( s.success ); + jqXHR.fail( s.error ); + + // Get transport + transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); + + // If no transport, we auto-abort + if ( !transport ) { + done( -1, "No Transport" ); + } else { + jqXHR.readyState = 1; + + // Send global event + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); + } + + // If request was aborted inside ajaxSend, stop there + if ( completed ) { + return jqXHR; + } + + // Timeout + if ( s.async && s.timeout > 0 ) { + timeoutTimer = window.setTimeout( function() { + jqXHR.abort( "timeout" ); + }, s.timeout ); + } + + try { + completed = false; + transport.send( requestHeaders, done ); + } catch ( e ) { + + // Rethrow post-completion exceptions + if ( completed ) { + throw e; + } + + // Propagate others as results + done( -1, e ); + } + } + + // Callback for when everything is done + function done( status, nativeStatusText, responses, headers ) { + var isSuccess, success, error, response, modified, + statusText = nativeStatusText; + + // Ignore repeat invocations + if ( completed ) { + return; + } + + completed = true; + + // Clear timeout if it exists + if ( timeoutTimer ) { + window.clearTimeout( timeoutTimer ); + } + + // Dereference transport for early garbage collection + // (no matter how long the jqXHR object will be used) + transport = undefined; + + // Cache response headers + responseHeadersString = headers || ""; + + // Set readyState + jqXHR.readyState = status > 0 ? 4 : 0; + + // Determine if successful + isSuccess = status >= 200 && status < 300 || status === 304; + + // Get response data + if ( responses ) { + response = ajaxHandleResponses( s, jqXHR, responses ); + } + + // Convert no matter what (that way responseXXX fields are always set) + response = ajaxConvert( s, response, jqXHR, isSuccess ); + + // If successful, handle type chaining + if ( isSuccess ) { + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + modified = jqXHR.getResponseHeader( "Last-Modified" ); + if ( modified ) { + jQuery.lastModified[ cacheURL ] = modified; + } + modified = jqXHR.getResponseHeader( "etag" ); + if ( modified ) { + jQuery.etag[ cacheURL ] = modified; + } + } + + // if no content + if ( status === 204 || s.type === "HEAD" ) { + statusText = "nocontent"; + + // if not modified + } else if ( status === 304 ) { + statusText = "notmodified"; + + // If we have data, let's convert it + } else { + statusText = response.state; + success = response.data; + error = response.error; + isSuccess = !error; + } + } else { + + // Extract error from statusText and normalize for non-aborts + error = statusText; + if ( status || !statusText ) { + statusText = "error"; + if ( status < 0 ) { + status = 0; + } + } + } + + // Set data for the fake xhr object + jqXHR.status = status; + jqXHR.statusText = ( nativeStatusText || statusText ) + ""; + + // Success/Error + if ( isSuccess ) { + deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); + } else { + deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); + } + + // Status-dependent callbacks + jqXHR.statusCode( statusCode ); + statusCode = undefined; + + if ( fireGlobals ) { + globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError", + [ jqXHR, s, isSuccess ? success : error ] ); + } + + // Complete + completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); + + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); + + // Handle the global AJAX counter + if ( !( --jQuery.active ) ) { + jQuery.event.trigger( "ajaxStop" ); + } + } + } + + return jqXHR; + }, + + getJSON: function( url, data, callback ) { + return jQuery.get( url, data, callback, "json" ); + }, + + getScript: function( url, callback ) { + return jQuery.get( url, undefined, callback, "script" ); + } +} ); + +jQuery.each( [ "get", "post" ], function( i, method ) { + jQuery[ method ] = function( url, data, callback, type ) { + + // Shift arguments if data argument was omitted + if ( isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } + + // The url can be an options object (which then must have .url) + return jQuery.ajax( jQuery.extend( { + url: url, + type: method, + dataType: type, + data: data, + success: callback + }, jQuery.isPlainObject( url ) && url ) ); + }; +} ); + + +jQuery._evalUrl = function( url ) { + return jQuery.ajax( { + url: url, + + // Make this explicit, since user can override this through ajaxSetup (#11264) + type: "GET", + dataType: "script", + cache: true, + async: false, + global: false, + "throws": true + } ); +}; + + +jQuery.fn.extend( { + wrapAll: function( html ) { + var wrap; + + if ( this[ 0 ] ) { + if ( isFunction( html ) ) { + html = html.call( this[ 0 ] ); + } + + // The elements to wrap the target around + wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); + + if ( this[ 0 ].parentNode ) { + wrap.insertBefore( this[ 0 ] ); + } + + wrap.map( function() { + var elem = this; + + while ( elem.firstElementChild ) { + elem = elem.firstElementChild; + } + + return elem; + } ).append( this ); + } + + return this; + }, + + wrapInner: function( html ) { + if ( isFunction( html ) ) { + return this.each( function( i ) { + jQuery( this ).wrapInner( html.call( this, i ) ); + } ); + } + + return this.each( function() { + var self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + } ); + }, + + wrap: function( html ) { + var htmlIsFunction = isFunction( html ); + + return this.each( function( i ) { + jQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html ); + } ); + }, + + unwrap: function( selector ) { + this.parent( selector ).not( "body" ).each( function() { + jQuery( this ).replaceWith( this.childNodes ); + } ); + return this; + } +} ); + + +jQuery.expr.pseudos.hidden = function( elem ) { + return !jQuery.expr.pseudos.visible( elem ); +}; +jQuery.expr.pseudos.visible = function( elem ) { + return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ); +}; + + + + +jQuery.ajaxSettings.xhr = function() { + try { + return new window.XMLHttpRequest(); + } catch ( e ) {} +}; + +var xhrSuccessStatus = { + + // File protocol always yields status code 0, assume 200 + 0: 200, + + // Support: IE <=9 only + // #1450: sometimes IE returns 1223 when it should be 204 + 1223: 204 + }, + xhrSupported = jQuery.ajaxSettings.xhr(); + +support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); +support.ajax = xhrSupported = !!xhrSupported; + +jQuery.ajaxTransport( function( options ) { + var callback, errorCallback; + + // Cross domain only allowed if supported through XMLHttpRequest + if ( support.cors || xhrSupported && !options.crossDomain ) { + return { + send: function( headers, complete ) { + var i, + xhr = options.xhr(); + + xhr.open( + options.type, + options.url, + options.async, + options.username, + options.password + ); + + // Apply custom fields if provided + if ( options.xhrFields ) { + for ( i in options.xhrFields ) { + xhr[ i ] = options.xhrFields[ i ]; + } + } + + // Override mime type if needed + if ( options.mimeType && xhr.overrideMimeType ) { + xhr.overrideMimeType( options.mimeType ); + } + + // X-Requested-With header + // For cross-domain requests, seeing as conditions for a preflight are + // akin to a jigsaw puzzle, we simply never set it to be sure. + // (it can always be set on a per-request basis or even using ajaxSetup) + // For same-domain requests, won't change header if already provided. + if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) { + headers[ "X-Requested-With" ] = "XMLHttpRequest"; + } + + // Set headers + for ( i in headers ) { + xhr.setRequestHeader( i, headers[ i ] ); + } + + // Callback + callback = function( type ) { + return function() { + if ( callback ) { + callback = errorCallback = xhr.onload = + xhr.onerror = xhr.onabort = xhr.ontimeout = + xhr.onreadystatechange = null; + + if ( type === "abort" ) { + xhr.abort(); + } else if ( type === "error" ) { + + // Support: IE <=9 only + // On a manual native abort, IE9 throws + // errors on any property access that is not readyState + if ( typeof xhr.status !== "number" ) { + complete( 0, "error" ); + } else { + complete( + + // File: protocol always yields status 0; see #8605, #14207 + xhr.status, + xhr.statusText + ); + } + } else { + complete( + xhrSuccessStatus[ xhr.status ] || xhr.status, + xhr.statusText, + + // Support: IE <=9 only + // IE9 has no XHR2 but throws on binary (trac-11426) + // For XHR2 non-text, let the caller handle it (gh-2498) + ( xhr.responseType || "text" ) !== "text" || + typeof xhr.responseText !== "string" ? + { binary: xhr.response } : + { text: xhr.responseText }, + xhr.getAllResponseHeaders() + ); + } + } + }; + }; + + // Listen to events + xhr.onload = callback(); + errorCallback = xhr.onerror = xhr.ontimeout = callback( "error" ); + + // Support: IE 9 only + // Use onreadystatechange to replace onabort + // to handle uncaught aborts + if ( xhr.onabort !== undefined ) { + xhr.onabort = errorCallback; + } else { + xhr.onreadystatechange = function() { + + // Check readyState before timeout as it changes + if ( xhr.readyState === 4 ) { + + // Allow onerror to be called first, + // but that will not handle a native abort + // Also, save errorCallback to a variable + // as xhr.onerror cannot be accessed + window.setTimeout( function() { + if ( callback ) { + errorCallback(); + } + } ); + } + }; + } + + // Create the abort callback + callback = callback( "abort" ); + + try { + + // Do send the request (this may raise an exception) + xhr.send( options.hasContent && options.data || null ); + } catch ( e ) { + + // #14683: Only rethrow if this hasn't been notified as an error yet + if ( callback ) { + throw e; + } + } + }, + + abort: function() { + if ( callback ) { + callback(); + } + } + }; + } +} ); + + + + +// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432) +jQuery.ajaxPrefilter( function( s ) { + if ( s.crossDomain ) { + s.contents.script = false; + } +} ); + +// Install script dataType +jQuery.ajaxSetup( { + accepts: { + script: "text/javascript, application/javascript, " + + "application/ecmascript, application/x-ecmascript" + }, + contents: { + script: /\b(?:java|ecma)script\b/ + }, + converters: { + "text script": function( text ) { + jQuery.globalEval( text ); + return text; + } + } +} ); + +// Handle cache's special case and crossDomain +jQuery.ajaxPrefilter( "script", function( s ) { + if ( s.cache === undefined ) { + s.cache = false; + } + if ( s.crossDomain ) { + s.type = "GET"; + } +} ); + +// Bind script tag hack transport +jQuery.ajaxTransport( "script", function( s ) { + + // This transport only deals with cross domain requests + if ( s.crossDomain ) { + var script, callback; + return { + send: function( _, complete ) { + script = jQuery( "<script>" ).prop( { + charset: s.scriptCharset, + src: s.url + } ).on( + "load error", + callback = function( evt ) { + script.remove(); + callback = null; + if ( evt ) { + complete( evt.type === "error" ? 404 : 200, evt.type ); + } + } + ); + + // Use native DOM manipulation to avoid our domManip AJAX trickery + document.head.appendChild( script[ 0 ] ); + }, + abort: function() { + if ( callback ) { + callback(); + } + } + }; + } +} ); + + + + +var oldCallbacks = [], + rjsonp = /(=)\?(?=&|$)|\?\?/; + +// Default jsonp settings +jQuery.ajaxSetup( { + jsonp: "callback", + jsonpCallback: function() { + var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce++ ) ); + this[ callback ] = true; + return callback; + } +} ); + +// Detect, normalize options and install callbacks for jsonp requests +jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) { + + var callbackName, overwritten, responseContainer, + jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ? + "url" : + typeof s.data === "string" && + ( s.contentType || "" ) + .indexOf( "application/x-www-form-urlencoded" ) === 0 && + rjsonp.test( s.data ) && "data" + ); + + // Handle iff the expected data type is "jsonp" or we have a parameter to set + if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) { + + // Get callback name, remembering preexisting value associated with it + callbackName = s.jsonpCallback = isFunction( s.jsonpCallback ) ? + s.jsonpCallback() : + s.jsonpCallback; + + // Insert callback into url or form data + if ( jsonProp ) { + s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName ); + } else if ( s.jsonp !== false ) { + s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName; + } + + // Use data converter to retrieve json after script execution + s.converters[ "script json" ] = function() { + if ( !responseContainer ) { + jQuery.error( callbackName + " was not called" ); + } + return responseContainer[ 0 ]; + }; + + // Force json dataType + s.dataTypes[ 0 ] = "json"; + + // Install callback + overwritten = window[ callbackName ]; + window[ callbackName ] = function() { + responseContainer = arguments; + }; + + // Clean-up function (fires after converters) + jqXHR.always( function() { + + // If previous value didn't exist - remove it + if ( overwritten === undefined ) { + jQuery( window ).removeProp( callbackName ); + + // Otherwise restore preexisting value + } else { + window[ callbackName ] = overwritten; + } + + // Save back as free + if ( s[ callbackName ] ) { + + // Make sure that re-using the options doesn't screw things around + s.jsonpCallback = originalSettings.jsonpCallback; + + // Save the callback name for future use + oldCallbacks.push( callbackName ); + } + + // Call if it was a function and we have a response + if ( responseContainer && isFunction( overwritten ) ) { + overwritten( responseContainer[ 0 ] ); + } + + responseContainer = overwritten = undefined; + } ); + + // Delegate to script + return "script"; + } +} ); + + + + +// Support: Safari 8 only +// In Safari 8 documents created via document.implementation.createHTMLDocument +// collapse sibling forms: the second one becomes a child of the first one. +// Because of that, this security measure has to be disabled in Safari 8. +// https://bugs.webkit.org/show_bug.cgi?id=137337 +support.createHTMLDocument = ( function() { + var body = document.implementation.createHTMLDocument( "" ).body; + body.innerHTML = "<form></form><form></form>"; + return body.childNodes.length === 2; +} )(); + + +// Argument "data" should be string of html +// context (optional): If specified, the fragment will be created in this context, +// defaults to document +// keepScripts (optional): If true, will include scripts passed in the html string +jQuery.parseHTML = function( data, context, keepScripts ) { + if ( typeof data !== "string" ) { + return []; + } + if ( typeof context === "boolean" ) { + keepScripts = context; + context = false; + } + + var base, parsed, scripts; + + if ( !context ) { + + // Stop scripts or inline event handlers from being executed immediately + // by using document.implementation + if ( support.createHTMLDocument ) { + context = document.implementation.createHTMLDocument( "" ); + + // Set the base href for the created document + // so any parsed elements with URLs + // are based on the document's URL (gh-2965) + base = context.createElement( "base" ); + base.href = document.location.href; + context.head.appendChild( base ); + } else { + context = document; + } + } + + parsed = rsingleTag.exec( data ); + scripts = !keepScripts && []; + + // Single tag + if ( parsed ) { + return [ context.createElement( parsed[ 1 ] ) ]; + } + + parsed = buildFragment( [ data ], context, scripts ); + + if ( scripts && scripts.length ) { + jQuery( scripts ).remove(); + } + + return jQuery.merge( [], parsed.childNodes ); +}; + + +/** + * Load a url into a page + */ +jQuery.fn.load = function( url, params, callback ) { + var selector, type, response, + self = this, + off = url.indexOf( " " ); + + if ( off > -1 ) { + selector = stripAndCollapse( url.slice( off ) ); + url = url.slice( 0, off ); + } + + // If it's a function + if ( isFunction( params ) ) { + + // We assume that it's the callback + callback = params; + params = undefined; + + // Otherwise, build a param string + } else if ( params && typeof params === "object" ) { + type = "POST"; + } + + // If we have elements to modify, make the request + if ( self.length > 0 ) { + jQuery.ajax( { + url: url, + + // If "type" variable is undefined, then "GET" method will be used. + // Make value of this field explicit since + // user can override it through ajaxSetup method + type: type || "GET", + dataType: "html", + data: params + } ).done( function( responseText ) { + + // Save response for use in complete callback + response = arguments; + + self.html( selector ? + + // If a selector was specified, locate the right elements in a dummy div + // Exclude scripts to avoid IE 'Permission Denied' errors + jQuery( "<div>" ).append( jQuery.parseHTML( responseText ) ).find( selector ) : + + // Otherwise use the full result + responseText ); + + // If the request succeeds, this function gets "data", "status", "jqXHR" + // but they are ignored because response was set above. + // If it fails, this function gets "jqXHR", "status", "error" + } ).always( callback && function( jqXHR, status ) { + self.each( function() { + callback.apply( this, response || [ jqXHR.responseText, status, jqXHR ] ); + } ); + } ); + } + + return this; +}; + + + + +// Attach a bunch of functions for handling common AJAX events +jQuery.each( [ + "ajaxStart", + "ajaxStop", + "ajaxComplete", + "ajaxError", + "ajaxSuccess", + "ajaxSend" +], function( i, type ) { + jQuery.fn[ type ] = function( fn ) { + return this.on( type, fn ); + }; +} ); + + + + +jQuery.expr.pseudos.animated = function( elem ) { + return jQuery.grep( jQuery.timers, function( fn ) { + return elem === fn.elem; + } ).length; +}; + + + + +jQuery.offset = { + setOffset: function( elem, options, i ) { + var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition, + position = jQuery.css( elem, "position" ), + curElem = jQuery( elem ), + props = {}; + + // Set position first, in-case top/left are set even on static elem + if ( position === "static" ) { + elem.style.position = "relative"; + } + + curOffset = curElem.offset(); + curCSSTop = jQuery.css( elem, "top" ); + curCSSLeft = jQuery.css( elem, "left" ); + calculatePosition = ( position === "absolute" || position === "fixed" ) && + ( curCSSTop + curCSSLeft ).indexOf( "auto" ) > -1; + + // Need to be able to calculate position if either + // top or left is auto and position is either absolute or fixed + if ( calculatePosition ) { + curPosition = curElem.position(); + curTop = curPosition.top; + curLeft = curPosition.left; + + } else { + curTop = parseFloat( curCSSTop ) || 0; + curLeft = parseFloat( curCSSLeft ) || 0; + } + + if ( isFunction( options ) ) { + + // Use jQuery.extend here to allow modification of coordinates argument (gh-1848) + options = options.call( elem, i, jQuery.extend( {}, curOffset ) ); + } + + if ( options.top != null ) { + props.top = ( options.top - curOffset.top ) + curTop; + } + if ( options.left != null ) { + props.left = ( options.left - curOffset.left ) + curLeft; + } + + if ( "using" in options ) { + options.using.call( elem, props ); + + } else { + curElem.css( props ); + } + } +}; + +jQuery.fn.extend( { + + // offset() relates an element's border box to the document origin + offset: function( options ) { + + // Preserve chaining for setter + if ( arguments.length ) { + return options === undefined ? + this : + this.each( function( i ) { + jQuery.offset.setOffset( this, options, i ); + } ); + } + + var rect, win, + elem = this[ 0 ]; + + if ( !elem ) { + return; + } + + // Return zeros for disconnected and hidden (display: none) elements (gh-2310) + // Support: IE <=11 only + // Running getBoundingClientRect on a + // disconnected node in IE throws an error + if ( !elem.getClientRects().length ) { + return { top: 0, left: 0 }; + } + + // Get document-relative position by adding viewport scroll to viewport-relative gBCR + rect = elem.getBoundingClientRect(); + win = elem.ownerDocument.defaultView; + return { + top: rect.top + win.pageYOffset, + left: rect.left + win.pageXOffset + }; + }, + + // position() relates an element's margin box to its offset parent's padding box + // This corresponds to the behavior of CSS absolute positioning + position: function() { + if ( !this[ 0 ] ) { + return; + } + + var offsetParent, offset, doc, + elem = this[ 0 ], + parentOffset = { top: 0, left: 0 }; + + // position:fixed elements are offset from the viewport, which itself always has zero offset + if ( jQuery.css( elem, "position" ) === "fixed" ) { + + // Assume position:fixed implies availability of getBoundingClientRect + offset = elem.getBoundingClientRect(); + + } else { + offset = this.offset(); + + // Account for the *real* offset parent, which can be the document or its root element + // when a statically positioned element is identified + doc = elem.ownerDocument; + offsetParent = elem.offsetParent || doc.documentElement; + while ( offsetParent && + ( offsetParent === doc.body || offsetParent === doc.documentElement ) && + jQuery.css( offsetParent, "position" ) === "static" ) { + + offsetParent = offsetParent.parentNode; + } + if ( offsetParent && offsetParent !== elem && offsetParent.nodeType === 1 ) { + + // Incorporate borders into its offset, since they are outside its content origin + parentOffset = jQuery( offsetParent ).offset(); + parentOffset.top += jQuery.css( offsetParent, "borderTopWidth", true ); + parentOffset.left += jQuery.css( offsetParent, "borderLeftWidth", true ); + } + } + + // Subtract parent offsets and element margins + return { + top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ), + left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true ) + }; + }, + + // This method will return documentElement in the following cases: + // 1) For the element inside the iframe without offsetParent, this method will return + // documentElement of the parent window + // 2) For the hidden or detached element + // 3) For body or html element, i.e. in case of the html node - it will return itself + // + // but those exceptions were never presented as a real life use-cases + // and might be considered as more preferable results. + // + // This logic, however, is not guaranteed and can change at any point in the future + offsetParent: function() { + return this.map( function() { + var offsetParent = this.offsetParent; + + while ( offsetParent && jQuery.css( offsetParent, "position" ) === "static" ) { + offsetParent = offsetParent.offsetParent; + } + + return offsetParent || documentElement; + } ); + } +} ); + +// Create scrollLeft and scrollTop methods +jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( method, prop ) { + var top = "pageYOffset" === prop; + + jQuery.fn[ method ] = function( val ) { + return access( this, function( elem, method, val ) { + + // Coalesce documents and windows + var win; + if ( isWindow( elem ) ) { + win = elem; + } else if ( elem.nodeType === 9 ) { + win = elem.defaultView; + } + + if ( val === undefined ) { + return win ? win[ prop ] : elem[ method ]; + } + + if ( win ) { + win.scrollTo( + !top ? val : win.pageXOffset, + top ? val : win.pageYOffset + ); + + } else { + elem[ method ] = val; + } + }, method, val, arguments.length ); + }; +} ); + +// Support: Safari <=7 - 9.1, Chrome <=37 - 49 +// Add the top/left cssHooks using jQuery.fn.position +// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084 +// Blink bug: https://bugs.chromium.org/p/chromium/issues/detail?id=589347 +// getComputedStyle returns percent when specified for top/left/bottom/right; +// rather than make the css module depend on the offset module, just check for it here +jQuery.each( [ "top", "left" ], function( i, prop ) { + jQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition, + function( elem, computed ) { + if ( computed ) { + computed = curCSS( elem, prop ); + + // If curCSS returns percentage, fallback to offset + return rnumnonpx.test( computed ) ? + jQuery( elem ).position()[ prop ] + "px" : + computed; + } + } + ); +} ); + + +// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods +jQuery.each( { Height: "height", Width: "width" }, function( name, type ) { + jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, + function( defaultExtra, funcName ) { + + // Margin is only for outerHeight, outerWidth + jQuery.fn[ funcName ] = function( margin, value ) { + var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ), + extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" ); + + return access( this, function( elem, type, value ) { + var doc; + + if ( isWindow( elem ) ) { + + // $( window ).outerWidth/Height return w/h including scrollbars (gh-1729) + return funcName.indexOf( "outer" ) === 0 ? + elem[ "inner" + name ] : + elem.document.documentElement[ "client" + name ]; + } + + // Get document width or height + if ( elem.nodeType === 9 ) { + doc = elem.documentElement; + + // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], + // whichever is greatest + return Math.max( + elem.body[ "scroll" + name ], doc[ "scroll" + name ], + elem.body[ "offset" + name ], doc[ "offset" + name ], + doc[ "client" + name ] + ); + } + + return value === undefined ? + + // Get width or height on the element, requesting but not forcing parseFloat + jQuery.css( elem, type, extra ) : + + // Set width or height on the element + jQuery.style( elem, type, value, extra ); + }, type, chainable ? margin : undefined, chainable ); + }; + } ); +} ); + + +jQuery.each( ( "blur focus focusin focusout resize scroll click dblclick " + + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + + "change select submit keydown keypress keyup contextmenu" ).split( " " ), + function( i, name ) { + + // Handle event binding + jQuery.fn[ name ] = function( data, fn ) { + return arguments.length > 0 ? + this.on( name, null, data, fn ) : + this.trigger( name ); + }; +} ); + +jQuery.fn.extend( { + hover: function( fnOver, fnOut ) { + return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); + } +} ); + + + + +jQuery.fn.extend( { + + bind: function( types, data, fn ) { + return this.on( types, null, data, fn ); + }, + unbind: function( types, fn ) { + return this.off( types, null, fn ); + }, + + delegate: function( selector, types, data, fn ) { + return this.on( types, selector, data, fn ); + }, + undelegate: function( selector, types, fn ) { + + // ( namespace ) or ( selector, types [, fn] ) + return arguments.length === 1 ? + this.off( selector, "**" ) : + this.off( types, selector || "**", fn ); + } +} ); + +// Bind a function to a context, optionally partially applying any +// arguments. +// jQuery.proxy is deprecated to promote standards (specifically Function#bind) +// However, it is not slated for removal any time soon +jQuery.proxy = function( fn, context ) { + var tmp, args, proxy; + + if ( typeof context === "string" ) { + tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + args = slice.call( arguments, 2 ); + proxy = function() { + return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || jQuery.guid++; + + return proxy; +}; + +jQuery.holdReady = function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } +}; +jQuery.isArray = Array.isArray; +jQuery.parseJSON = JSON.parse; +jQuery.nodeName = nodeName; +jQuery.isFunction = isFunction; +jQuery.isWindow = isWindow; +jQuery.camelCase = camelCase; +jQuery.type = toType; + +jQuery.now = Date.now; + +jQuery.isNumeric = function( obj ) { + + // As of jQuery 3.0, isNumeric is limited to + // strings and numbers (primitives or objects) + // that can be coerced to finite numbers (gh-2662) + var type = jQuery.type( obj ); + return ( type === "number" || type === "string" ) && + + // parseFloat NaNs numeric-cast false positives ("") + // ...but misinterprets leading-number strings, particularly hex literals ("0x...") + // subtraction forces infinities to NaN + !isNaN( obj - parseFloat( obj ) ); +}; + + + + +// Register as a named AMD module, since jQuery can be concatenated with other +// files that may use define, but not via a proper concatenation script that +// understands anonymous AMD modules. A named AMD is safest and most robust +// way to register. Lowercase jquery is used because AMD module names are +// derived from file names, and jQuery is normally delivered in a lowercase +// file name. Do this after creating the global so that if an AMD module wants +// to call noConflict to hide this version of jQuery, it will work. + +// Note that for maximum portability, libraries that are not jQuery should +// declare themselves as anonymous modules, and avoid setting a global if an +// AMD loader is present. jQuery is a special case. For more information, see +// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon + +if ( typeof define === "function" && define.amd ) { + define( "jquery", [], function() { + return jQuery; + } ); +} + + + + +var + + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + + // Map over the $ in case of overwrite + _$ = window.$; + +jQuery.noConflict = function( deep ) { + if ( window.$ === jQuery ) { + window.$ = _$; + } + + if ( deep && window.jQuery === jQuery ) { + window.jQuery = _jQuery; + } + + return jQuery; +}; + +// Expose jQuery and $ identifiers, even in AMD +// (#7102#comment:10, https://github.com/jquery/jquery/pull/557) +// and CommonJS for browser emulators (#13566) +if ( !noGlobal ) { + window.jQuery = window.$ = jQuery; +} + + + + +return jQuery; +} ); diff --git a/plugins/javadoc/src/main/resources/static_res/jquery/images/ui-bg_glass_55_fbf9ee_1x400.png b/plugins/javadoc/src/main/resources/static_res/jquery/images/ui-bg_glass_55_fbf9ee_1x400.png Binary files differnew file mode 100644 index 00000000..34abd18f --- /dev/null +++ b/plugins/javadoc/src/main/resources/static_res/jquery/images/ui-bg_glass_55_fbf9ee_1x400.png diff --git a/plugins/javadoc/src/main/resources/static_res/jquery/images/ui-bg_glass_65_dadada_1x400.png b/plugins/javadoc/src/main/resources/static_res/jquery/images/ui-bg_glass_65_dadada_1x400.png Binary files differnew file mode 100644 index 00000000..f058a938 --- /dev/null +++ b/plugins/javadoc/src/main/resources/static_res/jquery/images/ui-bg_glass_65_dadada_1x400.png diff --git a/plugins/javadoc/src/main/resources/static_res/jquery/images/ui-bg_glass_75_dadada_1x400.png b/plugins/javadoc/src/main/resources/static_res/jquery/images/ui-bg_glass_75_dadada_1x400.png Binary files differnew file mode 100644 index 00000000..2ce04c16 --- /dev/null +++ b/plugins/javadoc/src/main/resources/static_res/jquery/images/ui-bg_glass_75_dadada_1x400.png diff --git a/plugins/javadoc/src/main/resources/static_res/jquery/images/ui-bg_glass_75_e6e6e6_1x400.png b/plugins/javadoc/src/main/resources/static_res/jquery/images/ui-bg_glass_75_e6e6e6_1x400.png Binary files differnew file mode 100644 index 00000000..a90afb8b --- /dev/null +++ b/plugins/javadoc/src/main/resources/static_res/jquery/images/ui-bg_glass_75_e6e6e6_1x400.png diff --git a/plugins/javadoc/src/main/resources/static_res/jquery/images/ui-bg_glass_95_fef1ec_1x400.png b/plugins/javadoc/src/main/resources/static_res/jquery/images/ui-bg_glass_95_fef1ec_1x400.png Binary files differnew file mode 100644 index 00000000..dbe091f6 --- /dev/null +++ b/plugins/javadoc/src/main/resources/static_res/jquery/images/ui-bg_glass_95_fef1ec_1x400.png diff --git a/plugins/javadoc/src/main/resources/static_res/jquery/images/ui-bg_highlight-soft_75_cccccc_1x100.png b/plugins/javadoc/src/main/resources/static_res/jquery/images/ui-bg_highlight-soft_75_cccccc_1x100.png Binary files differnew file mode 100644 index 00000000..5dc3593e --- /dev/null +++ b/plugins/javadoc/src/main/resources/static_res/jquery/images/ui-bg_highlight-soft_75_cccccc_1x100.png diff --git a/plugins/javadoc/src/main/resources/static_res/jquery/images/ui-icons_222222_256x240.png b/plugins/javadoc/src/main/resources/static_res/jquery/images/ui-icons_222222_256x240.png Binary files differnew file mode 100644 index 00000000..e723e17c --- /dev/null +++ b/plugins/javadoc/src/main/resources/static_res/jquery/images/ui-icons_222222_256x240.png diff --git a/plugins/javadoc/src/main/resources/static_res/jquery/images/ui-icons_2e83ff_256x240.png b/plugins/javadoc/src/main/resources/static_res/jquery/images/ui-icons_2e83ff_256x240.png Binary files differnew file mode 100644 index 00000000..1f5f4975 --- /dev/null +++ b/plugins/javadoc/src/main/resources/static_res/jquery/images/ui-icons_2e83ff_256x240.png diff --git a/plugins/javadoc/src/main/resources/static_res/jquery/images/ui-icons_454545_256x240.png b/plugins/javadoc/src/main/resources/static_res/jquery/images/ui-icons_454545_256x240.png Binary files differnew file mode 100644 index 00000000..618f5b0c --- /dev/null +++ b/plugins/javadoc/src/main/resources/static_res/jquery/images/ui-icons_454545_256x240.png diff --git a/plugins/javadoc/src/main/resources/static_res/jquery/images/ui-icons_888888_256x240.png b/plugins/javadoc/src/main/resources/static_res/jquery/images/ui-icons_888888_256x240.png Binary files differnew file mode 100644 index 00000000..ee5e33f2 --- /dev/null +++ b/plugins/javadoc/src/main/resources/static_res/jquery/images/ui-icons_888888_256x240.png diff --git a/plugins/javadoc/src/main/resources/static_res/jquery/images/ui-icons_cd0a0a_256x240.png b/plugins/javadoc/src/main/resources/static_res/jquery/images/ui-icons_cd0a0a_256x240.png Binary files differnew file mode 100644 index 00000000..7e8ebc18 --- /dev/null +++ b/plugins/javadoc/src/main/resources/static_res/jquery/images/ui-icons_cd0a0a_256x240.png diff --git a/plugins/javadoc/src/main/resources/static_res/jquery/jquery-3.3.1.js b/plugins/javadoc/src/main/resources/static_res/jquery/jquery-3.3.1.js new file mode 100644 index 00000000..9b5206bc --- /dev/null +++ b/plugins/javadoc/src/main/resources/static_res/jquery/jquery-3.3.1.js @@ -0,0 +1,10364 @@ +/*! + * jQuery JavaScript Library v3.3.1 + * https://jquery.com/ + * + * Includes Sizzle.js + * https://sizzlejs.com/ + * + * Copyright JS Foundation and other contributors + * Released under the MIT license + * https://jquery.org/license + * + * Date: 2018-01-20T17:24Z + */ +( function( global, factory ) { + + "use strict"; + + if ( typeof module === "object" && typeof module.exports === "object" ) { + + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get jQuery. + // For environments that do not have a `window` with a `document` + // (such as Node.js), expose a factory as module.exports. + // This accentuates the need for the creation of a real `window`. + // e.g. var jQuery = require("jquery")(window); + // See ticket #14549 for more info. + module.exports = global.document ? + factory( global, true ) : + function( w ) { + if ( !w.document ) { + throw new Error( "jQuery requires a window with a document" ); + } + return factory( w ); + }; + } else { + factory( global ); + } + +// Pass this if window is not defined yet +} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { + +// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 +// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode +// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common +// enough that all such attempts are guarded in a try block. +"use strict"; + +var arr = []; + +var document = window.document; + +var getProto = Object.getPrototypeOf; + +var slice = arr.slice; + +var concat = arr.concat; + +var push = arr.push; + +var indexOf = arr.indexOf; + +var class2type = {}; + +var toString = class2type.toString; + +var hasOwn = class2type.hasOwnProperty; + +var fnToString = hasOwn.toString; + +var ObjectFunctionString = fnToString.call( Object ); + +var support = {}; + +var isFunction = function isFunction( obj ) { + + // Support: Chrome <=57, Firefox <=52 + // In some browsers, typeof returns "function" for HTML <object> elements + // (i.e., `typeof document.createElement( "object" ) === "function"`). + // We don't want to classify *any* DOM node as a function. + return typeof obj === "function" && typeof obj.nodeType !== "number"; + }; + + +var isWindow = function isWindow( obj ) { + return obj != null && obj === obj.window; + }; + + + + + var preservedScriptAttributes = { + type: true, + src: true, + noModule: true + }; + + function DOMEval( code, doc, node ) { + doc = doc || document; + + var i, + script = doc.createElement( "script" ); + + script.text = code; + if ( node ) { + for ( i in preservedScriptAttributes ) { + if ( node[ i ] ) { + script[ i ] = node[ i ]; + } + } + } + doc.head.appendChild( script ).parentNode.removeChild( script ); + } + + +function toType( obj ) { + if ( obj == null ) { + return obj + ""; + } + + // Support: Android <=2.3 only (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call( obj ) ] || "object" : + typeof obj; +} +/* global Symbol */ +// Defining this global in .eslintrc.json would create a danger of using the global +// unguarded in another place, it seems safer to define global only for this module + + + +var + version = "3.3.1", + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }, + + // Support: Android <=4.0 only + // Make sure we trim BOM and NBSP + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g; + +jQuery.fn = jQuery.prototype = { + + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + + // Return all the elements in a clean array + if ( num == null ) { + return slice.call( this ); + } + + // Return just the one element from the set + return num < 0 ? this[ num + this.length ] : this[ num ]; + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + each: function( callback ) { + return jQuery.each( this, callback ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map( this, function( elem, i ) { + return callback.call( elem, i, elem ); + } ) ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: arr.sort, + splice: arr.splice +}; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[ 0 ] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // Skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !isFunction( target ) ) { + target = {}; + } + + // Extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } + + for ( ; i < length; i++ ) { + + // Only deal with non-null/undefined values + if ( ( options = arguments[ i ] ) != null ) { + + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject( copy ) || + ( copyIsArray = Array.isArray( copy ) ) ) ) { + + if ( copyIsArray ) { + copyIsArray = false; + clone = src && Array.isArray( src ) ? src : []; + + } else { + clone = src && jQuery.isPlainObject( src ) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend( { + + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + isPlainObject: function( obj ) { + var proto, Ctor; + + // Detect obvious negatives + // Use toString instead of jQuery.type to catch host objects + if ( !obj || toString.call( obj ) !== "[object Object]" ) { + return false; + } + + proto = getProto( obj ); + + // Objects with no prototype (e.g., `Object.create( null )`) are plain + if ( !proto ) { + return true; + } + + // Objects with prototype are plain iff they were constructed by a global Object function + Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; + return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; + }, + + isEmptyObject: function( obj ) { + + /* eslint-disable no-unused-vars */ + // See https://github.com/eslint/eslint/issues/6125 + var name; + + for ( name in obj ) { + return false; + } + return true; + }, + + // Evaluates a script in a global context + globalEval: function( code ) { + DOMEval( code ); + }, + + each: function( obj, callback ) { + var length, i = 0; + + if ( isArrayLike( obj ) ) { + length = obj.length; + for ( ; i < length; i++ ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } else { + for ( i in obj ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } + + return obj; + }, + + // Support: Android <=4.0 only + trim: function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArrayLike( Object( arr ) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + return arr == null ? -1 : indexOf.call( arr, elem, i ); + }, + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; + + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var length, value, + i = 0, + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArrayLike( elems ) ) { + length = elems.length; + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +} ); + +if ( typeof Symbol === "function" ) { + jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; +} + +// Populate the class2type map +jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), +function( i, name ) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +} ); + +function isArrayLike( obj ) { + + // Support: real iOS 8.2 only (not reproducible in simulator) + // `in` check used to prevent JIT error (gh-2145) + // hasOwn isn't used here due to false negatives + // regarding Nodelist length in IE + var length = !!obj && "length" in obj && obj.length, + type = toType( obj ); + + if ( isFunction( obj ) || isWindow( obj ) ) { + return false; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +var Sizzle = +/*! + * Sizzle CSS Selector Engine v2.3.3 + * https://sizzlejs.com/ + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2016-08-08 + */ +(function( window ) { + +var i, + support, + Expr, + getText, + isXML, + tokenize, + compile, + select, + outermostContext, + sortInput, + hasDuplicate, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + 1 * new Date(), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, + + // Instance methods + hasOwn = ({}).hasOwnProperty, + arr = [], + pop = arr.pop, + push_native = arr.push, + push = arr.push, + slice = arr.slice, + // Use a stripped-down indexOf as it's faster than native + // https://jsperf.com/thor-indexof-vs-for/5 + indexOf = function( list, elem ) { + var i = 0, + len = list.length; + for ( ; i < len; i++ ) { + if ( list[i] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + + // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = "(?:\\\\.|[\\w-]|[^\0-\\xa0])+", + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + + "*\\]", + + pseudos = ":(" + identifier + ")(?:\\((" + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rwhitespace = new RegExp( whitespace + "+", "g" ), + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), + + rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + identifier + ")" ), + "CLASS": new RegExp( "^\\.(" + identifier + ")" ), + "TAG": new RegExp( "^(" + identifier + "|[*])" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + + // CSS escapes + // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), + funescape = function( _, escaped, escapedWhitespace ) { + var high = "0x" + escaped - 0x10000; + // NaN means non-codepoint + // Support: Firefox<24 + // Workaround erroneous numeric interpretation of +"0x" + return high !== high || escapedWhitespace ? + escaped : + high < 0 ? + // BMP codepoint + String.fromCharCode( high + 0x10000 ) : + // Supplemental Plane codepoint (surrogate pair) + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }, + + // CSS string/identifier serialization + // https://drafts.csswg.org/cssom/#common-serializing-idioms + rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g, + fcssescape = function( ch, asCodePoint ) { + if ( asCodePoint ) { + + // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER + if ( ch === "\0" ) { + return "\uFFFD"; + } + + // Control characters and (dependent upon position) numbers get escaped as code points + return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; + } + + // Other potentially-special ASCII characters get backslash-escaped + return "\\" + ch; + }, + + // Used for iframes + // See setDocument() + // Removing the function wrapper causes a "Permission Denied" + // error in IE + unloadHandler = function() { + setDocument(); + }, + + disabledAncestor = addCombinator( + function( elem ) { + return elem.disabled === true && ("form" in elem || "label" in elem); + }, + { dir: "parentNode", next: "legend" } + ); + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + (arr = slice.call( preferredDoc.childNodes )), + preferredDoc.childNodes + ); + // Support: Android<4.0 + // Detect silently failing push.apply + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + push_native.apply( target, slice.call(els) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + // Can't trust NodeList.length + while ( (target[j++] = els[i++]) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var m, i, elem, nid, match, groups, newSelector, + newContext = context && context.ownerDocument, + + // nodeType defaults to 9, since context defaults to document + nodeType = context ? context.nodeType : 9; + + results = results || []; + + // Return early from calls with invalid selector or context + if ( typeof selector !== "string" || !selector || + nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { + + return results; + } + + // Try to shortcut find operations (as opposed to filters) in HTML documents + if ( !seed ) { + + if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { + setDocument( context ); + } + context = context || document; + + if ( documentIsHTML ) { + + // If the selector is sufficiently simple, try using a "get*By*" DOM method + // (excepting DocumentFragment context, where the methods don't exist) + if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { + + // ID selector + if ( (m = match[1]) ) { + + // Document context + if ( nodeType === 9 ) { + if ( (elem = context.getElementById( m )) ) { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + + // Element context + } else { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( newContext && (elem = newContext.getElementById( m )) && + contains( context, elem ) && + elem.id === m ) { + + results.push( elem ); + return results; + } + } + + // Type selector + } else if ( match[2] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Class selector + } else if ( (m = match[3]) && support.getElementsByClassName && + context.getElementsByClassName ) { + + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // Take advantage of querySelectorAll + if ( support.qsa && + !compilerCache[ selector + " " ] && + (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { + + if ( nodeType !== 1 ) { + newContext = context; + newSelector = selector; + + // qSA looks outside Element context, which is not what we want + // Thanks to Andrew Dupont for this workaround technique + // Support: IE <=8 + // Exclude object elements + } else if ( context.nodeName.toLowerCase() !== "object" ) { + + // Capture the context ID, setting it first if necessary + if ( (nid = context.getAttribute( "id" )) ) { + nid = nid.replace( rcssescape, fcssescape ); + } else { + context.setAttribute( "id", (nid = expando) ); + } + + // Prefix every selector in the list + groups = tokenize( selector ); + i = groups.length; + while ( i-- ) { + groups[i] = "#" + nid + " " + toSelector( groups[i] ); + } + newSelector = groups.join( "," ); + + // Expand context for sibling selectors + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || + context; + } + + if ( newSelector ) { + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch ( qsaError ) { + } finally { + if ( nid === expando ) { + context.removeAttribute( "id" ); + } + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {function(string, object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return (cache[ key + " " ] = value); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created element and returns a boolean result + */ +function assert( fn ) { + var el = document.createElement("fieldset"); + + try { + return !!fn( el ); + } catch (e) { + return false; + } finally { + // Remove from its parent by default + if ( el.parentNode ) { + el.parentNode.removeChild( el ); + } + // release memory in IE + el = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split("|"), + i = arr.length; + + while ( i-- ) { + Expr.attrHandle[ arr[i] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + a.sourceIndex - b.sourceIndex; + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( (cur = cur.nextSibling) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for :enabled/:disabled + * @param {Boolean} disabled true for :disabled; false for :enabled + */ +function createDisabledPseudo( disabled ) { + + // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable + return function( elem ) { + + // Only certain elements can match :enabled or :disabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled + if ( "form" in elem ) { + + // Check for inherited disabledness on relevant non-disabled elements: + // * listed form-associated elements in a disabled fieldset + // https://html.spec.whatwg.org/multipage/forms.html#category-listed + // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled + // * option elements in a disabled optgroup + // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled + // All such elements have a "form" property. + if ( elem.parentNode && elem.disabled === false ) { + + // Option elements defer to a parent optgroup if present + if ( "label" in elem ) { + if ( "label" in elem.parentNode ) { + return elem.parentNode.disabled === disabled; + } else { + return elem.disabled === disabled; + } + } + + // Support: IE 6 - 11 + // Use the isDisabled shortcut property to check for disabled fieldset ancestors + return elem.isDisabled === disabled || + + // Where there is no isDisabled, check manually + /* jshint -W018 */ + elem.isDisabled !== !disabled && + disabledAncestor( elem ) === disabled; + } + + return elem.disabled === disabled; + + // Try to winnow out elements that can't be disabled before trusting the disabled property. + // Some victims get caught in our net (label, legend, menu, track), but it shouldn't + // even exist on them, let alone have a boolean value. + } else if ( "label" in elem ) { + return elem.disabled === disabled; + } + + // Remaining elements are neither :enabled nor :disabled + return false; + }; +} + +/** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ +function createPositionalPseudo( fn ) { + return markFunction(function( argument ) { + argument = +argument; + return markFunction(function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ (j = matchIndexes[i]) ] ) { + seed[j] = !(matches[j] = seed[j]); + } + } + }); + }); +} + +/** + * Checks a node for validity as a Sizzle context + * @param {Element|Object=} context + * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value + */ +function testContext( context ) { + return context && typeof context.getElementsByTagName !== "undefined" && context; +} + +// Expose support vars for convenience +support = Sizzle.support = {}; + +/** + * Detects XML nodes + * @param {Element|Object} elem An element or a document + * @returns {Boolean} True iff elem is a non-HTML XML node + */ +isXML = Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = elem && (elem.ownerDocument || elem).documentElement; + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var hasCompare, subWindow, + doc = node ? node.ownerDocument || node : preferredDoc; + + // Return early if doc is invalid or already selected + if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Update global variables + document = doc; + docElem = document.documentElement; + documentIsHTML = !isXML( document ); + + // Support: IE 9-11, Edge + // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) + if ( preferredDoc !== document && + (subWindow = document.defaultView) && subWindow.top !== subWindow ) { + + // Support: IE 11, Edge + if ( subWindow.addEventListener ) { + subWindow.addEventListener( "unload", unloadHandler, false ); + + // Support: IE 9 - 10 only + } else if ( subWindow.attachEvent ) { + subWindow.attachEvent( "onunload", unloadHandler ); + } + } + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties + // (excepting IE8 booleans) + support.attributes = assert(function( el ) { + el.className = "i"; + return !el.getAttribute("className"); + }); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert(function( el ) { + el.appendChild( document.createComment("") ); + return !el.getElementsByTagName("*").length; + }); + + // Support: IE<9 + support.getElementsByClassName = rnative.test( document.getElementsByClassName ); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programmatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert(function( el ) { + docElem.appendChild( el ).id = expando; + return !document.getElementsByName || !document.getElementsByName( expando ).length; + }); + + // ID filter and find + if ( support.getById ) { + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute("id") === attrId; + }; + }; + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var elem = context.getElementById( id ); + return elem ? [ elem ] : []; + } + }; + } else { + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== "undefined" && + elem.getAttributeNode("id"); + return node && node.value === attrId; + }; + }; + + // Support: IE 6 - 7 only + // getElementById is not reliable as a find shortcut + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var node, i, elems, + elem = context.getElementById( id ); + + if ( elem ) { + + // Verify the id attribute + node = elem.getAttributeNode("id"); + if ( node && node.value === id ) { + return [ elem ]; + } + + // Fall back on getElementsByName + elems = context.getElementsByName( id ); + i = 0; + while ( (elem = elems[i++]) ) { + node = elem.getAttributeNode("id"); + if ( node && node.value === id ) { + return [ elem ]; + } + } + } + + return []; + } + }; + } + + // Tag + Expr.find["TAG"] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( tag ); + + // DocumentFragment nodes don't have gEBTN + } else if ( support.qsa ) { + return context.querySelectorAll( tag ); + } + } : + + function( tag, context ) { + var elem, + tmp = [], + i = 0, + // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( (elem = results[i++]) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Class + Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See https://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( (support.qsa = rnative.test( document.querySelectorAll )) ) { + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert(function( el ) { + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // https://bugs.jquery.com/ticket/12359 + docElem.appendChild( el ).innerHTML = "<a id='" + expando + "'></a>" + + "<select id='" + expando + "-\r\\' msallowcapture=''>" + + "<option selected=''></option></select>"; + + // Support: IE8, Opera 11-12.16 + // Nothing should be selected when empty strings follow ^= or $= or *= + // The test attribute must be unknown in Opera but "safe" for WinRT + // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if ( el.querySelectorAll("[msallowcapture^='']").length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !el.querySelectorAll("[selected]").length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ + if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { + rbuggyQSA.push("~="); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !el.querySelectorAll(":checked").length ) { + rbuggyQSA.push(":checked"); + } + + // Support: Safari 8+, iOS 8+ + // https://bugs.webkit.org/show_bug.cgi?id=136851 + // In-page `selector#id sibling-combinator selector` fails + if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) { + rbuggyQSA.push(".#.+[+~]"); + } + }); + + assert(function( el ) { + el.innerHTML = "<a href='' disabled='disabled'></a>" + + "<select disabled='disabled'><option/></select>"; + + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + var input = document.createElement("input"); + input.setAttribute( "type", "hidden" ); + el.appendChild( input ).setAttribute( "name", "D" ); + + // Support: IE8 + // Enforce case-sensitivity of name attribute + if ( el.querySelectorAll("[name=d]").length ) { + rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( el.querySelectorAll(":enabled").length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Support: IE9-11+ + // IE's :disabled selector does not pick up the children of disabled fieldsets + docElem.appendChild( el ).disabled = true; + if ( el.querySelectorAll(":disabled").length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Opera 10-11 does not throw on post-comma invalid pseudos + el.querySelectorAll("*,:x"); + rbuggyQSA.push(",.*:"); + }); + } + + if ( (support.matchesSelector = rnative.test( (matches = docElem.matches || + docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector) )) ) { + + assert(function( el ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( el, "*" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( el, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + }); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); + + /* Contains + ---------------------------------------------------------------------- */ + hasCompare = rnative.test( docElem.compareDocumentPosition ); + + // Element contains another + // Purposefully self-exclusive + // As in, an element does not contain itself + contains = hasCompare || rnative.test( docElem.contains ) ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + )); + } : + function( a, b ) { + if ( b ) { + while ( (b = b.parentNode) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + /* Sorting + ---------------------------------------------------------------------- */ + + // Document order sorting + sortOrder = hasCompare ? + function( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + // Sort on method existence if only one input has compareDocumentPosition + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; + if ( compare ) { + return compare; + } + + // Calculate position if both inputs belong to the same document + compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? + a.compareDocumentPosition( b ) : + + // Otherwise we know they are disconnected + 1; + + // Disconnected nodes + if ( compare & 1 || + (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { + + // Choose the first element that is related to our preferred document + if ( a === document || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { + return -1; + } + if ( b === document || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } : + function( a, b ) { + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Parentless nodes are either documents or disconnected + if ( !aup || !bup ) { + return a === document ? -1 : + b === document ? 1 : + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( (cur = cur.parentNode) ) { + ap.unshift( cur ); + } + cur = b; + while ( (cur = cur.parentNode) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[i] === bp[i] ) { + i++; + } + + return i ? + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[i], bp[i] ) : + + // Otherwise nodes in our document sort first + ap[i] === preferredDoc ? -1 : + bp[i] === preferredDoc ? 1 : + 0; + }; + + return document; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + // Make sure that attribute selectors are quoted + expr = expr.replace( rattributeQuotes, "='$1']" ); + + if ( support.matchesSelector && documentIsHTML && + !compilerCache[ expr + " " ] && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch (e) {} + } + + return Sizzle( expr, document, null, [ elem ] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + // Set document vars if needed + if ( ( context.ownerDocument || context ) !== document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + var fn = Expr.attrHandle[ name.toLowerCase() ], + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined; + + return val !== undefined ? + val : + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + (val = elem.getAttributeNode(name)) && val.specified ? + val.value : + null; +}; + +Sizzle.escape = function( sel ) { + return (sel + "").replace( rcssescape, fcssescape ); +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + j = 0, + i = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice( 0 ); + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( (elem = results[i++]) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; + + return results; +}; + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + // If no nodeType, this is expected to be an array + while ( (node = elem[i++]) ) { + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent for elements + // innerText usage removed for consistency of new lines (jQuery #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + // Do not include comment or processing instruction nodes + + return ret; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[1] = match[1].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape ); + + if ( match[2] === "~=" ) { + match[3] = " " + match[3] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[1] = match[1].toLowerCase(); + + if ( match[1].slice( 0, 3 ) === "nth" ) { + // nth-* requires argument + if ( !match[3] ) { + Sizzle.error( match[0] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); + match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); + + // other types prohibit arguments + } else if ( match[3] ) { + Sizzle.error( match[0] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[6] && match[2]; + + if ( matchExpr["CHILD"].test( match[0] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[3] ) { + match[2] = match[4] || match[5] || ""; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + // Get excess from tokenize (recursively) + (excess = tokenize( unquoted, true )) && + // advance to the next closing parenthesis + (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { + + // excess is a negative index + match[0] = match[0].slice( 0, excess ); + match[2] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeNameSelector ) { + var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { return true; } : + function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && + classCache( className, function( elem ) { + return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" ); + }); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + }; + }, + + "CHILD": function( type, what, argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, context, xml ) { + var cache, uniqueCache, outerCache, node, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType, + diff = false; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( (node = node[ dir ]) ) { + if ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) { + + return false; + } + } + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + + // Seek `elem` from a previously-cached index + + // ...in a gzip-friendly way + node = parent; + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex && cache[ 2 ]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( (node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + (diff = nodeIndex = 0) || start.pop()) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + } else { + // Use previously-cached element index if available + if ( useCache ) { + // ...in a gzip-friendly way + node = elem; + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex; + } + + // xml :nth-child(...) + // or :nth-last-child(...) or :nth(-last)?-of-type(...) + if ( diff === false ) { + // Use the same loop as above to seek `elem` from the start + while ( (node = ++nodeIndex && node && node[ dir ] || + (diff = nodeIndex = 0) || start.pop()) ) { + + if ( ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) && + ++diff ) { + + // Cache the index of each encountered element + if ( useCache ) { + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + uniqueCache[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction(function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf( seed, matched[i] ); + seed[ idx ] = !( matches[ idx ] = matched[i] ); + } + }) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + // Potentially complex pseudos + "not": markFunction(function( selector ) { + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction(function( seed, matches, context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( (elem = unmatched[i]) ) { + seed[i] = !(matches[i] = elem); + } + } + }) : + function( elem, context, xml ) { + input[0] = elem; + matcher( input, null, xml, results ); + // Don't keep the element (issue #299) + input[0] = null; + return !results.pop(); + }; + }), + + "has": markFunction(function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + }), + + "contains": markFunction(function( text ) { + text = text.replace( runescape, funescape ); + return function( elem ) { + return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; + }; + }), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + // lang value must be a valid identifier + if ( !ridentifier.test(lang || "") ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( (elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); + return false; + }; + }), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); + }, + + // Boolean properties + "enabled": createDisabledPseudo( false ), + "disabled": createDisabledPseudo( true ), + + "checked": function( elem ) { + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); + }, + + "selected": function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), + // but not by others (comment: 8; processing instruction: 7; etc.) + // nodeType < 6 works because attributes (2) do not appear as children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeType < 6 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos["empty"]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + + // Support: IE<8 + // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" + ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" ); + }, + + // Position-in-collection + "first": createPositionalPseudo(function() { + return [ 0 ]; + }), + + "last": createPositionalPseudo(function( matchIndexes, length ) { + return [ length - 1 ]; + }), + + "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + }), + + "even": createPositionalPseudo(function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "odd": createPositionalPseudo(function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }) + } +}; + +Expr.pseudos["nth"] = Expr.pseudos["eq"]; + +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +// Easy API for creating new setFilters +function setFilters() {} +setFilters.prototype = Expr.filters = Expr.pseudos; +Expr.setFilters = new setFilters(); + +tokenize = Sizzle.tokenize = function( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || (match = rcomma.exec( soFar )) ) { + if ( match ) { + // Don't consume trailing commas as valid + soFar = soFar.slice( match[0].length ) || soFar; + } + groups.push( (tokens = []) ); + } + + matched = false; + + // Combinators + if ( (match = rcombinators.exec( soFar )) ) { + matched = match.shift(); + tokens.push({ + value: matched, + // Cast descendant combinators to space + type: match[0].replace( rtrim, " " ) + }); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || + (match = preFilters[ type ]( match ))) ) { + matched = match.shift(); + tokens.push({ + value: matched, + type: type, + matches: match + }); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +}; + +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[i].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + skip = combinator.next, + key = skip || dir, + checkNonElements = base && key === "parentNode", + doneName = done++; + + return combinator.first ? + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + return false; + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var oldCache, uniqueCache, outerCache, + newCache = [ dirruns, doneName ]; + + // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching + if ( xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || (elem[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ elem.uniqueID ] || (outerCache[ elem.uniqueID ] = {}); + + if ( skip && skip === elem.nodeName.toLowerCase() ) { + elem = elem[ dir ] || elem; + } else if ( (oldCache = uniqueCache[ key ]) && + oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { + + // Assign to newCache so results back-propagate to previous elements + return (newCache[ 2 ] = oldCache[ 2 ]); + } else { + // Reuse newcache so results back-propagate to previous elements + uniqueCache[ key ] = newCache; + + // A match means we're done; a fail means we have to keep checking + if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { + return true; + } + } + } + } + } + return false; + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[i]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[0]; +} + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[i], results ); + } + return results; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( (elem = unmatched[i]) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction(function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( (elem = temp[i]) ) { + matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) ) { + // Restore matcherIn since elem is not yet a final match + temp.push( (matcherIn[i] = elem) ); + } + } + postFinder( null, (matcherOut = []), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) && + (temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) { + + seed[temp] = !(results[temp] = elem); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + }); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[0].type ], + implicitRelative = leadingRelative || Expr.relative[" "], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + (checkContext = context).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + // Avoid hanging onto element (issue #299) + checkContext = null; + return ret; + } ]; + + for ( ; i < len; i++ ) { + if ( (matcher = Expr.relative[ tokens[i].type ]) ) { + matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; + } else { + matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[j].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) + ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, outermost ) { + var elem, j, matcher, + matchedCount = 0, + i = "0", + unmatched = seed && [], + setMatched = [], + contextBackup = outermostContext, + // We must always have either seed elements or outermost context + elems = seed || byElement && Expr.find["TAG"]( "*", outermost ), + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), + len = elems.length; + + if ( outermost ) { + outermostContext = context === document || context || outermost; + } + + // Add elements passing elementMatchers directly to results + // Support: IE<9, Safari + // Tolerate NodeList properties (IE: "length"; Safari: <number>) matching elements by id + for ( ; i !== len && (elem = elems[i]) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + if ( !context && elem.ownerDocument !== document ) { + setDocument( elem ); + xml = !documentIsHTML; + } + while ( (matcher = elementMatchers[j++]) ) { + if ( matcher( elem, context || document, xml) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + // They will have gone through all possible matchers + if ( (elem = !matcher && elem) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // `i` is now the count of elements visited above, and adding it to `matchedCount` + // makes the latter nonnegative. + matchedCount += i; + + // Apply set filters to unmatched elements + // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` + // equals `i`), unless we didn't visit _any_ elements in the above loop because we have + // no element matchers and no seed. + // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that + // case, which will result in a "00" `matchedCount` that differs from `i` but is also + // numerically zero. + if ( bySet && i !== matchedCount ) { + j = 0; + while ( (matcher = setMatchers[j++]) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !(unmatched[i] || setMatched[i]) ) { + setMatched[i] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + // Generate a function of recursive functions that can be used to check each element + if ( !match ) { + match = tokenize( selector ); + } + i = match.length; + while ( i-- ) { + cached = matcherFromTokens( match[i] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); + + // Save selector and tokenization + cached.selector = selector; + } + return cached; +}; + +/** + * A low-level selection function that works with Sizzle's compiled + * selector functions + * @param {String|Function} selector A selector or a pre-compiled + * selector function built with Sizzle.compile + * @param {Element} context + * @param {Array} [results] + * @param {Array} [seed] A set of elements to match against + */ +select = Sizzle.select = function( selector, context, results, seed ) { + var i, tokens, token, type, find, + compiled = typeof selector === "function" && selector, + match = !seed && tokenize( (selector = compiled.selector || selector) ); + + results = results || []; + + // Try to minimize operations if there is only one selector in the list and no seed + // (the latter of which guarantees us context) + if ( match.length === 1 ) { + + // Reduce context if the leading compound selector is an ID + tokens = match[0] = match[0].slice( 0 ); + if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && + context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[1].type ] ) { + + context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; + if ( !context ) { + return results; + + // Precompiled matchers will still verify ancestry, so step up a level + } else if ( compiled ) { + context = context.parentNode; + } + + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[i]; + + // Abort if we hit a combinator + if ( Expr.relative[ (type = token.type) ] ) { + break; + } + if ( (find = Expr.find[ type ]) ) { + // Search, expanding context for leading sibling combinators + if ( (seed = find( + token.matches[0].replace( runescape, funescape ), + rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context + )) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; + } + + break; + } + } + } + } + + // Compile and execute a filtering function if one is not provided + // Provide `match` to avoid retokenization if we modified the selector above + ( compiled || compile( selector, match ) )( + seed, + context, + !documentIsHTML, + results, + !context || rsibling.test( selector ) && testContext( context.parentNode ) || context + ); + return results; +}; + +// One-time assignments + +// Sort stability +support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; + +// Support: Chrome 14-35+ +// Always assume duplicates if they aren't passed to the comparison function +support.detectDuplicates = !!hasDuplicate; + +// Initialize against the default document +setDocument(); + +// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) +// Detached nodes confoundingly follow *each other* +support.sortDetached = assert(function( el ) { + // Should return 1, but returns 4 (following) + return el.compareDocumentPosition( document.createElement("fieldset") ) & 1; +}); + +// Support: IE<8 +// Prevent attribute/property "interpolation" +// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !assert(function( el ) { + el.innerHTML = "<a href='#'></a>"; + return el.firstChild.getAttribute("href") === "#" ; +}) ) { + addHandle( "type|href|height|width", function( elem, name, isXML ) { + if ( !isXML ) { + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); + } + }); +} + +// Support: IE<9 +// Use defaultValue in place of getAttribute("value") +if ( !support.attributes || !assert(function( el ) { + el.innerHTML = "<input/>"; + el.firstChild.setAttribute( "value", "" ); + return el.firstChild.getAttribute( "value" ) === ""; +}) ) { + addHandle( "value", function( elem, name, isXML ) { + if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } + }); +} + +// Support: IE<9 +// Use getAttributeNode to fetch booleans when getAttribute lies +if ( !assert(function( el ) { + return el.getAttribute("disabled") == null; +}) ) { + addHandle( booleans, function( elem, name, isXML ) { + var val; + if ( !isXML ) { + return elem[ name ] === true ? name.toLowerCase() : + (val = elem.getAttributeNode( name )) && val.specified ? + val.value : + null; + } + }); +} + +return Sizzle; + +})( window ); + + + +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; + +// Deprecated +jQuery.expr[ ":" ] = jQuery.expr.pseudos; +jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; +jQuery.escapeSelector = Sizzle.escape; + + + + +var dir = function( elem, dir, until ) { + var matched = [], + truncate = until !== undefined; + + while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { + if ( elem.nodeType === 1 ) { + if ( truncate && jQuery( elem ).is( until ) ) { + break; + } + matched.push( elem ); + } + } + return matched; +}; + + +var siblings = function( n, elem ) { + var matched = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + matched.push( n ); + } + } + + return matched; +}; + + +var rneedsContext = jQuery.expr.match.needsContext; + + + +function nodeName( elem, name ) { + + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + +}; +var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); + + + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + return !!qualifier.call( elem, i, elem ) !== not; + } ); + } + + // Single element + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + } ); + } + + // Arraylike of elements (jQuery, arguments, Array) + if ( typeof qualifier !== "string" ) { + return jQuery.grep( elements, function( elem ) { + return ( indexOf.call( qualifier, elem ) > -1 ) !== not; + } ); + } + + // Filtered directly for both simple and complex selectors + return jQuery.filter( qualifier, elements, not ); +} + +jQuery.filter = function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + if ( elems.length === 1 && elem.nodeType === 1 ) { + return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : []; + } + + return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + } ) ); +}; + +jQuery.fn.extend( { + find: function( selector ) { + var i, ret, + len = this.length, + self = this; + + if ( typeof selector !== "string" ) { + return this.pushStack( jQuery( selector ).filter( function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + } ) ); + } + + ret = this.pushStack( [] ); + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + return len > 1 ? jQuery.uniqueSort( ret ) : ret; + }, + filter: function( selector ) { + return this.pushStack( winnow( this, selector || [], false ) ); + }, + not: function( selector ) { + return this.pushStack( winnow( this, selector || [], true ) ); + }, + is: function( selector ) { + return !!winnow( + this, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + } +} ); + + +// Initialize a jQuery object + + +// A central reference to the root jQuery(document) +var rootjQuery, + + // A simple way to check for HTML strings + // Prioritize #id over <tag> to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + // Shortcut simple #id case for speed + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, + + init = jQuery.fn.init = function( selector, context, root ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Method init() accepts an alternate rootjQuery + // so migrate can support jQuery.sub (gh-2101) + root = root || rootjQuery; + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector[ 0 ] === "<" && + selector[ selector.length - 1 ] === ">" && + selector.length >= 3 ) { + + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && ( match[ 1 ] || !context ) ) { + + // HANDLE: $(html) -> $(array) + if ( match[ 1 ] ) { + context = context instanceof jQuery ? context[ 0 ] : context; + + // Option to run scripts is true for back-compat + // Intentionally let the error be thrown if parseHTML is not present + jQuery.merge( this, jQuery.parseHTML( + match[ 1 ], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + + // Properties of context are called as methods if possible + if ( isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[ 2 ] ); + + if ( elem ) { + + // Inject the element directly into the jQuery object + this[ 0 ] = elem; + this.length = 1; + } + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || root ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this[ 0 ] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( isFunction( selector ) ) { + return root.ready !== undefined ? + root.ready( selector ) : + + // Execute immediately if ready is not present + selector( jQuery ); + } + + return jQuery.makeArray( selector, this ); + }; + +// Give the init function the jQuery prototype for later instantiation +init.prototype = jQuery.fn; + +// Initialize central reference +rootjQuery = jQuery( document ); + + +var rparentsprev = /^(?:parents|prev(?:Until|All))/, + + // Methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend( { + has: function( target ) { + var targets = jQuery( target, this ), + l = targets.length; + + return this.filter( function() { + var i = 0; + for ( ; i < l; i++ ) { + if ( jQuery.contains( this, targets[ i ] ) ) { + return true; + } + } + } ); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + matched = [], + targets = typeof selectors !== "string" && jQuery( selectors ); + + // Positional selectors never match, since there's no _selection_ context + if ( !rneedsContext.test( selectors ) ) { + for ( ; i < l; i++ ) { + for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { + + // Always skip document fragments + if ( cur.nodeType < 11 && ( targets ? + targets.index( cur ) > -1 : + + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector( cur, selectors ) ) ) { + + matched.push( cur ); + break; + } + } + } + } + + return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); + }, + + // Determine the position of an element within the set + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; + } + + // Index in selector + if ( typeof elem === "string" ) { + return indexOf.call( jQuery( elem ), this[ 0 ] ); + } + + // Locate the position of the desired element + return indexOf.call( this, + + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[ 0 ] : elem + ); + }, + + add: function( selector, context ) { + return this.pushStack( + jQuery.uniqueSort( + jQuery.merge( this.get(), jQuery( selector, context ) ) + ) + ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter( selector ) + ); + } +} ); + +function sibling( cur, dir ) { + while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {} + return cur; +} + +jQuery.each( { + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return siblings( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return siblings( elem.firstChild ); + }, + contents: function( elem ) { + if ( nodeName( elem, "iframe" ) ) { + return elem.contentDocument; + } + + // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only + // Treat the template element as a regular one in browsers that + // don't support it. + if ( nodeName( elem, "template" ) ) { + elem = elem.content || elem; + } + + return jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var matched = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + matched = jQuery.filter( selector, matched ); + } + + if ( this.length > 1 ) { + + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + jQuery.uniqueSort( matched ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + matched.reverse(); + } + } + + return this.pushStack( matched ); + }; +} ); +var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g ); + + + +// Convert String-formatted options into Object-formatted ones +function createOptions( options ) { + var object = {}; + jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) { + object[ flag ] = true; + } ); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + createOptions( options ) : + jQuery.extend( {}, options ); + + var // Flag to know if list is currently firing + firing, + + // Last fire value for non-forgettable lists + memory, + + // Flag to know if list was already fired + fired, + + // Flag to prevent firing + locked, + + // Actual callback list + list = [], + + // Queue of execution data for repeatable lists + queue = [], + + // Index of currently firing callback (modified by add/remove as needed) + firingIndex = -1, + + // Fire callbacks + fire = function() { + + // Enforce single-firing + locked = locked || options.once; + + // Execute callbacks for all pending executions, + // respecting firingIndex overrides and runtime changes + fired = firing = true; + for ( ; queue.length; firingIndex = -1 ) { + memory = queue.shift(); + while ( ++firingIndex < list.length ) { + + // Run callback and check for early termination + if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && + options.stopOnFalse ) { + + // Jump to end and forget the data so .add doesn't re-fire + firingIndex = list.length; + memory = false; + } + } + } + + // Forget the data if we're done with it + if ( !options.memory ) { + memory = false; + } + + firing = false; + + // Clean up if we're done firing for good + if ( locked ) { + + // Keep an empty list if we have data for future add calls + if ( memory ) { + list = []; + + // Otherwise, this object is spent + } else { + list = ""; + } + } + }, + + // Actual Callbacks object + self = { + + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + + // If we have memory from a past run, we should fire after adding + if ( memory && !firing ) { + firingIndex = list.length - 1; + queue.push( memory ); + } + + ( function add( args ) { + jQuery.each( args, function( _, arg ) { + if ( isFunction( arg ) ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && toType( arg ) !== "string" ) { + + // Inspect recursively + add( arg ); + } + } ); + } )( arguments ); + + if ( memory && !firing ) { + fire(); + } + } + return this; + }, + + // Remove a callback from the list + remove: function() { + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + + // Handle firing indexes + if ( index <= firingIndex ) { + firingIndex--; + } + } + } ); + return this; + }, + + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? + jQuery.inArray( fn, list ) > -1 : + list.length > 0; + }, + + // Remove all callbacks from the list + empty: function() { + if ( list ) { + list = []; + } + return this; + }, + + // Disable .fire and .add + // Abort any current/pending executions + // Clear all callbacks and values + disable: function() { + locked = queue = []; + list = memory = ""; + return this; + }, + disabled: function() { + return !list; + }, + + // Disable .fire + // Also disable .add unless we have memory (since it would have no effect) + // Abort any pending executions + lock: function() { + locked = queue = []; + if ( !memory && !firing ) { + list = memory = ""; + } + return this; + }, + locked: function() { + return !!locked; + }, + + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( !locked ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + queue.push( args ); + if ( !firing ) { + fire(); + } + } + return this; + }, + + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; + + +function Identity( v ) { + return v; +} +function Thrower( ex ) { + throw ex; +} + +function adoptValue( value, resolve, reject, noValue ) { + var method; + + try { + + // Check for promise aspect first to privilege synchronous behavior + if ( value && isFunction( ( method = value.promise ) ) ) { + method.call( value ).done( resolve ).fail( reject ); + + // Other thenables + } else if ( value && isFunction( ( method = value.then ) ) ) { + method.call( value, resolve, reject ); + + // Other non-thenables + } else { + + // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer: + // * false: [ value ].slice( 0 ) => resolve( value ) + // * true: [ value ].slice( 1 ) => resolve() + resolve.apply( undefined, [ value ].slice( noValue ) ); + } + + // For Promises/A+, convert exceptions into rejections + // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in + // Deferred#then to conditionally suppress rejection. + } catch ( value ) { + + // Support: Android 4.0 only + // Strict mode functions invoked without .call/.apply get global-object context + reject.apply( undefined, [ value ] ); + } +} + +jQuery.extend( { + + Deferred: function( func ) { + var tuples = [ + + // action, add listener, callbacks, + // ... .then handlers, argument index, [final state] + [ "notify", "progress", jQuery.Callbacks( "memory" ), + jQuery.Callbacks( "memory" ), 2 ], + [ "resolve", "done", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 0, "resolved" ], + [ "reject", "fail", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 1, "rejected" ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + "catch": function( fn ) { + return promise.then( null, fn ); + }, + + // Keep pipe for back-compat + pipe: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + + return jQuery.Deferred( function( newDefer ) { + jQuery.each( tuples, function( i, tuple ) { + + // Map tuples (progress, done, fail) to arguments (done, fail, progress) + var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ]; + + // deferred.progress(function() { bind to newDefer or newDefer.notify }) + // deferred.done(function() { bind to newDefer or newDefer.resolve }) + // deferred.fail(function() { bind to newDefer or newDefer.reject }) + deferred[ tuple[ 1 ] ]( function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && isFunction( returned.promise ) ) { + returned.promise() + .progress( newDefer.notify ) + .done( newDefer.resolve ) + .fail( newDefer.reject ); + } else { + newDefer[ tuple[ 0 ] + "With" ]( + this, + fn ? [ returned ] : arguments + ); + } + } ); + } ); + fns = null; + } ).promise(); + }, + then: function( onFulfilled, onRejected, onProgress ) { + var maxDepth = 0; + function resolve( depth, deferred, handler, special ) { + return function() { + var that = this, + args = arguments, + mightThrow = function() { + var returned, then; + + // Support: Promises/A+ section 2.3.3.3.3 + // https://promisesaplus.com/#point-59 + // Ignore double-resolution attempts + if ( depth < maxDepth ) { + return; + } + + returned = handler.apply( that, args ); + + // Support: Promises/A+ section 2.3.1 + // https://promisesaplus.com/#point-48 + if ( returned === deferred.promise() ) { + throw new TypeError( "Thenable self-resolution" ); + } + + // Support: Promises/A+ sections 2.3.3.1, 3.5 + // https://promisesaplus.com/#point-54 + // https://promisesaplus.com/#point-75 + // Retrieve `then` only once + then = returned && + + // Support: Promises/A+ section 2.3.4 + // https://promisesaplus.com/#point-64 + // Only check objects and functions for thenability + ( typeof returned === "object" || + typeof returned === "function" ) && + returned.then; + + // Handle a returned thenable + if ( isFunction( then ) ) { + + // Special processors (notify) just wait for resolution + if ( special ) { + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ) + ); + + // Normal processors (resolve) also hook into progress + } else { + + // ...and disregard older resolution values + maxDepth++; + + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ), + resolve( maxDepth, deferred, Identity, + deferred.notifyWith ) + ); + } + + // Handle all other returned values + } else { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Identity ) { + that = undefined; + args = [ returned ]; + } + + // Process the value(s) + // Default process is resolve + ( special || deferred.resolveWith )( that, args ); + } + }, + + // Only normal processors (resolve) catch and reject exceptions + process = special ? + mightThrow : + function() { + try { + mightThrow(); + } catch ( e ) { + + if ( jQuery.Deferred.exceptionHook ) { + jQuery.Deferred.exceptionHook( e, + process.stackTrace ); + } + + // Support: Promises/A+ section 2.3.3.3.4.1 + // https://promisesaplus.com/#point-61 + // Ignore post-resolution exceptions + if ( depth + 1 >= maxDepth ) { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Thrower ) { + that = undefined; + args = [ e ]; + } + + deferred.rejectWith( that, args ); + } + } + }; + + // Support: Promises/A+ section 2.3.3.3.1 + // https://promisesaplus.com/#point-57 + // Re-resolve promises immediately to dodge false rejection from + // subsequent errors + if ( depth ) { + process(); + } else { + + // Call an optional hook to record the stack, in case of exception + // since it's otherwise lost when execution goes async + if ( jQuery.Deferred.getStackHook ) { + process.stackTrace = jQuery.Deferred.getStackHook(); + } + window.setTimeout( process ); + } + }; + } + + return jQuery.Deferred( function( newDefer ) { + + // progress_handlers.add( ... ) + tuples[ 0 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onProgress ) ? + onProgress : + Identity, + newDefer.notifyWith + ) + ); + + // fulfilled_handlers.add( ... ) + tuples[ 1 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onFulfilled ) ? + onFulfilled : + Identity + ) + ); + + // rejected_handlers.add( ... ) + tuples[ 2 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onRejected ) ? + onRejected : + Thrower + ) + ); + } ).promise(); + }, + + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 5 ]; + + // promise.progress = list.add + // promise.done = list.add + // promise.fail = list.add + promise[ tuple[ 1 ] ] = list.add; + + // Handle state + if ( stateString ) { + list.add( + function() { + + // state = "resolved" (i.e., fulfilled) + // state = "rejected" + state = stateString; + }, + + // rejected_callbacks.disable + // fulfilled_callbacks.disable + tuples[ 3 - i ][ 2 ].disable, + + // rejected_handlers.disable + // fulfilled_handlers.disable + tuples[ 3 - i ][ 3 ].disable, + + // progress_callbacks.lock + tuples[ 0 ][ 2 ].lock, + + // progress_handlers.lock + tuples[ 0 ][ 3 ].lock + ); + } + + // progress_handlers.fire + // fulfilled_handlers.fire + // rejected_handlers.fire + list.add( tuple[ 3 ].fire ); + + // deferred.notify = function() { deferred.notifyWith(...) } + // deferred.resolve = function() { deferred.resolveWith(...) } + // deferred.reject = function() { deferred.rejectWith(...) } + deferred[ tuple[ 0 ] ] = function() { + deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); + return this; + }; + + // deferred.notifyWith = list.fireWith + // deferred.resolveWith = list.fireWith + // deferred.rejectWith = list.fireWith + deferred[ tuple[ 0 ] + "With" ] = list.fireWith; + } ); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( singleValue ) { + var + + // count of uncompleted subordinates + remaining = arguments.length, + + // count of unprocessed arguments + i = remaining, + + // subordinate fulfillment data + resolveContexts = Array( i ), + resolveValues = slice.call( arguments ), + + // the master Deferred + master = jQuery.Deferred(), + + // subordinate callback factory + updateFunc = function( i ) { + return function( value ) { + resolveContexts[ i ] = this; + resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; + if ( !( --remaining ) ) { + master.resolveWith( resolveContexts, resolveValues ); + } + }; + }; + + // Single- and empty arguments are adopted like Promise.resolve + if ( remaining <= 1 ) { + adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject, + !remaining ); + + // Use .then() to unwrap secondary thenables (cf. gh-3000) + if ( master.state() === "pending" || + isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { + + return master.then(); + } + } + + // Multiple arguments are aggregated like Promise.all array elements + while ( i-- ) { + adoptValue( resolveValues[ i ], updateFunc( i ), master.reject ); + } + + return master.promise(); + } +} ); + + +// These usually indicate a programmer mistake during development, +// warn about them ASAP rather than swallowing them by default. +var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/; + +jQuery.Deferred.exceptionHook = function( error, stack ) { + + // Support: IE 8 - 9 only + // Console exists when dev tools are open, which can happen at any time + if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) { + window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack ); + } +}; + + + + +jQuery.readyException = function( error ) { + window.setTimeout( function() { + throw error; + } ); +}; + + + + +// The deferred used on DOM ready +var readyList = jQuery.Deferred(); + +jQuery.fn.ready = function( fn ) { + + readyList + .then( fn ) + + // Wrap jQuery.readyException in a function so that the lookup + // happens at the time of error handling instead of callback + // registration. + .catch( function( error ) { + jQuery.readyException( error ); + } ); + + return this; +}; + +jQuery.extend( { + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + } +} ); + +jQuery.ready.then = readyList.then; + +// The ready event handler and self cleanup method +function completed() { + document.removeEventListener( "DOMContentLoaded", completed ); + window.removeEventListener( "load", completed ); + jQuery.ready(); +} + +// Catch cases where $(document).ready() is called +// after the browser event has already occurred. +// Support: IE <=9 - 10 only +// Older IE sometimes signals "interactive" too soon +if ( document.readyState === "complete" || + ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { + + // Handle it asynchronously to allow scripts the opportunity to delay ready + window.setTimeout( jQuery.ready ); + +} else { + + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed ); +} + + + + +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function +var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + len = elems.length, + bulk = key == null; + + // Sets many values + if ( toType( key ) === "object" ) { + chainable = true; + for ( i in key ) { + access( elems, fn, i, key[ i ], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < len; i++ ) { + fn( + elems[ i ], key, raw ? + value : + value.call( elems[ i ], i, fn( elems[ i ], key ) ) + ); + } + } + } + + if ( chainable ) { + return elems; + } + + // Gets + if ( bulk ) { + return fn.call( elems ); + } + + return len ? fn( elems[ 0 ], key ) : emptyGet; +}; + + +// Matches dashed string for camelizing +var rmsPrefix = /^-ms-/, + rdashAlpha = /-([a-z])/g; + +// Used by camelCase as callback to replace() +function fcamelCase( all, letter ) { + return letter.toUpperCase(); +} + +// Convert dashed to camelCase; used by the css and data modules +// Support: IE <=9 - 11, Edge 12 - 15 +// Microsoft forgot to hump their vendor prefix (#9572) +function camelCase( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); +} +var acceptData = function( owner ) { + + // Accepts only: + // - Node + // - Node.ELEMENT_NODE + // - Node.DOCUMENT_NODE + // - Object + // - Any + return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); +}; + + + + +function Data() { + this.expando = jQuery.expando + Data.uid++; +} + +Data.uid = 1; + +Data.prototype = { + + cache: function( owner ) { + + // Check if the owner object already has a cache + var value = owner[ this.expando ]; + + // If not, create one + if ( !value ) { + value = {}; + + // We can accept data for non-element nodes in modern browsers, + // but we should not, see #8335. + // Always return an empty object. + if ( acceptData( owner ) ) { + + // If it is a node unlikely to be stringify-ed or looped over + // use plain assignment + if ( owner.nodeType ) { + owner[ this.expando ] = value; + + // Otherwise secure it in a non-enumerable property + // configurable must be true to allow the property to be + // deleted when data is removed + } else { + Object.defineProperty( owner, this.expando, { + value: value, + configurable: true + } ); + } + } + } + + return value; + }, + set: function( owner, data, value ) { + var prop, + cache = this.cache( owner ); + + // Handle: [ owner, key, value ] args + // Always use camelCase key (gh-2257) + if ( typeof data === "string" ) { + cache[ camelCase( data ) ] = value; + + // Handle: [ owner, { properties } ] args + } else { + + // Copy the properties one-by-one to the cache object + for ( prop in data ) { + cache[ camelCase( prop ) ] = data[ prop ]; + } + } + return cache; + }, + get: function( owner, key ) { + return key === undefined ? + this.cache( owner ) : + + // Always use camelCase key (gh-2257) + owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ]; + }, + access: function( owner, key, value ) { + + // In cases where either: + // + // 1. No key was specified + // 2. A string key was specified, but no value provided + // + // Take the "read" path and allow the get method to determine + // which value to return, respectively either: + // + // 1. The entire cache object + // 2. The data stored at the key + // + if ( key === undefined || + ( ( key && typeof key === "string" ) && value === undefined ) ) { + + return this.get( owner, key ); + } + + // When the key is not a string, or both a key and value + // are specified, set or extend (existing objects) with either: + // + // 1. An object of properties + // 2. A key and value + // + this.set( owner, key, value ); + + // Since the "set" path can have two possible entry points + // return the expected data based on which path was taken[*] + return value !== undefined ? value : key; + }, + remove: function( owner, key ) { + var i, + cache = owner[ this.expando ]; + + if ( cache === undefined ) { + return; + } + + if ( key !== undefined ) { + + // Support array or space separated string of keys + if ( Array.isArray( key ) ) { + + // If key is an array of keys... + // We always set camelCase keys, so remove that. + key = key.map( camelCase ); + } else { + key = camelCase( key ); + + // If a key with the spaces exists, use it. + // Otherwise, create an array by matching non-whitespace + key = key in cache ? + [ key ] : + ( key.match( rnothtmlwhite ) || [] ); + } + + i = key.length; + + while ( i-- ) { + delete cache[ key[ i ] ]; + } + } + + // Remove the expando if there's no more data + if ( key === undefined || jQuery.isEmptyObject( cache ) ) { + + // Support: Chrome <=35 - 45 + // Webkit & Blink performance suffers when deleting properties + // from DOM nodes, so set to undefined instead + // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted) + if ( owner.nodeType ) { + owner[ this.expando ] = undefined; + } else { + delete owner[ this.expando ]; + } + } + }, + hasData: function( owner ) { + var cache = owner[ this.expando ]; + return cache !== undefined && !jQuery.isEmptyObject( cache ); + } +}; +var dataPriv = new Data(); + +var dataUser = new Data(); + + + +// Implementation Summary +// +// 1. Enforce API surface and semantic compatibility with 1.9.x branch +// 2. Improve the module's maintainability by reducing the storage +// paths to a single mechanism. +// 3. Use the same single mechanism to support "private" and "user" data. +// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) +// 5. Avoid exposing implementation details on user objects (eg. expando properties) +// 6. Provide a clear path for implementation upgrade to WeakMap in 2014 + +var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, + rmultiDash = /[A-Z]/g; + +function getData( data ) { + if ( data === "true" ) { + return true; + } + + if ( data === "false" ) { + return false; + } + + if ( data === "null" ) { + return null; + } + + // Only convert to a number if it doesn't change the string + if ( data === +data + "" ) { + return +data; + } + + if ( rbrace.test( data ) ) { + return JSON.parse( data ); + } + + return data; +} + +function dataAttr( elem, key, data ) { + var name; + + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase(); + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = getData( data ); + } catch ( e ) {} + + // Make sure we set the data so it isn't changed later + dataUser.set( elem, key, data ); + } else { + data = undefined; + } + } + return data; +} + +jQuery.extend( { + hasData: function( elem ) { + return dataUser.hasData( elem ) || dataPriv.hasData( elem ); + }, + + data: function( elem, name, data ) { + return dataUser.access( elem, name, data ); + }, + + removeData: function( elem, name ) { + dataUser.remove( elem, name ); + }, + + // TODO: Now that all calls to _data and _removeData have been replaced + // with direct calls to dataPriv methods, these can be deprecated. + _data: function( elem, name, data ) { + return dataPriv.access( elem, name, data ); + }, + + _removeData: function( elem, name ) { + dataPriv.remove( elem, name ); + } +} ); + +jQuery.fn.extend( { + data: function( key, value ) { + var i, name, data, + elem = this[ 0 ], + attrs = elem && elem.attributes; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = dataUser.get( elem ); + + if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { + i = attrs.length; + while ( i-- ) { + + // Support: IE 11 only + // The attrs elements can be null (#14894) + if ( attrs[ i ] ) { + name = attrs[ i ].name; + if ( name.indexOf( "data-" ) === 0 ) { + name = camelCase( name.slice( 5 ) ); + dataAttr( elem, name, data[ name ] ); + } + } + } + dataPriv.set( elem, "hasDataAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each( function() { + dataUser.set( this, key ); + } ); + } + + return access( this, function( value ) { + var data; + + // The calling jQuery object (element matches) is not empty + // (and therefore has an element appears at this[ 0 ]) and the + // `value` parameter was not undefined. An empty jQuery object + // will result in `undefined` for elem = this[ 0 ] which will + // throw an exception if an attempt to read a data cache is made. + if ( elem && value === undefined ) { + + // Attempt to get data from the cache + // The key will always be camelCased in Data + data = dataUser.get( elem, key ); + if ( data !== undefined ) { + return data; + } + + // Attempt to "discover" the data in + // HTML5 custom data-* attrs + data = dataAttr( elem, key ); + if ( data !== undefined ) { + return data; + } + + // We tried really hard, but the data doesn't exist. + return; + } + + // Set the data... + this.each( function() { + + // We always store the camelCased key + dataUser.set( this, key, value ); + } ); + }, null, value, arguments.length > 1, null, true ); + }, + + removeData: function( key ) { + return this.each( function() { + dataUser.remove( this, key ); + } ); + } +} ); + + +jQuery.extend( { + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = dataPriv.get( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || Array.isArray( data ) ) { + queue = dataPriv.access( elem, type, jQuery.makeArray( data ) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // Clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // Not public - generate a queueHooks object, or return the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return dataPriv.get( elem, key ) || dataPriv.access( elem, key, { + empty: jQuery.Callbacks( "once memory" ).add( function() { + dataPriv.remove( elem, [ type + "queue", key ] ); + } ) + } ); + } +} ); + +jQuery.fn.extend( { + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[ 0 ], type ); + } + + return data === undefined ? + this : + this.each( function() { + var queue = jQuery.queue( this, type, data ); + + // Ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + } ); + }, + dequeue: function( type ) { + return this.each( function() { + jQuery.dequeue( this, type ); + } ); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while ( i-- ) { + tmp = dataPriv.get( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +} ); +var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; + +var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); + + +var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; + +var isHiddenWithinTree = function( elem, el ) { + + // isHiddenWithinTree might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + + // Inline style trumps all + return elem.style.display === "none" || + elem.style.display === "" && + + // Otherwise, check computed style + // Support: Firefox <=43 - 45 + // Disconnected elements can have computed display: none, so first confirm that elem is + // in the document. + jQuery.contains( elem.ownerDocument, elem ) && + + jQuery.css( elem, "display" ) === "none"; + }; + +var swap = function( elem, options, callback, args ) { + var ret, name, + old = {}; + + // Remember the old values, and insert the new ones + for ( name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + ret = callback.apply( elem, args || [] ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + + return ret; +}; + + + + +function adjustCSS( elem, prop, valueParts, tween ) { + var adjusted, scale, + maxIterations = 20, + currentValue = tween ? + function() { + return tween.cur(); + } : + function() { + return jQuery.css( elem, prop, "" ); + }, + initial = currentValue(), + unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), + + // Starting value computation is required for potential unit mismatches + initialInUnit = ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && + rcssNum.exec( jQuery.css( elem, prop ) ); + + if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { + + // Support: Firefox <=54 + // Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144) + initial = initial / 2; + + // Trust units reported by jQuery.css + unit = unit || initialInUnit[ 3 ]; + + // Iteratively approximate from a nonzero starting point + initialInUnit = +initial || 1; + + while ( maxIterations-- ) { + + // Evaluate and update our best guess (doubling guesses that zero out). + // Finish if the scale equals or crosses 1 (making the old*new product non-positive). + jQuery.style( elem, prop, initialInUnit + unit ); + if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) { + maxIterations = 0; + } + initialInUnit = initialInUnit / scale; + + } + + initialInUnit = initialInUnit * 2; + jQuery.style( elem, prop, initialInUnit + unit ); + + // Make sure we update the tween properties later on + valueParts = valueParts || []; + } + + if ( valueParts ) { + initialInUnit = +initialInUnit || +initial || 0; + + // Apply relative offset (+=/-=) if specified + adjusted = valueParts[ 1 ] ? + initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : + +valueParts[ 2 ]; + if ( tween ) { + tween.unit = unit; + tween.start = initialInUnit; + tween.end = adjusted; + } + } + return adjusted; +} + + +var defaultDisplayMap = {}; + +function getDefaultDisplay( elem ) { + var temp, + doc = elem.ownerDocument, + nodeName = elem.nodeName, + display = defaultDisplayMap[ nodeName ]; + + if ( display ) { + return display; + } + + temp = doc.body.appendChild( doc.createElement( nodeName ) ); + display = jQuery.css( temp, "display" ); + + temp.parentNode.removeChild( temp ); + + if ( display === "none" ) { + display = "block"; + } + defaultDisplayMap[ nodeName ] = display; + + return display; +} + +function showHide( elements, show ) { + var display, elem, + values = [], + index = 0, + length = elements.length; + + // Determine new display value for elements that need to change + for ( ; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + + display = elem.style.display; + if ( show ) { + + // Since we force visibility upon cascade-hidden elements, an immediate (and slow) + // check is required in this first loop unless we have a nonempty display value (either + // inline or about-to-be-restored) + if ( display === "none" ) { + values[ index ] = dataPriv.get( elem, "display" ) || null; + if ( !values[ index ] ) { + elem.style.display = ""; + } + } + if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) { + values[ index ] = getDefaultDisplay( elem ); + } + } else { + if ( display !== "none" ) { + values[ index ] = "none"; + + // Remember what we're overwriting + dataPriv.set( elem, "display", display ); + } + } + } + + // Set the display of the elements in a second loop to avoid constant reflow + for ( index = 0; index < length; index++ ) { + if ( values[ index ] != null ) { + elements[ index ].style.display = values[ index ]; + } + } + + return elements; +} + +jQuery.fn.extend( { + show: function() { + return showHide( this, true ); + }, + hide: function() { + return showHide( this ); + }, + toggle: function( state ) { + if ( typeof state === "boolean" ) { + return state ? this.show() : this.hide(); + } + + return this.each( function() { + if ( isHiddenWithinTree( this ) ) { + jQuery( this ).show(); + } else { + jQuery( this ).hide(); + } + } ); + } +} ); +var rcheckableType = ( /^(?:checkbox|radio)$/i ); + +var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]+)/i ); + +var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i ); + + + +// We have to close these tags to support XHTML (#13200) +var wrapMap = { + + // Support: IE <=9 only + option: [ 1, "<select multiple='multiple'>", "</select>" ], + + // XHTML parsers do not magically insert elements in the + // same way that tag soup parsers do. So we cannot shorten + // this by omitting <tbody> or other required elements. + thead: [ 1, "<table>", "</table>" ], + col: [ 2, "<table><colgroup>", "</colgroup></table>" ], + tr: [ 2, "<table><tbody>", "</tbody></table>" ], + td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ], + + _default: [ 0, "", "" ] +}; + +// Support: IE <=9 only +wrapMap.optgroup = wrapMap.option; + +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + + +function getAll( context, tag ) { + + // Support: IE <=9 - 11 only + // Use typeof to avoid zero-argument method invocation on host objects (#15151) + var ret; + + if ( typeof context.getElementsByTagName !== "undefined" ) { + ret = context.getElementsByTagName( tag || "*" ); + + } else if ( typeof context.querySelectorAll !== "undefined" ) { + ret = context.querySelectorAll( tag || "*" ); + + } else { + ret = []; + } + + if ( tag === undefined || tag && nodeName( context, tag ) ) { + return jQuery.merge( [ context ], ret ); + } + + return ret; +} + + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + dataPriv.set( + elems[ i ], + "globalEval", + !refElements || dataPriv.get( refElements[ i ], "globalEval" ) + ); + } +} + + +var rhtml = /<|&#?\w+;/; + +function buildFragment( elems, context, scripts, selection, ignored ) { + var elem, tmp, tag, wrap, contains, j, + fragment = context.createDocumentFragment(), + nodes = [], + i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( toType( elem ) === "object" ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); + + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; + + // Descend through wrappers to the right content + j = wrap[ 0 ]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, tmp.childNodes ); + + // Remember the top-level container + tmp = fragment.firstChild; + + // Ensure the created nodes are orphaned (#12392) + tmp.textContent = ""; + } + } + } + + // Remove wrapper from fragment + fragment.textContent = ""; + + i = 0; + while ( ( elem = nodes[ i++ ] ) ) { + + // Skip elements already in the context collection (trac-4087) + if ( selection && jQuery.inArray( elem, selection ) > -1 ) { + if ( ignored ) { + ignored.push( elem ); + } + continue; + } + + contains = jQuery.contains( elem.ownerDocument, elem ); + + // Append to fragment + tmp = getAll( fragment.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( contains ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( ( elem = tmp[ j++ ] ) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + return fragment; +} + + +( function() { + var fragment = document.createDocumentFragment(), + div = fragment.appendChild( document.createElement( "div" ) ), + input = document.createElement( "input" ); + + // Support: Android 4.0 - 4.3 only + // Check state lost if the name is set (#11217) + // Support: Windows Web Apps (WWA) + // `name` and `type` must use .setAttribute for WWA (#14901) + input.setAttribute( "type", "radio" ); + input.setAttribute( "checked", "checked" ); + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + + // Support: Android <=4.1 only + // Older WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE <=11 only + // Make sure textarea (and checkbox) defaultValue is properly cloned + div.innerHTML = "<textarea>x</textarea>"; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; +} )(); +var documentElement = document.documentElement; + + + +var + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +// Support: IE <=9 only +// See #13393 for more info +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +function on( elem, types, selector, data, fn, one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + on( elem, type, selector, data, types[ type ], one ); + } + return elem; + } + + if ( data == null && fn == null ) { + + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return elem; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return elem.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + } ); +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + + var handleObjIn, eventHandle, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.get( elem ); + + // Don't attach events to noData or text/comment nodes (but allow plain objects) + if ( !elemData ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Ensure that invalid selectors throw exceptions at attach time + // Evaluate against documentElement in case elem is a non-element node (e.g., document) + if ( selector ) { + jQuery.find.matchesSelector( documentElement, selector ); + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !( events = elemData.events ) ) { + events = elemData.events = {}; + } + if ( !( eventHandle = elemData.handle ) ) { + eventHandle = elemData.handle = function( e ) { + + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? + jQuery.event.dispatch.apply( elem, arguments ) : undefined; + }; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend( { + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join( "." ) + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !( handlers = events[ type ] ) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener if the special events handler returns false + if ( !special.setup || + special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var j, origCount, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); + + if ( !elemData || !( events = elemData.events ) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[ 2 ] && + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || + selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || + special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove data and the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + dataPriv.remove( elem, "handle events" ); + } + }, + + dispatch: function( nativeEvent ) { + + // Make a writable jQuery.Event from the native event object + var event = jQuery.event.fix( nativeEvent ); + + var i, j, ret, matched, handleObj, handlerQueue, + args = new Array( arguments.length ), + handlers = ( dataPriv.get( this, "events" ) || {} )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[ 0 ] = event; + + for ( i = 1; i < arguments.length; i++ ) { + args[ i ] = arguments[ i ]; + } + + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( ( handleObj = matched.handlers[ j++ ] ) && + !event.isImmediatePropagationStopped() ) { + + // Triggered event must either 1) have no namespace, or 2) have namespace(s) + // a subset or equal to those in the bound event (both can have no namespace). + if ( !event.rnamespace || event.rnamespace.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || + handleObj.handler ).apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( ( event.result = ret ) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var i, handleObj, sel, matchedHandlers, matchedSelectors, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + if ( delegateCount && + + // Support: IE <=9 + // Black-hole SVG <use> instance trees (trac-13180) + cur.nodeType && + + // Support: Firefox <=42 + // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861) + // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click + // Support: IE 11 only + // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343) + !( event.type === "click" && event.button >= 1 ) ) { + + for ( ; cur !== this; cur = cur.parentNode || this ) { + + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) { + matchedHandlers = []; + matchedSelectors = {}; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matchedSelectors[ sel ] === undefined ) { + matchedSelectors[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) > -1 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matchedSelectors[ sel ] ) { + matchedHandlers.push( handleObj ); + } + } + if ( matchedHandlers.length ) { + handlerQueue.push( { elem: cur, handlers: matchedHandlers } ); + } + } + } + } + + // Add the remaining (directly-bound) handlers + cur = this; + if ( delegateCount < handlers.length ) { + handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } ); + } + + return handlerQueue; + }, + + addProp: function( name, hook ) { + Object.defineProperty( jQuery.Event.prototype, name, { + enumerable: true, + configurable: true, + + get: isFunction( hook ) ? + function() { + if ( this.originalEvent ) { + return hook( this.originalEvent ); + } + } : + function() { + if ( this.originalEvent ) { + return this.originalEvent[ name ]; + } + }, + + set: function( value ) { + Object.defineProperty( this, name, { + enumerable: true, + configurable: true, + writable: true, + value: value + } ); + } + } ); + }, + + fix: function( originalEvent ) { + return originalEvent[ jQuery.expando ] ? + originalEvent : + new jQuery.Event( originalEvent ); + }, + + special: { + load: { + + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + focus: { + + // Fire native event if possible so blur/focus sequence is correct + trigger: function() { + if ( this !== safeActiveElement() && this.focus ) { + this.focus(); + return false; + } + }, + delegateType: "focusin" + }, + blur: { + trigger: function() { + if ( this === safeActiveElement() && this.blur ) { + this.blur(); + return false; + } + }, + delegateType: "focusout" + }, + click: { + + // For checkbox, fire native event so checked state will be right + trigger: function() { + if ( this.type === "checkbox" && this.click && nodeName( this, "input" ) ) { + this.click(); + return false; + } + }, + + // For cross-browser consistency, don't fire native .click() on links + _default: function( event ) { + return nodeName( event.target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Support: Firefox 20+ + // Firefox doesn't alert if the returnValue field is not set. + if ( event.result !== undefined && event.originalEvent ) { + event.originalEvent.returnValue = event.result; + } + } + } + } +}; + +jQuery.removeEvent = function( elem, type, handle ) { + + // This "if" is needed for plain objects + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle ); + } +}; + +jQuery.Event = function( src, props ) { + + // Allow instantiation without the 'new' keyword + if ( !( this instanceof jQuery.Event ) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = src.defaultPrevented || + src.defaultPrevented === undefined && + + // Support: Android <=2.3 only + src.returnValue === false ? + returnTrue : + returnFalse; + + // Create target properties + // Support: Safari <=6 - 7 only + // Target should not be a text node (#504, #13143) + this.target = ( src.target && src.target.nodeType === 3 ) ? + src.target.parentNode : + src.target; + + this.currentTarget = src.currentTarget; + this.relatedTarget = src.relatedTarget; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || Date.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + constructor: jQuery.Event, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + isSimulated: false, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + + if ( e && !this.isSimulated ) { + e.preventDefault(); + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopPropagation(); + } + }, + stopImmediatePropagation: function() { + var e = this.originalEvent; + + this.isImmediatePropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopImmediatePropagation(); + } + + this.stopPropagation(); + } +}; + +// Includes all common event props including KeyEvent and MouseEvent specific props +jQuery.each( { + altKey: true, + bubbles: true, + cancelable: true, + changedTouches: true, + ctrlKey: true, + detail: true, + eventPhase: true, + metaKey: true, + pageX: true, + pageY: true, + shiftKey: true, + view: true, + "char": true, + charCode: true, + key: true, + keyCode: true, + button: true, + buttons: true, + clientX: true, + clientY: true, + offsetX: true, + offsetY: true, + pointerId: true, + pointerType: true, + screenX: true, + screenY: true, + targetTouches: true, + toElement: true, + touches: true, + + which: function( event ) { + var button = event.button; + + // Add which for key events + if ( event.which == null && rkeyEvent.test( event.type ) ) { + return event.charCode != null ? event.charCode : event.keyCode; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + if ( !event.which && button !== undefined && rmouseEvent.test( event.type ) ) { + if ( button & 1 ) { + return 1; + } + + if ( button & 2 ) { + return 3; + } + + if ( button & 4 ) { + return 2; + } + + return 0; + } + + return event.which; + } +}, jQuery.event.addProp ); + +// Create mouseenter/leave events using mouseover/out and event-time checks +// so that event delegation works in jQuery. +// Do the same for pointerenter/pointerleave and pointerover/pointerout +// +// Support: Safari 7 only +// Safari sends mouseenter too often; see: +// https://bugs.chromium.org/p/chromium/issues/detail?id=470258 +// for the description of the bug (it existed in older Chrome versions as well). +jQuery.each( { + mouseenter: "mouseover", + mouseleave: "mouseout", + pointerenter: "pointerover", + pointerleave: "pointerout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mouseenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +} ); + +jQuery.fn.extend( { + + on: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn ); + }, + one: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? + handleObj.origType + "." + handleObj.namespace : + handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each( function() { + jQuery.event.remove( this, types, fn, selector ); + } ); + } +} ); + + +var + + /* eslint-disable max-len */ + + // See https://github.com/eslint/eslint/issues/3229 + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi, + + /* eslint-enable */ + + // Support: IE <=10 - 11, Edge 12 - 13 only + // In IE/Edge using regex groups here causes severe slowdowns. + // See https://connect.microsoft.com/IE/feedback/details/1736512/ + rnoInnerhtml = /<script|<style|<link/i, + + // checked="checked" or checked + rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, + rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g; + +// Prefer a tbody over its parent table for containing new rows +function manipulationTarget( elem, content ) { + if ( nodeName( elem, "table" ) && + nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) { + + return jQuery( elem ).children( "tbody" )[ 0 ] || elem; + } + + return elem; +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) { + elem.type = elem.type.slice( 5 ); + } else { + elem.removeAttribute( "type" ); + } + + return elem; +} + +function cloneCopyEvent( src, dest ) { + var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events; + + if ( dest.nodeType !== 1 ) { + return; + } + + // 1. Copy private data: events, handlers, etc. + if ( dataPriv.hasData( src ) ) { + pdataOld = dataPriv.access( src ); + pdataCur = dataPriv.set( dest, pdataOld ); + events = pdataOld.events; + + if ( events ) { + delete pdataCur.handle; + pdataCur.events = {}; + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + } + + // 2. Copy user data + if ( dataUser.hasData( src ) ) { + udataOld = dataUser.access( src ); + udataCur = jQuery.extend( {}, udataOld ); + + dataUser.set( dest, udataCur ); + } +} + +// Fix IE bugs, see support tests +function fixInput( src, dest ) { + var nodeName = dest.nodeName.toLowerCase(); + + // Fails to persist the checked state of a cloned checkbox or radio button. + if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + dest.checked = src.checked; + + // Fails to return the selected option to the default selected state when cloning options + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +function domManip( collection, args, callback, ignored ) { + + // Flatten any nested arrays + args = concat.apply( [], args ); + + var fragment, first, scripts, hasScripts, node, doc, + i = 0, + l = collection.length, + iNoClone = l - 1, + value = args[ 0 ], + valueIsFunction = isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( valueIsFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return collection.each( function( index ) { + var self = collection.eq( index ); + if ( valueIsFunction ) { + args[ 0 ] = value.call( this, index, self.html() ); + } + domManip( self, args, callback, ignored ); + } ); + } + + if ( l ) { + fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + // Require either new content or an interest in ignored elements to invoke the callback + if ( first || ignored ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item + // instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( collection[ i ], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !dataPriv.access( node, "globalEval" ) && + jQuery.contains( doc, node ) ) { + + if ( node.src && ( node.type || "" ).toLowerCase() !== "module" ) { + + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl ) { + jQuery._evalUrl( node.src ); + } + } else { + DOMEval( node.textContent.replace( rcleanScript, "" ), doc, node ); + } + } + } + } + } + } + + return collection; +} + +function remove( elem, selector, keepData ) { + var node, + nodes = selector ? jQuery.filter( selector, elem ) : elem, + i = 0; + + for ( ; ( node = nodes[ i ] ) != null; i++ ) { + if ( !keepData && node.nodeType === 1 ) { + jQuery.cleanData( getAll( node ) ); + } + + if ( node.parentNode ) { + if ( keepData && jQuery.contains( node.ownerDocument, node ) ) { + setGlobalEval( getAll( node, "script" ) ); + } + node.parentNode.removeChild( node ); + } + } + + return elem; +} + +jQuery.extend( { + htmlPrefilter: function( html ) { + return html.replace( rxhtmlTag, "<$1></$2>" ); + }, + + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var i, l, srcElements, destElements, + clone = elem.cloneNode( true ), + inPage = jQuery.contains( elem.ownerDocument, elem ); + + // Fix IE cloning issues + if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && + !jQuery.isXMLDoc( elem ) ) { + + // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + fixInput( srcElements[ i ], destElements[ i ] ); + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + cloneCopyEvent( srcElements[ i ], destElements[ i ] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + // Return the cloned set + return clone; + }, + + cleanData: function( elems ) { + var data, elem, type, + special = jQuery.event.special, + i = 0; + + for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { + if ( acceptData( elem ) ) { + if ( ( data = elem[ dataPriv.expando ] ) ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataPriv.expando ] = undefined; + } + if ( elem[ dataUser.expando ] ) { + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataUser.expando ] = undefined; + } + } + } + } +} ); + +jQuery.fn.extend( { + detach: function( selector ) { + return remove( this, selector, true ); + }, + + remove: function( selector ) { + return remove( this, selector ); + }, + + text: function( value ) { + return access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().each( function() { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + this.textContent = value; + } + } ); + }, null, value, arguments.length ); + }, + + append: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + } ); + }, + + prepend: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + } ); + }, + + before: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + } ); + }, + + after: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + } ); + }, + + empty: function() { + var elem, + i = 0; + + for ( ; ( elem = this[ i ] ) != null; i++ ) { + if ( elem.nodeType === 1 ) { + + // Prevent memory leaks + jQuery.cleanData( getAll( elem, false ) ); + + // Remove any remaining nodes + elem.textContent = ""; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function() { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + } ); + }, + + html: function( value ) { + return access( this, function( value ) { + var elem = this[ 0 ] || {}, + i = 0, + l = this.length; + + if ( value === undefined && elem.nodeType === 1 ) { + return elem.innerHTML; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { + + value = jQuery.htmlPrefilter( value ); + + try { + for ( ; i < l; i++ ) { + elem = this[ i ] || {}; + + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch ( e ) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var ignored = []; + + // Make the changes, replacing each non-ignored context element with the new content + return domManip( this, arguments, function( elem ) { + var parent = this.parentNode; + + if ( jQuery.inArray( this, ignored ) < 0 ) { + jQuery.cleanData( getAll( this ) ); + if ( parent ) { + parent.replaceChild( elem, this ); + } + } + + // Force callback invocation + }, ignored ); + } +} ); + +jQuery.each( { + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1, + i = 0; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone( true ); + jQuery( insert[ i ] )[ original ]( elems ); + + // Support: Android <=4.0 only, PhantomJS 1 only + // .get() because push.apply(_, arraylike) throws on ancient WebKit + push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +} ); +var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); + +var getStyles = function( elem ) { + + // Support: IE <=11 only, Firefox <=30 (#15098, #14150) + // IE throws on elements created in popups + // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" + var view = elem.ownerDocument.defaultView; + + if ( !view || !view.opener ) { + view = window; + } + + return view.getComputedStyle( elem ); + }; + +var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" ); + + + +( function() { + + // Executing both pixelPosition & boxSizingReliable tests require only one layout + // so they're executed at the same time to save the second computation. + function computeStyleTests() { + + // This is a singleton, we need to execute it only once + if ( !div ) { + return; + } + + container.style.cssText = "position:absolute;left:-11111px;width:60px;" + + "margin-top:1px;padding:0;border:0"; + div.style.cssText = + "position:relative;display:block;box-sizing:border-box;overflow:scroll;" + + "margin:auto;border:1px;padding:1px;" + + "width:60%;top:1%"; + documentElement.appendChild( container ).appendChild( div ); + + var divStyle = window.getComputedStyle( div ); + pixelPositionVal = divStyle.top !== "1%"; + + // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44 + reliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12; + + // Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3 + // Some styles come back with percentage values, even though they shouldn't + div.style.right = "60%"; + pixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36; + + // Support: IE 9 - 11 only + // Detect misreporting of content dimensions for box-sizing:border-box elements + boxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36; + + // Support: IE 9 only + // Detect overflow:scroll screwiness (gh-3699) + div.style.position = "absolute"; + scrollboxSizeVal = div.offsetWidth === 36 || "absolute"; + + documentElement.removeChild( container ); + + // Nullify the div so it wouldn't be stored in the memory and + // it will also be a sign that checks already performed + div = null; + } + + function roundPixelMeasures( measure ) { + return Math.round( parseFloat( measure ) ); + } + + var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal, + reliableMarginLeftVal, + container = document.createElement( "div" ), + div = document.createElement( "div" ); + + // Finish early in limited (non-browser) environments + if ( !div.style ) { + return; + } + + // Support: IE <=9 - 11 only + // Style of cloned element affects source element cloned (#8908) + div.style.backgroundClip = "content-box"; + div.cloneNode( true ).style.backgroundClip = ""; + support.clearCloneStyle = div.style.backgroundClip === "content-box"; + + jQuery.extend( support, { + boxSizingReliable: function() { + computeStyleTests(); + return boxSizingReliableVal; + }, + pixelBoxStyles: function() { + computeStyleTests(); + return pixelBoxStylesVal; + }, + pixelPosition: function() { + computeStyleTests(); + return pixelPositionVal; + }, + reliableMarginLeft: function() { + computeStyleTests(); + return reliableMarginLeftVal; + }, + scrollboxSize: function() { + computeStyleTests(); + return scrollboxSizeVal; + } + } ); +} )(); + + +function curCSS( elem, name, computed ) { + var width, minWidth, maxWidth, ret, + + // Support: Firefox 51+ + // Retrieving style before computed somehow + // fixes an issue with getting wrong values + // on detached elements + style = elem.style; + + computed = computed || getStyles( elem ); + + // getPropertyValue is needed for: + // .css('filter') (IE 9 only, #12537) + // .css('--customProperty) (#3144) + if ( computed ) { + ret = computed.getPropertyValue( name ) || computed[ name ]; + + if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) { + ret = jQuery.style( elem, name ); + } + + // A tribute to the "awesome hack by Dean Edwards" + // Android Browser returns percentage for some values, + // but width seems to be reliably pixels. + // This is against the CSSOM draft spec: + // https://drafts.csswg.org/cssom/#resolved-values + if ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) { + + // Remember the original values + width = style.width; + minWidth = style.minWidth; + maxWidth = style.maxWidth; + + // Put in the new values to get a computed value out + style.minWidth = style.maxWidth = style.width = ret; + ret = computed.width; + + // Revert the changed values + style.width = width; + style.minWidth = minWidth; + style.maxWidth = maxWidth; + } + } + + return ret !== undefined ? + + // Support: IE <=9 - 11 only + // IE returns zIndex value as an integer. + ret + "" : + ret; +} + + +function addGetHookIf( conditionFn, hookFn ) { + + // Define the hook, we'll check on the first run if it's really needed. + return { + get: function() { + if ( conditionFn() ) { + + // Hook not needed (or it's not possible to use it due + // to missing dependency), remove it. + delete this.get; + return; + } + + // Hook needed; redefine it so that the support test is not executed again. + return ( this.get = hookFn ).apply( this, arguments ); + } + }; +} + + +var + + // Swappable if display is none or starts with table + // except "table", "table-cell", or "table-caption" + // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + rdisplayswap = /^(none|table(?!-c[ea]).+)/, + rcustomProp = /^--/, + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + cssNormalTransform = { + letterSpacing: "0", + fontWeight: "400" + }, + + cssPrefixes = [ "Webkit", "Moz", "ms" ], + emptyStyle = document.createElement( "div" ).style; + +// Return a css property mapped to a potentially vendor prefixed property +function vendorPropName( name ) { + + // Shortcut for names that are not vendor prefixed + if ( name in emptyStyle ) { + return name; + } + + // Check for vendor prefixed names + var capName = name[ 0 ].toUpperCase() + name.slice( 1 ), + i = cssPrefixes.length; + + while ( i-- ) { + name = cssPrefixes[ i ] + capName; + if ( name in emptyStyle ) { + return name; + } + } +} + +// Return a property mapped along what jQuery.cssProps suggests or to +// a vendor prefixed property. +function finalPropName( name ) { + var ret = jQuery.cssProps[ name ]; + if ( !ret ) { + ret = jQuery.cssProps[ name ] = vendorPropName( name ) || name; + } + return ret; +} + +function setPositiveNumber( elem, value, subtract ) { + + // Any relative (+/-) values have already been + // normalized at this point + var matches = rcssNum.exec( value ); + return matches ? + + // Guard against undefined "subtract", e.g., when used as in cssHooks + Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) : + value; +} + +function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) { + var i = dimension === "width" ? 1 : 0, + extra = 0, + delta = 0; + + // Adjustment may not be necessary + if ( box === ( isBorderBox ? "border" : "content" ) ) { + return 0; + } + + for ( ; i < 4; i += 2 ) { + + // Both box models exclude margin + if ( box === "margin" ) { + delta += jQuery.css( elem, box + cssExpand[ i ], true, styles ); + } + + // If we get here with a content-box, we're seeking "padding" or "border" or "margin" + if ( !isBorderBox ) { + + // Add padding + delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + + // For "border" or "margin", add border + if ( box !== "padding" ) { + delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + + // But still keep track of it otherwise + } else { + extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + + // If we get here with a border-box (content + padding + border), we're seeking "content" or + // "padding" or "margin" + } else { + + // For "content", subtract padding + if ( box === "content" ) { + delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + } + + // For "content" or "padding", subtract border + if ( box !== "margin" ) { + delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } + } + + // Account for positive content-box scroll gutter when requested by providing computedVal + if ( !isBorderBox && computedVal >= 0 ) { + + // offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border + // Assuming integer scroll gutter, subtract the rest and round down + delta += Math.max( 0, Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + computedVal - + delta - + extra - + 0.5 + ) ); + } + + return delta; +} + +function getWidthOrHeight( elem, dimension, extra ) { + + // Start with computed style + var styles = getStyles( elem ), + val = curCSS( elem, dimension, styles ), + isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + valueIsBorderBox = isBorderBox; + + // Support: Firefox <=54 + // Return a confounding non-pixel value or feign ignorance, as appropriate. + if ( rnumnonpx.test( val ) ) { + if ( !extra ) { + return val; + } + val = "auto"; + } + + // Check for style in case a browser which returns unreliable values + // for getComputedStyle silently falls back to the reliable elem.style + valueIsBorderBox = valueIsBorderBox && + ( support.boxSizingReliable() || val === elem.style[ dimension ] ); + + // Fall back to offsetWidth/offsetHeight when value is "auto" + // This happens for inline elements with no explicit setting (gh-3571) + // Support: Android <=4.1 - 4.3 only + // Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602) + if ( val === "auto" || + !parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) { + + val = elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ]; + + // offsetWidth/offsetHeight provide border-box values + valueIsBorderBox = true; + } + + // Normalize "" and auto + val = parseFloat( val ) || 0; + + // Adjust for the element's box model + return ( val + + boxModelAdjustment( + elem, + dimension, + extra || ( isBorderBox ? "border" : "content" ), + valueIsBorderBox, + styles, + + // Provide the current computed size to request scroll gutter calculation (gh-3589) + val + ) + ) + "px"; +} + +jQuery.extend( { + + // Add in style property hooks for overriding the default + // behavior of getting and setting a style property + cssHooks: { + opacity: { + get: function( elem, computed ) { + if ( computed ) { + + // We should always get a number back from opacity + var ret = curCSS( elem, "opacity" ); + return ret === "" ? "1" : ret; + } + } + } + }, + + // Don't automatically add "px" to these possibly-unitless properties + cssNumber: { + "animationIterationCount": true, + "columnCount": true, + "fillOpacity": true, + "flexGrow": true, + "flexShrink": true, + "fontWeight": true, + "lineHeight": true, + "opacity": true, + "order": true, + "orphans": true, + "widows": true, + "zIndex": true, + "zoom": true + }, + + // Add in properties whose names you wish to fix before + // setting or getting the value + cssProps: {}, + + // Get and set the style property on a DOM Node + style: function( elem, name, value, extra ) { + + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + var ret, type, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ), + style = elem.style; + + // Make sure that we're working with the right name. We don't + // want to query the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Gets hook for the prefixed version, then unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // Convert "+=" or "-=" to relative numbers (#7345) + if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) { + value = adjustCSS( elem, name, ret ); + + // Fixes bug #9237 + type = "number"; + } + + // Make sure that null and NaN values aren't set (#7116) + if ( value == null || value !== value ) { + return; + } + + // If a number was passed in, add the unit (except for certain CSS properties) + if ( type === "number" ) { + value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" ); + } + + // background-* props affect original clone's values + if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { + style[ name ] = "inherit"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !( "set" in hooks ) || + ( value = hooks.set( elem, value, extra ) ) !== undefined ) { + + if ( isCustomProp ) { + style.setProperty( name, value ); + } else { + style[ name ] = value; + } + } + + } else { + + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && + ( ret = hooks.get( elem, false, extra ) ) !== undefined ) { + + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }, + + css: function( elem, name, extra, styles ) { + var val, num, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ); + + // Make sure that we're working with the right name. We don't + // want to modify the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Try prefixed name followed by the unprefixed name + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks ) { + val = hooks.get( elem, true, extra ); + } + + // Otherwise, if a way to get the computed value exists, use that + if ( val === undefined ) { + val = curCSS( elem, name, styles ); + } + + // Convert "normal" to computed value + if ( val === "normal" && name in cssNormalTransform ) { + val = cssNormalTransform[ name ]; + } + + // Make numeric if forced or a qualifier was provided and val looks numeric + if ( extra === "" || extra ) { + num = parseFloat( val ); + return extra === true || isFinite( num ) ? num || 0 : val; + } + + return val; + } +} ); + +jQuery.each( [ "height", "width" ], function( i, dimension ) { + jQuery.cssHooks[ dimension ] = { + get: function( elem, computed, extra ) { + if ( computed ) { + + // Certain elements can have dimension info if we invisibly show them + // but it must have a current display style that would benefit + return rdisplayswap.test( jQuery.css( elem, "display" ) ) && + + // Support: Safari 8+ + // Table columns in Safari have non-zero offsetWidth & zero + // getBoundingClientRect().width unless display is changed. + // Support: IE <=11 only + // Running getBoundingClientRect on a disconnected node + // in IE throws an error. + ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? + swap( elem, cssShow, function() { + return getWidthOrHeight( elem, dimension, extra ); + } ) : + getWidthOrHeight( elem, dimension, extra ); + } + }, + + set: function( elem, value, extra ) { + var matches, + styles = getStyles( elem ), + isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + subtract = extra && boxModelAdjustment( + elem, + dimension, + extra, + isBorderBox, + styles + ); + + // Account for unreliable border-box dimensions by comparing offset* to computed and + // faking a content-box to get border and padding (gh-3699) + if ( isBorderBox && support.scrollboxSize() === styles.position ) { + subtract -= Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + parseFloat( styles[ dimension ] ) - + boxModelAdjustment( elem, dimension, "border", false, styles ) - + 0.5 + ); + } + + // Convert to pixels if value adjustment is needed + if ( subtract && ( matches = rcssNum.exec( value ) ) && + ( matches[ 3 ] || "px" ) !== "px" ) { + + elem.style[ dimension ] = value; + value = jQuery.css( elem, dimension ); + } + + return setPositiveNumber( elem, value, subtract ); + } + }; +} ); + +jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft, + function( elem, computed ) { + if ( computed ) { + return ( parseFloat( curCSS( elem, "marginLeft" ) ) || + elem.getBoundingClientRect().left - + swap( elem, { marginLeft: 0 }, function() { + return elem.getBoundingClientRect().left; + } ) + ) + "px"; + } + } +); + +// These hooks are used by animate to expand properties +jQuery.each( { + margin: "", + padding: "", + border: "Width" +}, function( prefix, suffix ) { + jQuery.cssHooks[ prefix + suffix ] = { + expand: function( value ) { + var i = 0, + expanded = {}, + + // Assumes a single number if not a string + parts = typeof value === "string" ? value.split( " " ) : [ value ]; + + for ( ; i < 4; i++ ) { + expanded[ prefix + cssExpand[ i ] + suffix ] = + parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; + } + + return expanded; + } + }; + + if ( prefix !== "margin" ) { + jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; + } +} ); + +jQuery.fn.extend( { + css: function( name, value ) { + return access( this, function( elem, name, value ) { + var styles, len, + map = {}, + i = 0; + + if ( Array.isArray( name ) ) { + styles = getStyles( elem ); + len = name.length; + + for ( ; i < len; i++ ) { + map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); + } + + return map; + } + + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }, name, value, arguments.length > 1 ); + } +} ); + + +function Tween( elem, options, prop, end, easing ) { + return new Tween.prototype.init( elem, options, prop, end, easing ); +} +jQuery.Tween = Tween; + +Tween.prototype = { + constructor: Tween, + init: function( elem, options, prop, end, easing, unit ) { + this.elem = elem; + this.prop = prop; + this.easing = easing || jQuery.easing._default; + this.options = options; + this.start = this.now = this.cur(); + this.end = end; + this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); + }, + cur: function() { + var hooks = Tween.propHooks[ this.prop ]; + + return hooks && hooks.get ? + hooks.get( this ) : + Tween.propHooks._default.get( this ); + }, + run: function( percent ) { + var eased, + hooks = Tween.propHooks[ this.prop ]; + + if ( this.options.duration ) { + this.pos = eased = jQuery.easing[ this.easing ]( + percent, this.options.duration * percent, 0, 1, this.options.duration + ); + } else { + this.pos = eased = percent; + } + this.now = ( this.end - this.start ) * eased + this.start; + + if ( this.options.step ) { + this.options.step.call( this.elem, this.now, this ); + } + + if ( hooks && hooks.set ) { + hooks.set( this ); + } else { + Tween.propHooks._default.set( this ); + } + return this; + } +}; + +Tween.prototype.init.prototype = Tween.prototype; + +Tween.propHooks = { + _default: { + get: function( tween ) { + var result; + + // Use a property on the element directly when it is not a DOM element, + // or when there is no matching style property that exists. + if ( tween.elem.nodeType !== 1 || + tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) { + return tween.elem[ tween.prop ]; + } + + // Passing an empty string as a 3rd parameter to .css will automatically + // attempt a parseFloat and fallback to a string if the parse fails. + // Simple values such as "10px" are parsed to Float; + // complex values such as "rotate(1rad)" are returned as-is. + result = jQuery.css( tween.elem, tween.prop, "" ); + + // Empty strings, null, undefined and "auto" are converted to 0. + return !result || result === "auto" ? 0 : result; + }, + set: function( tween ) { + + // Use step hook for back compat. + // Use cssHook if its there. + // Use .style if available and use plain properties where available. + if ( jQuery.fx.step[ tween.prop ] ) { + jQuery.fx.step[ tween.prop ]( tween ); + } else if ( tween.elem.nodeType === 1 && + ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || + jQuery.cssHooks[ tween.prop ] ) ) { + jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); + } else { + tween.elem[ tween.prop ] = tween.now; + } + } + } +}; + +// Support: IE <=9 only +// Panic based approach to setting things on disconnected nodes +Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { + set: function( tween ) { + if ( tween.elem.nodeType && tween.elem.parentNode ) { + tween.elem[ tween.prop ] = tween.now; + } + } +}; + +jQuery.easing = { + linear: function( p ) { + return p; + }, + swing: function( p ) { + return 0.5 - Math.cos( p * Math.PI ) / 2; + }, + _default: "swing" +}; + +jQuery.fx = Tween.prototype.init; + +// Back compat <1.8 extension point +jQuery.fx.step = {}; + + + + +var + fxNow, inProgress, + rfxtypes = /^(?:toggle|show|hide)$/, + rrun = /queueHooks$/; + +function schedule() { + if ( inProgress ) { + if ( document.hidden === false && window.requestAnimationFrame ) { + window.requestAnimationFrame( schedule ); + } else { + window.setTimeout( schedule, jQuery.fx.interval ); + } + + jQuery.fx.tick(); + } +} + +// Animations created synchronously will run synchronously +function createFxNow() { + window.setTimeout( function() { + fxNow = undefined; + } ); + return ( fxNow = Date.now() ); +} + +// Generate parameters to create a standard animation +function genFx( type, includeWidth ) { + var which, + i = 0, + attrs = { height: type }; + + // If we include width, step value is 1 to do all cssExpand values, + // otherwise step value is 2 to skip over Left and Right + includeWidth = includeWidth ? 1 : 0; + for ( ; i < 4; i += 2 - includeWidth ) { + which = cssExpand[ i ]; + attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; + } + + if ( includeWidth ) { + attrs.opacity = attrs.width = type; + } + + return attrs; +} + +function createTween( value, prop, animation ) { + var tween, + collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ), + index = 0, + length = collection.length; + for ( ; index < length; index++ ) { + if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) { + + // We're done with this property + return tween; + } + } +} + +function defaultPrefilter( elem, props, opts ) { + var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display, + isBox = "width" in props || "height" in props, + anim = this, + orig = {}, + style = elem.style, + hidden = elem.nodeType && isHiddenWithinTree( elem ), + dataShow = dataPriv.get( elem, "fxshow" ); + + // Queue-skipping animations hijack the fx hooks + if ( !opts.queue ) { + hooks = jQuery._queueHooks( elem, "fx" ); + if ( hooks.unqueued == null ) { + hooks.unqueued = 0; + oldfire = hooks.empty.fire; + hooks.empty.fire = function() { + if ( !hooks.unqueued ) { + oldfire(); + } + }; + } + hooks.unqueued++; + + anim.always( function() { + + // Ensure the complete handler is called before this completes + anim.always( function() { + hooks.unqueued--; + if ( !jQuery.queue( elem, "fx" ).length ) { + hooks.empty.fire(); + } + } ); + } ); + } + + // Detect show/hide animations + for ( prop in props ) { + value = props[ prop ]; + if ( rfxtypes.test( value ) ) { + delete props[ prop ]; + toggle = toggle || value === "toggle"; + if ( value === ( hidden ? "hide" : "show" ) ) { + + // Pretend to be hidden if this is a "show" and + // there is still data from a stopped show/hide + if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) { + hidden = true; + + // Ignore all other no-op show/hide data + } else { + continue; + } + } + orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop ); + } + } + + // Bail out if this is a no-op like .hide().hide() + propTween = !jQuery.isEmptyObject( props ); + if ( !propTween && jQuery.isEmptyObject( orig ) ) { + return; + } + + // Restrict "overflow" and "display" styles during box animations + if ( isBox && elem.nodeType === 1 ) { + + // Support: IE <=9 - 11, Edge 12 - 15 + // Record all 3 overflow attributes because IE does not infer the shorthand + // from identically-valued overflowX and overflowY and Edge just mirrors + // the overflowX value there. + opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; + + // Identify a display type, preferring old show/hide data over the CSS cascade + restoreDisplay = dataShow && dataShow.display; + if ( restoreDisplay == null ) { + restoreDisplay = dataPriv.get( elem, "display" ); + } + display = jQuery.css( elem, "display" ); + if ( display === "none" ) { + if ( restoreDisplay ) { + display = restoreDisplay; + } else { + + // Get nonempty value(s) by temporarily forcing visibility + showHide( [ elem ], true ); + restoreDisplay = elem.style.display || restoreDisplay; + display = jQuery.css( elem, "display" ); + showHide( [ elem ] ); + } + } + + // Animate inline elements as inline-block + if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) { + if ( jQuery.css( elem, "float" ) === "none" ) { + + // Restore the original display value at the end of pure show/hide animations + if ( !propTween ) { + anim.done( function() { + style.display = restoreDisplay; + } ); + if ( restoreDisplay == null ) { + display = style.display; + restoreDisplay = display === "none" ? "" : display; + } + } + style.display = "inline-block"; + } + } + } + + if ( opts.overflow ) { + style.overflow = "hidden"; + anim.always( function() { + style.overflow = opts.overflow[ 0 ]; + style.overflowX = opts.overflow[ 1 ]; + style.overflowY = opts.overflow[ 2 ]; + } ); + } + + // Implement show/hide animations + propTween = false; + for ( prop in orig ) { + + // General show/hide setup for this element animation + if ( !propTween ) { + if ( dataShow ) { + if ( "hidden" in dataShow ) { + hidden = dataShow.hidden; + } + } else { + dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } ); + } + + // Store hidden/visible for toggle so `.stop().toggle()` "reverses" + if ( toggle ) { + dataShow.hidden = !hidden; + } + + // Show elements before animating them + if ( hidden ) { + showHide( [ elem ], true ); + } + + /* eslint-disable no-loop-func */ + + anim.done( function() { + + /* eslint-enable no-loop-func */ + + // The final step of a "hide" animation is actually hiding the element + if ( !hidden ) { + showHide( [ elem ] ); + } + dataPriv.remove( elem, "fxshow" ); + for ( prop in orig ) { + jQuery.style( elem, prop, orig[ prop ] ); + } + } ); + } + + // Per-property setup + propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim ); + if ( !( prop in dataShow ) ) { + dataShow[ prop ] = propTween.start; + if ( hidden ) { + propTween.end = propTween.start; + propTween.start = 0; + } + } + } +} + +function propFilter( props, specialEasing ) { + var index, name, easing, value, hooks; + + // camelCase, specialEasing and expand cssHook pass + for ( index in props ) { + name = camelCase( index ); + easing = specialEasing[ name ]; + value = props[ index ]; + if ( Array.isArray( value ) ) { + easing = value[ 1 ]; + value = props[ index ] = value[ 0 ]; + } + + if ( index !== name ) { + props[ name ] = value; + delete props[ index ]; + } + + hooks = jQuery.cssHooks[ name ]; + if ( hooks && "expand" in hooks ) { + value = hooks.expand( value ); + delete props[ name ]; + + // Not quite $.extend, this won't overwrite existing keys. + // Reusing 'index' because we have the correct "name" + for ( index in value ) { + if ( !( index in props ) ) { + props[ index ] = value[ index ]; + specialEasing[ index ] = easing; + } + } + } else { + specialEasing[ name ] = easing; + } + } +} + +function Animation( elem, properties, options ) { + var result, + stopped, + index = 0, + length = Animation.prefilters.length, + deferred = jQuery.Deferred().always( function() { + + // Don't match elem in the :animated selector + delete tick.elem; + } ), + tick = function() { + if ( stopped ) { + return false; + } + var currentTime = fxNow || createFxNow(), + remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), + + // Support: Android 2.3 only + // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497) + temp = remaining / animation.duration || 0, + percent = 1 - temp, + index = 0, + length = animation.tweens.length; + + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( percent ); + } + + deferred.notifyWith( elem, [ animation, percent, remaining ] ); + + // If there's more to do, yield + if ( percent < 1 && length ) { + return remaining; + } + + // If this was an empty animation, synthesize a final progress notification + if ( !length ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + } + + // Resolve the animation and report its conclusion + deferred.resolveWith( elem, [ animation ] ); + return false; + }, + animation = deferred.promise( { + elem: elem, + props: jQuery.extend( {}, properties ), + opts: jQuery.extend( true, { + specialEasing: {}, + easing: jQuery.easing._default + }, options ), + originalProperties: properties, + originalOptions: options, + startTime: fxNow || createFxNow(), + duration: options.duration, + tweens: [], + createTween: function( prop, end ) { + var tween = jQuery.Tween( elem, animation.opts, prop, end, + animation.opts.specialEasing[ prop ] || animation.opts.easing ); + animation.tweens.push( tween ); + return tween; + }, + stop: function( gotoEnd ) { + var index = 0, + + // If we are going to the end, we want to run all the tweens + // otherwise we skip this part + length = gotoEnd ? animation.tweens.length : 0; + if ( stopped ) { + return this; + } + stopped = true; + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( 1 ); + } + + // Resolve when we played the last frame; otherwise, reject + if ( gotoEnd ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + deferred.resolveWith( elem, [ animation, gotoEnd ] ); + } else { + deferred.rejectWith( elem, [ animation, gotoEnd ] ); + } + return this; + } + } ), + props = animation.props; + + propFilter( props, animation.opts.specialEasing ); + + for ( ; index < length; index++ ) { + result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts ); + if ( result ) { + if ( isFunction( result.stop ) ) { + jQuery._queueHooks( animation.elem, animation.opts.queue ).stop = + result.stop.bind( result ); + } + return result; + } + } + + jQuery.map( props, createTween, animation ); + + if ( isFunction( animation.opts.start ) ) { + animation.opts.start.call( elem, animation ); + } + + // Attach callbacks from options + animation + .progress( animation.opts.progress ) + .done( animation.opts.done, animation.opts.complete ) + .fail( animation.opts.fail ) + .always( animation.opts.always ); + + jQuery.fx.timer( + jQuery.extend( tick, { + elem: elem, + anim: animation, + queue: animation.opts.queue + } ) + ); + + return animation; +} + +jQuery.Animation = jQuery.extend( Animation, { + + tweeners: { + "*": [ function( prop, value ) { + var tween = this.createTween( prop, value ); + adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween ); + return tween; + } ] + }, + + tweener: function( props, callback ) { + if ( isFunction( props ) ) { + callback = props; + props = [ "*" ]; + } else { + props = props.match( rnothtmlwhite ); + } + + var prop, + index = 0, + length = props.length; + + for ( ; index < length; index++ ) { + prop = props[ index ]; + Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || []; + Animation.tweeners[ prop ].unshift( callback ); + } + }, + + prefilters: [ defaultPrefilter ], + + prefilter: function( callback, prepend ) { + if ( prepend ) { + Animation.prefilters.unshift( callback ); + } else { + Animation.prefilters.push( callback ); + } + } +} ); + +jQuery.speed = function( speed, easing, fn ) { + var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { + complete: fn || !fn && easing || + isFunction( speed ) && speed, + duration: speed, + easing: fn && easing || easing && !isFunction( easing ) && easing + }; + + // Go to the end state if fx are off + if ( jQuery.fx.off ) { + opt.duration = 0; + + } else { + if ( typeof opt.duration !== "number" ) { + if ( opt.duration in jQuery.fx.speeds ) { + opt.duration = jQuery.fx.speeds[ opt.duration ]; + + } else { + opt.duration = jQuery.fx.speeds._default; + } + } + } + + // Normalize opt.queue - true/undefined/null -> "fx" + if ( opt.queue == null || opt.queue === true ) { + opt.queue = "fx"; + } + + // Queueing + opt.old = opt.complete; + + opt.complete = function() { + if ( isFunction( opt.old ) ) { + opt.old.call( this ); + } + + if ( opt.queue ) { + jQuery.dequeue( this, opt.queue ); + } + }; + + return opt; +}; + +jQuery.fn.extend( { + fadeTo: function( speed, to, easing, callback ) { + + // Show any hidden elements after setting opacity to 0 + return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show() + + // Animate to the value specified + .end().animate( { opacity: to }, speed, easing, callback ); + }, + animate: function( prop, speed, easing, callback ) { + var empty = jQuery.isEmptyObject( prop ), + optall = jQuery.speed( speed, easing, callback ), + doAnimation = function() { + + // Operate on a copy of prop so per-property easing won't be lost + var anim = Animation( this, jQuery.extend( {}, prop ), optall ); + + // Empty animations, or finishing resolves immediately + if ( empty || dataPriv.get( this, "finish" ) ) { + anim.stop( true ); + } + }; + doAnimation.finish = doAnimation; + + return empty || optall.queue === false ? + this.each( doAnimation ) : + this.queue( optall.queue, doAnimation ); + }, + stop: function( type, clearQueue, gotoEnd ) { + var stopQueue = function( hooks ) { + var stop = hooks.stop; + delete hooks.stop; + stop( gotoEnd ); + }; + + if ( typeof type !== "string" ) { + gotoEnd = clearQueue; + clearQueue = type; + type = undefined; + } + if ( clearQueue && type !== false ) { + this.queue( type || "fx", [] ); + } + + return this.each( function() { + var dequeue = true, + index = type != null && type + "queueHooks", + timers = jQuery.timers, + data = dataPriv.get( this ); + + if ( index ) { + if ( data[ index ] && data[ index ].stop ) { + stopQueue( data[ index ] ); + } + } else { + for ( index in data ) { + if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { + stopQueue( data[ index ] ); + } + } + } + + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && + ( type == null || timers[ index ].queue === type ) ) { + + timers[ index ].anim.stop( gotoEnd ); + dequeue = false; + timers.splice( index, 1 ); + } + } + + // Start the next in the queue if the last step wasn't forced. + // Timers currently will call their complete callbacks, which + // will dequeue but only if they were gotoEnd. + if ( dequeue || !gotoEnd ) { + jQuery.dequeue( this, type ); + } + } ); + }, + finish: function( type ) { + if ( type !== false ) { + type = type || "fx"; + } + return this.each( function() { + var index, + data = dataPriv.get( this ), + queue = data[ type + "queue" ], + hooks = data[ type + "queueHooks" ], + timers = jQuery.timers, + length = queue ? queue.length : 0; + + // Enable finishing flag on private data + data.finish = true; + + // Empty the queue first + jQuery.queue( this, type, [] ); + + if ( hooks && hooks.stop ) { + hooks.stop.call( this, true ); + } + + // Look for any active animations, and finish them + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && timers[ index ].queue === type ) { + timers[ index ].anim.stop( true ); + timers.splice( index, 1 ); + } + } + + // Look for any animations in the old queue and finish them + for ( index = 0; index < length; index++ ) { + if ( queue[ index ] && queue[ index ].finish ) { + queue[ index ].finish.call( this ); + } + } + + // Turn off finishing flag + delete data.finish; + } ); + } +} ); + +jQuery.each( [ "toggle", "show", "hide" ], function( i, name ) { + var cssFn = jQuery.fn[ name ]; + jQuery.fn[ name ] = function( speed, easing, callback ) { + return speed == null || typeof speed === "boolean" ? + cssFn.apply( this, arguments ) : + this.animate( genFx( name, true ), speed, easing, callback ); + }; +} ); + +// Generate shortcuts for custom animations +jQuery.each( { + slideDown: genFx( "show" ), + slideUp: genFx( "hide" ), + slideToggle: genFx( "toggle" ), + fadeIn: { opacity: "show" }, + fadeOut: { opacity: "hide" }, + fadeToggle: { opacity: "toggle" } +}, function( name, props ) { + jQuery.fn[ name ] = function( speed, easing, callback ) { + return this.animate( props, speed, easing, callback ); + }; +} ); + +jQuery.timers = []; +jQuery.fx.tick = function() { + var timer, + i = 0, + timers = jQuery.timers; + + fxNow = Date.now(); + + for ( ; i < timers.length; i++ ) { + timer = timers[ i ]; + + // Run the timer and safely remove it when done (allowing for external removal) + if ( !timer() && timers[ i ] === timer ) { + timers.splice( i--, 1 ); + } + } + + if ( !timers.length ) { + jQuery.fx.stop(); + } + fxNow = undefined; +}; + +jQuery.fx.timer = function( timer ) { + jQuery.timers.push( timer ); + jQuery.fx.start(); +}; + +jQuery.fx.interval = 13; +jQuery.fx.start = function() { + if ( inProgress ) { + return; + } + + inProgress = true; + schedule(); +}; + +jQuery.fx.stop = function() { + inProgress = null; +}; + +jQuery.fx.speeds = { + slow: 600, + fast: 200, + + // Default speed + _default: 400 +}; + + +// Based off of the plugin by Clint Helfers, with permission. +// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/ +jQuery.fn.delay = function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = window.setTimeout( next, time ); + hooks.stop = function() { + window.clearTimeout( timeout ); + }; + } ); +}; + + +( function() { + var input = document.createElement( "input" ), + select = document.createElement( "select" ), + opt = select.appendChild( document.createElement( "option" ) ); + + input.type = "checkbox"; + + // Support: Android <=4.3 only + // Default value for a checkbox should be "on" + support.checkOn = input.value !== ""; + + // Support: IE <=11 only + // Must access selectedIndex to make default options select + support.optSelected = opt.selected; + + // Support: IE <=11 only + // An input loses its value after becoming a radio + input = document.createElement( "input" ); + input.value = "t"; + input.type = "radio"; + support.radioValue = input.value === "t"; +} )(); + + +var boolHook, + attrHandle = jQuery.expr.attrHandle; + +jQuery.fn.extend( { + attr: function( name, value ) { + return access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each( function() { + jQuery.removeAttr( this, name ); + } ); + } +} ); + +jQuery.extend( { + attr: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set attributes on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === "undefined" ) { + return jQuery.prop( elem, name, value ); + } + + // Attribute hooks are determined by the lowercase version + // Grab necessary hook if one is defined + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + hooks = jQuery.attrHooks[ name.toLowerCase() ] || + ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined ); + } + + if ( value !== undefined ) { + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return; + } + + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + elem.setAttribute( name, value + "" ); + return value; + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + ret = jQuery.find.attr( elem, name ); + + // Non-existent attributes return null, we normalize to undefined + return ret == null ? undefined : ret; + }, + + attrHooks: { + type: { + set: function( elem, value ) { + if ( !support.radioValue && value === "radio" && + nodeName( elem, "input" ) ) { + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + } + }, + + removeAttr: function( elem, value ) { + var name, + i = 0, + + // Attribute names can contain non-HTML whitespace characters + // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 + attrNames = value && value.match( rnothtmlwhite ); + + if ( attrNames && elem.nodeType === 1 ) { + while ( ( name = attrNames[ i++ ] ) ) { + elem.removeAttribute( name ); + } + } + } +} ); + +// Hooks for boolean attributes +boolHook = { + set: function( elem, value, name ) { + if ( value === false ) { + + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + elem.setAttribute( name, name ); + } + return name; + } +}; + +jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) { + var getter = attrHandle[ name ] || jQuery.find.attr; + + attrHandle[ name ] = function( elem, name, isXML ) { + var ret, handle, + lowercaseName = name.toLowerCase(); + + if ( !isXML ) { + + // Avoid an infinite loop by temporarily removing this function from the getter + handle = attrHandle[ lowercaseName ]; + attrHandle[ lowercaseName ] = ret; + ret = getter( elem, name, isXML ) != null ? + lowercaseName : + null; + attrHandle[ lowercaseName ] = handle; + } + return ret; + }; +} ); + + + + +var rfocusable = /^(?:input|select|textarea|button)$/i, + rclickable = /^(?:a|area)$/i; + +jQuery.fn.extend( { + prop: function( name, value ) { + return access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + return this.each( function() { + delete this[ jQuery.propFix[ name ] || name ]; + } ); + } +} ); + +jQuery.extend( { + prop: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set properties on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + return ( elem[ name ] = value ); + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + return elem[ name ]; + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + + // Support: IE <=9 - 11 only + // elem.tabIndex doesn't always return the + // correct value when it hasn't been explicitly set + // https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + // Use proper attribute retrieval(#12072) + var tabindex = jQuery.find.attr( elem, "tabindex" ); + + if ( tabindex ) { + return parseInt( tabindex, 10 ); + } + + if ( + rfocusable.test( elem.nodeName ) || + rclickable.test( elem.nodeName ) && + elem.href + ) { + return 0; + } + + return -1; + } + } + }, + + propFix: { + "for": "htmlFor", + "class": "className" + } +} ); + +// Support: IE <=11 only +// Accessing the selectedIndex property +// forces the browser to respect setting selected +// on the option +// The getter ensures a default option is selected +// when in an optgroup +// eslint rule "no-unused-expressions" is disabled for this code +// since it considers such accessions noop +if ( !support.optSelected ) { + jQuery.propHooks.selected = { + get: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent && parent.parentNode ) { + parent.parentNode.selectedIndex; + } + return null; + }, + set: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent ) { + parent.selectedIndex; + + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + } + }; +} + +jQuery.each( [ + "tabIndex", + "readOnly", + "maxLength", + "cellSpacing", + "cellPadding", + "rowSpan", + "colSpan", + "useMap", + "frameBorder", + "contentEditable" +], function() { + jQuery.propFix[ this.toLowerCase() ] = this; +} ); + + + + + // Strip and collapse whitespace according to HTML spec + // https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace + function stripAndCollapse( value ) { + var tokens = value.match( rnothtmlwhite ) || []; + return tokens.join( " " ); + } + + +function getClass( elem ) { + return elem.getAttribute && elem.getAttribute( "class" ) || ""; +} + +function classesToArray( value ) { + if ( Array.isArray( value ) ) { + return value; + } + if ( typeof value === "string" ) { + return value.match( rnothtmlwhite ) || []; + } + return []; +} + +jQuery.fn.extend( { + addClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).addClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + classes = classesToArray( value ); + + if ( classes.length ) { + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + if ( cur.indexOf( " " + clazz + " " ) < 0 ) { + cur += clazz + " "; + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + if ( !arguments.length ) { + return this.attr( "class", "" ); + } + + classes = classesToArray( value ); + + if ( classes.length ) { + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + + // This expression is here for better compressibility (see addClass) + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + + // Remove *all* instances + while ( cur.indexOf( " " + clazz + " " ) > -1 ) { + cur = cur.replace( " " + clazz + " ", " " ); + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isValidValue = type === "string" || Array.isArray( value ); + + if ( typeof stateVal === "boolean" && isValidValue ) { + return stateVal ? this.addClass( value ) : this.removeClass( value ); + } + + if ( isFunction( value ) ) { + return this.each( function( i ) { + jQuery( this ).toggleClass( + value.call( this, i, getClass( this ), stateVal ), + stateVal + ); + } ); + } + + return this.each( function() { + var className, i, self, classNames; + + if ( isValidValue ) { + + // Toggle individual class names + i = 0; + self = jQuery( this ); + classNames = classesToArray( value ); + + while ( ( className = classNames[ i++ ] ) ) { + + // Check each className given, space separated list + if ( self.hasClass( className ) ) { + self.removeClass( className ); + } else { + self.addClass( className ); + } + } + + // Toggle whole class name + } else if ( value === undefined || type === "boolean" ) { + className = getClass( this ); + if ( className ) { + + // Store className if set + dataPriv.set( this, "__className__", className ); + } + + // If the element has a class name or if we're passed `false`, + // then remove the whole classname (if there was one, the above saved it). + // Otherwise bring back whatever was previously saved (if anything), + // falling back to the empty string if nothing was stored. + if ( this.setAttribute ) { + this.setAttribute( "class", + className || value === false ? + "" : + dataPriv.get( this, "__className__" ) || "" + ); + } + } + } ); + }, + + hasClass: function( selector ) { + var className, elem, + i = 0; + + className = " " + selector + " "; + while ( ( elem = this[ i++ ] ) ) { + if ( elem.nodeType === 1 && + ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) { + return true; + } + } + + return false; + } +} ); + + + + +var rreturn = /\r/g; + +jQuery.fn.extend( { + val: function( value ) { + var hooks, ret, valueIsFunction, + elem = this[ 0 ]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || + jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && + "get" in hooks && + ( ret = hooks.get( elem, "value" ) ) !== undefined + ) { + return ret; + } + + ret = elem.value; + + // Handle most common string cases + if ( typeof ret === "string" ) { + return ret.replace( rreturn, "" ); + } + + // Handle cases where value is null/undef or number + return ret == null ? "" : ret; + } + + return; + } + + valueIsFunction = isFunction( value ); + + return this.each( function( i ) { + var val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( valueIsFunction ) { + val = value.call( this, i, jQuery( this ).val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + + } else if ( typeof val === "number" ) { + val += ""; + + } else if ( Array.isArray( val ) ) { + val = jQuery.map( val, function( value ) { + return value == null ? "" : value + ""; + } ); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + } ); + } +} ); + +jQuery.extend( { + valHooks: { + option: { + get: function( elem ) { + + var val = jQuery.find.attr( elem, "value" ); + return val != null ? + val : + + // Support: IE <=10 - 11 only + // option.text throws exceptions (#14686, #14858) + // Strip and collapse whitespace + // https://html.spec.whatwg.org/#strip-and-collapse-whitespace + stripAndCollapse( jQuery.text( elem ) ); + } + }, + select: { + get: function( elem ) { + var value, option, i, + options = elem.options, + index = elem.selectedIndex, + one = elem.type === "select-one", + values = one ? null : [], + max = one ? index + 1 : options.length; + + if ( index < 0 ) { + i = max; + + } else { + i = one ? index : 0; + } + + // Loop through all the selected options + for ( ; i < max; i++ ) { + option = options[ i ]; + + // Support: IE <=9 only + // IE8-9 doesn't update selected after form reset (#2551) + if ( ( option.selected || i === index ) && + + // Don't return options that are disabled or in a disabled optgroup + !option.disabled && + ( !option.parentNode.disabled || + !nodeName( option.parentNode, "optgroup" ) ) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + }, + + set: function( elem, value ) { + var optionSet, option, + options = elem.options, + values = jQuery.makeArray( value ), + i = options.length; + + while ( i-- ) { + option = options[ i ]; + + /* eslint-disable no-cond-assign */ + + if ( option.selected = + jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1 + ) { + optionSet = true; + } + + /* eslint-enable no-cond-assign */ + } + + // Force browsers to behave consistently when non-matching value is set + if ( !optionSet ) { + elem.selectedIndex = -1; + } + return values; + } + } + } +} ); + +// Radios and checkboxes getter/setter +jQuery.each( [ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + set: function( elem, value ) { + if ( Array.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 ); + } + } + }; + if ( !support.checkOn ) { + jQuery.valHooks[ this ].get = function( elem ) { + return elem.getAttribute( "value" ) === null ? "on" : elem.value; + }; + } +} ); + + + + +// Return jQuery for attributes-only inclusion + + +support.focusin = "onfocusin" in window; + + +var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + stopPropagationCallback = function( e ) { + e.stopPropagation(); + }; + +jQuery.extend( jQuery.event, { + + trigger: function( event, data, elem, onlyHandlers ) { + + var i, cur, tmp, bubbleType, ontype, handle, special, lastElement, + eventPath = [ elem || document ], + type = hasOwn.call( event, "type" ) ? event.type : event, + namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; + + cur = lastElement = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf( "." ) > -1 ) { + + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split( "." ); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf( ":" ) < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) + event.isTrigger = onlyHandlers ? 2 : 3; + event.namespace = namespaces.join( "." ); + event.rnamespace = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === ( elem.ownerDocument || document ) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { + lastElement = cur; + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( dataPriv.get( cur, "events" ) || {} )[ event.type ] && + dataPriv.get( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && handle.apply && acceptData( cur ) ) { + event.result = handle.apply( cur, data ); + if ( event.result === false ) { + event.preventDefault(); + } + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( ( !special._default || + special._default.apply( eventPath.pop(), data ) === false ) && + acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name as the event. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + + if ( event.isPropagationStopped() ) { + lastElement.addEventListener( type, stopPropagationCallback ); + } + + elem[ type ](); + + if ( event.isPropagationStopped() ) { + lastElement.removeEventListener( type, stopPropagationCallback ); + } + + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + // Piggyback on a donor event to simulate a different one + // Used only for `focus(in | out)` events + simulate: function( type, elem, event ) { + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true + } + ); + + jQuery.event.trigger( e, null, elem ); + } + +} ); + +jQuery.fn.extend( { + + trigger: function( type, data ) { + return this.each( function() { + jQuery.event.trigger( type, data, this ); + } ); + }, + triggerHandler: function( type, data ) { + var elem = this[ 0 ]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } +} ); + + +// Support: Firefox <=44 +// Firefox doesn't have focus(in | out) events +// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 +// +// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1 +// focus(in | out) events fire after focus & blur events, +// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order +// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857 +if ( !support.focusin ) { + jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler on the document while someone wants focusin/focusout + var handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + var doc = this.ownerDocument || this, + attaches = dataPriv.access( doc, fix ); + + if ( !attaches ) { + doc.addEventListener( orig, handler, true ); + } + dataPriv.access( doc, fix, ( attaches || 0 ) + 1 ); + }, + teardown: function() { + var doc = this.ownerDocument || this, + attaches = dataPriv.access( doc, fix ) - 1; + + if ( !attaches ) { + doc.removeEventListener( orig, handler, true ); + dataPriv.remove( doc, fix ); + + } else { + dataPriv.access( doc, fix, attaches ); + } + } + }; + } ); +} +var location = window.location; + +var nonce = Date.now(); + +var rquery = ( /\?/ ); + + + +// Cross-browser xml parsing +jQuery.parseXML = function( data ) { + var xml; + if ( !data || typeof data !== "string" ) { + return null; + } + + // Support: IE 9 - 11 only + // IE throws on parseFromString with invalid input. + try { + xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" ); + } catch ( e ) { + xml = undefined; + } + + if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; +}; + + +var + rbracket = /\[\]$/, + rCRLF = /\r?\n/g, + rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, + rsubmittable = /^(?:input|select|textarea|keygen)/i; + +function buildParams( prefix, obj, traditional, add ) { + var name; + + if ( Array.isArray( obj ) ) { + + // Serialize array item. + jQuery.each( obj, function( i, v ) { + if ( traditional || rbracket.test( prefix ) ) { + + // Treat each array item as a scalar. + add( prefix, v ); + + } else { + + // Item is non-scalar (array or object), encode its numeric index. + buildParams( + prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]", + v, + traditional, + add + ); + } + } ); + + } else if ( !traditional && toType( obj ) === "object" ) { + + // Serialize object item. + for ( name in obj ) { + buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); + } + + } else { + + // Serialize scalar item. + add( prefix, obj ); + } +} + +// Serialize an array of form elements or a set of +// key/values into a query string +jQuery.param = function( a, traditional ) { + var prefix, + s = [], + add = function( key, valueOrFunction ) { + + // If value is a function, invoke it and use its return value + var value = isFunction( valueOrFunction ) ? + valueOrFunction() : + valueOrFunction; + + s[ s.length ] = encodeURIComponent( key ) + "=" + + encodeURIComponent( value == null ? "" : value ); + }; + + // If an array was passed in, assume that it is an array of form elements. + if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { + + // Serialize the form elements + jQuery.each( a, function() { + add( this.name, this.value ); + } ); + + } else { + + // If traditional, encode the "old" way (the way 1.3.2 or older + // did it), otherwise encode params recursively. + for ( prefix in a ) { + buildParams( prefix, a[ prefix ], traditional, add ); + } + } + + // Return the resulting serialization + return s.join( "&" ); +}; + +jQuery.fn.extend( { + serialize: function() { + return jQuery.param( this.serializeArray() ); + }, + serializeArray: function() { + return this.map( function() { + + // Can add propHook for "elements" to filter or add form elements + var elements = jQuery.prop( this, "elements" ); + return elements ? jQuery.makeArray( elements ) : this; + } ) + .filter( function() { + var type = this.type; + + // Use .is( ":disabled" ) so that fieldset[disabled] works + return this.name && !jQuery( this ).is( ":disabled" ) && + rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && + ( this.checked || !rcheckableType.test( type ) ); + } ) + .map( function( i, elem ) { + var val = jQuery( this ).val(); + + if ( val == null ) { + return null; + } + + if ( Array.isArray( val ) ) { + return jQuery.map( val, function( val ) { + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ); + } + + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ).get(); + } +} ); + + +var + r20 = /%20/g, + rhash = /#.*$/, + rantiCache = /([?&])_=[^&]*/, + rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, + + // #7653, #8125, #8152: local protocol detection + rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, + rnoContent = /^(?:GET|HEAD)$/, + rprotocol = /^\/\//, + + /* Prefilters + * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) + * 2) These are called: + * - BEFORE asking for a transport + * - AFTER param serialization (s.data is a string if s.processData is true) + * 3) key is the dataType + * 4) the catchall symbol "*" can be used + * 5) execution will start with transport dataType and THEN continue down to "*" if needed + */ + prefilters = {}, + + /* Transports bindings + * 1) key is the dataType + * 2) the catchall symbol "*" can be used + * 3) selection will start with transport dataType and THEN go to "*" if needed + */ + transports = {}, + + // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression + allTypes = "*/".concat( "*" ), + + // Anchor tag for parsing the document origin + originAnchor = document.createElement( "a" ); + originAnchor.href = location.href; + +// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport +function addToPrefiltersOrTransports( structure ) { + + // dataTypeExpression is optional and defaults to "*" + return function( dataTypeExpression, func ) { + + if ( typeof dataTypeExpression !== "string" ) { + func = dataTypeExpression; + dataTypeExpression = "*"; + } + + var dataType, + i = 0, + dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || []; + + if ( isFunction( func ) ) { + + // For each dataType in the dataTypeExpression + while ( ( dataType = dataTypes[ i++ ] ) ) { + + // Prepend if requested + if ( dataType[ 0 ] === "+" ) { + dataType = dataType.slice( 1 ) || "*"; + ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func ); + + // Otherwise append + } else { + ( structure[ dataType ] = structure[ dataType ] || [] ).push( func ); + } + } + } + }; +} + +// Base inspection function for prefilters and transports +function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { + + var inspected = {}, + seekingTransport = ( structure === transports ); + + function inspect( dataType ) { + var selected; + inspected[ dataType ] = true; + jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { + var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); + if ( typeof dataTypeOrTransport === "string" && + !seekingTransport && !inspected[ dataTypeOrTransport ] ) { + + options.dataTypes.unshift( dataTypeOrTransport ); + inspect( dataTypeOrTransport ); + return false; + } else if ( seekingTransport ) { + return !( selected = dataTypeOrTransport ); + } + } ); + return selected; + } + + return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" ); +} + +// A special extend for ajax options +// that takes "flat" options (not to be deep extended) +// Fixes #9887 +function ajaxExtend( target, src ) { + var key, deep, + flatOptions = jQuery.ajaxSettings.flatOptions || {}; + + for ( key in src ) { + if ( src[ key ] !== undefined ) { + ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; + } + } + if ( deep ) { + jQuery.extend( true, target, deep ); + } + + return target; +} + +/* Handles responses to an ajax request: + * - finds the right dataType (mediates between content-type and expected dataType) + * - returns the corresponding response + */ +function ajaxHandleResponses( s, jqXHR, responses ) { + + var ct, type, finalDataType, firstDataType, + contents = s.contents, + dataTypes = s.dataTypes; + + // Remove auto dataType and get content-type in the process + while ( dataTypes[ 0 ] === "*" ) { + dataTypes.shift(); + if ( ct === undefined ) { + ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" ); + } + } + + // Check if we're dealing with a known content-type + if ( ct ) { + for ( type in contents ) { + if ( contents[ type ] && contents[ type ].test( ct ) ) { + dataTypes.unshift( type ); + break; + } + } + } + + // Check to see if we have a response for the expected dataType + if ( dataTypes[ 0 ] in responses ) { + finalDataType = dataTypes[ 0 ]; + } else { + + // Try convertible dataTypes + for ( type in responses ) { + if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) { + finalDataType = type; + break; + } + if ( !firstDataType ) { + firstDataType = type; + } + } + + // Or just use first one + finalDataType = finalDataType || firstDataType; + } + + // If we found a dataType + // We add the dataType to the list if needed + // and return the corresponding response + if ( finalDataType ) { + if ( finalDataType !== dataTypes[ 0 ] ) { + dataTypes.unshift( finalDataType ); + } + return responses[ finalDataType ]; + } +} + +/* Chain conversions given the request and the original response + * Also sets the responseXXX fields on the jqXHR instance + */ +function ajaxConvert( s, response, jqXHR, isSuccess ) { + var conv2, current, conv, tmp, prev, + converters = {}, + + // Work with a copy of dataTypes in case we need to modify it for conversion + dataTypes = s.dataTypes.slice(); + + // Create converters map with lowercased keys + if ( dataTypes[ 1 ] ) { + for ( conv in s.converters ) { + converters[ conv.toLowerCase() ] = s.converters[ conv ]; + } + } + + current = dataTypes.shift(); + + // Convert to each sequential dataType + while ( current ) { + + if ( s.responseFields[ current ] ) { + jqXHR[ s.responseFields[ current ] ] = response; + } + + // Apply the dataFilter if provided + if ( !prev && isSuccess && s.dataFilter ) { + response = s.dataFilter( response, s.dataType ); + } + + prev = current; + current = dataTypes.shift(); + + if ( current ) { + + // There's only work to do if current dataType is non-auto + if ( current === "*" ) { + + current = prev; + + // Convert response if prev dataType is non-auto and differs from current + } else if ( prev !== "*" && prev !== current ) { + + // Seek a direct converter + conv = converters[ prev + " " + current ] || converters[ "* " + current ]; + + // If none found, seek a pair + if ( !conv ) { + for ( conv2 in converters ) { + + // If conv2 outputs current + tmp = conv2.split( " " ); + if ( tmp[ 1 ] === current ) { + + // If prev can be converted to accepted input + conv = converters[ prev + " " + tmp[ 0 ] ] || + converters[ "* " + tmp[ 0 ] ]; + if ( conv ) { + + // Condense equivalence converters + if ( conv === true ) { + conv = converters[ conv2 ]; + + // Otherwise, insert the intermediate dataType + } else if ( converters[ conv2 ] !== true ) { + current = tmp[ 0 ]; + dataTypes.unshift( tmp[ 1 ] ); + } + break; + } + } + } + } + + // Apply converter (if not an equivalence) + if ( conv !== true ) { + + // Unless errors are allowed to bubble, catch and return them + if ( conv && s.throws ) { + response = conv( response ); + } else { + try { + response = conv( response ); + } catch ( e ) { + return { + state: "parsererror", + error: conv ? e : "No conversion from " + prev + " to " + current + }; + } + } + } + } + } + } + + return { state: "success", data: response }; +} + +jQuery.extend( { + + // Counter for holding the number of active queries + active: 0, + + // Last-Modified header cache for next request + lastModified: {}, + etag: {}, + + ajaxSettings: { + url: location.href, + type: "GET", + isLocal: rlocalProtocol.test( location.protocol ), + global: true, + processData: true, + async: true, + contentType: "application/x-www-form-urlencoded; charset=UTF-8", + + /* + timeout: 0, + data: null, + dataType: null, + username: null, + password: null, + cache: null, + throws: false, + traditional: false, + headers: {}, + */ + + accepts: { + "*": allTypes, + text: "text/plain", + html: "text/html", + xml: "application/xml, text/xml", + json: "application/json, text/javascript" + }, + + contents: { + xml: /\bxml\b/, + html: /\bhtml/, + json: /\bjson\b/ + }, + + responseFields: { + xml: "responseXML", + text: "responseText", + json: "responseJSON" + }, + + // Data converters + // Keys separate source (or catchall "*") and destination types with a single space + converters: { + + // Convert anything to text + "* text": String, + + // Text to html (true = no transformation) + "text html": true, + + // Evaluate text as a json expression + "text json": JSON.parse, + + // Parse text as xml + "text xml": jQuery.parseXML + }, + + // For options that shouldn't be deep extended: + // you can add your own custom options here if + // and when you create one that shouldn't be + // deep extended (see ajaxExtend) + flatOptions: { + url: true, + context: true + } + }, + + // Creates a full fledged settings object into target + // with both ajaxSettings and settings fields. + // If target is omitted, writes into ajaxSettings. + ajaxSetup: function( target, settings ) { + return settings ? + + // Building a settings object + ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : + + // Extending ajaxSettings + ajaxExtend( jQuery.ajaxSettings, target ); + }, + + ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), + ajaxTransport: addToPrefiltersOrTransports( transports ), + + // Main method + ajax: function( url, options ) { + + // If url is an object, simulate pre-1.5 signature + if ( typeof url === "object" ) { + options = url; + url = undefined; + } + + // Force options to be an object + options = options || {}; + + var transport, + + // URL without anti-cache param + cacheURL, + + // Response headers + responseHeadersString, + responseHeaders, + + // timeout handle + timeoutTimer, + + // Url cleanup var + urlAnchor, + + // Request state (becomes false upon send and true upon completion) + completed, + + // To know if global events are to be dispatched + fireGlobals, + + // Loop variable + i, + + // uncached part of the url + uncached, + + // Create the final options object + s = jQuery.ajaxSetup( {}, options ), + + // Callbacks context + callbackContext = s.context || s, + + // Context for global events is callbackContext if it is a DOM node or jQuery collection + globalEventContext = s.context && + ( callbackContext.nodeType || callbackContext.jquery ) ? + jQuery( callbackContext ) : + jQuery.event, + + // Deferreds + deferred = jQuery.Deferred(), + completeDeferred = jQuery.Callbacks( "once memory" ), + + // Status-dependent callbacks + statusCode = s.statusCode || {}, + + // Headers (they are sent all at once) + requestHeaders = {}, + requestHeadersNames = {}, + + // Default abort message + strAbort = "canceled", + + // Fake xhr + jqXHR = { + readyState: 0, + + // Builds headers hashtable if needed + getResponseHeader: function( key ) { + var match; + if ( completed ) { + if ( !responseHeaders ) { + responseHeaders = {}; + while ( ( match = rheaders.exec( responseHeadersString ) ) ) { + responseHeaders[ match[ 1 ].toLowerCase() ] = match[ 2 ]; + } + } + match = responseHeaders[ key.toLowerCase() ]; + } + return match == null ? null : match; + }, + + // Raw string + getAllResponseHeaders: function() { + return completed ? responseHeadersString : null; + }, + + // Caches the header + setRequestHeader: function( name, value ) { + if ( completed == null ) { + name = requestHeadersNames[ name.toLowerCase() ] = + requestHeadersNames[ name.toLowerCase() ] || name; + requestHeaders[ name ] = value; + } + return this; + }, + + // Overrides response content-type header + overrideMimeType: function( type ) { + if ( completed == null ) { + s.mimeType = type; + } + return this; + }, + + // Status-dependent callbacks + statusCode: function( map ) { + var code; + if ( map ) { + if ( completed ) { + + // Execute the appropriate callbacks + jqXHR.always( map[ jqXHR.status ] ); + } else { + + // Lazy-add the new callbacks in a way that preserves old ones + for ( code in map ) { + statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; + } + } + } + return this; + }, + + // Cancel the request + abort: function( statusText ) { + var finalText = statusText || strAbort; + if ( transport ) { + transport.abort( finalText ); + } + done( 0, finalText ); + return this; + } + }; + + // Attach deferreds + deferred.promise( jqXHR ); + + // Add protocol if not provided (prefilters might expect it) + // Handle falsy url in the settings object (#10093: consistency with old signature) + // We also use the url parameter if available + s.url = ( ( url || s.url || location.href ) + "" ) + .replace( rprotocol, location.protocol + "//" ); + + // Alias method option to type as per ticket #12004 + s.type = options.method || options.type || s.method || s.type; + + // Extract dataTypes list + s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ]; + + // A cross-domain request is in order when the origin doesn't match the current origin. + if ( s.crossDomain == null ) { + urlAnchor = document.createElement( "a" ); + + // Support: IE <=8 - 11, Edge 12 - 15 + // IE throws exception on accessing the href property if url is malformed, + // e.g. http://example.com:80x/ + try { + urlAnchor.href = s.url; + + // Support: IE <=8 - 11 only + // Anchor's host property isn't correctly set when s.url is relative + urlAnchor.href = urlAnchor.href; + s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !== + urlAnchor.protocol + "//" + urlAnchor.host; + } catch ( e ) { + + // If there is an error parsing the URL, assume it is crossDomain, + // it can be rejected by the transport if it is invalid + s.crossDomain = true; + } + } + + // Convert data if not already a string + if ( s.data && s.processData && typeof s.data !== "string" ) { + s.data = jQuery.param( s.data, s.traditional ); + } + + // Apply prefilters + inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); + + // If request was aborted inside a prefilter, stop there + if ( completed ) { + return jqXHR; + } + + // We can fire global events as of now if asked to + // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118) + fireGlobals = jQuery.event && s.global; + + // Watch for a new set of requests + if ( fireGlobals && jQuery.active++ === 0 ) { + jQuery.event.trigger( "ajaxStart" ); + } + + // Uppercase the type + s.type = s.type.toUpperCase(); + + // Determine if request has content + s.hasContent = !rnoContent.test( s.type ); + + // Save the URL in case we're toying with the If-Modified-Since + // and/or If-None-Match header later on + // Remove hash to simplify url manipulation + cacheURL = s.url.replace( rhash, "" ); + + // More options handling for requests with no content + if ( !s.hasContent ) { + + // Remember the hash so we can put it back + uncached = s.url.slice( cacheURL.length ); + + // If data is available and should be processed, append data to url + if ( s.data && ( s.processData || typeof s.data === "string" ) ) { + cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data; + + // #9682: remove data so that it's not used in an eventual retry + delete s.data; + } + + // Add or update anti-cache param if needed + if ( s.cache === false ) { + cacheURL = cacheURL.replace( rantiCache, "$1" ); + uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce++ ) + uncached; + } + + // Put hash and anti-cache on the URL that will be requested (gh-1732) + s.url = cacheURL + uncached; + + // Change '%20' to '+' if this is encoded form body content (gh-2658) + } else if ( s.data && s.processData && + ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) { + s.data = s.data.replace( r20, "+" ); + } + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + if ( jQuery.lastModified[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] ); + } + if ( jQuery.etag[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] ); + } + } + + // Set the correct header, if data is being sent + if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { + jqXHR.setRequestHeader( "Content-Type", s.contentType ); + } + + // Set the Accepts header for the server, depending on the dataType + jqXHR.setRequestHeader( + "Accept", + s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ? + s.accepts[ s.dataTypes[ 0 ] ] + + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : + s.accepts[ "*" ] + ); + + // Check for headers option + for ( i in s.headers ) { + jqXHR.setRequestHeader( i, s.headers[ i ] ); + } + + // Allow custom headers/mimetypes and early abort + if ( s.beforeSend && + ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) { + + // Abort if not done already and return + return jqXHR.abort(); + } + + // Aborting is no longer a cancellation + strAbort = "abort"; + + // Install callbacks on deferreds + completeDeferred.add( s.complete ); + jqXHR.done( s.success ); + jqXHR.fail( s.error ); + + // Get transport + transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); + + // If no transport, we auto-abort + if ( !transport ) { + done( -1, "No Transport" ); + } else { + jqXHR.readyState = 1; + + // Send global event + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); + } + + // If request was aborted inside ajaxSend, stop there + if ( completed ) { + return jqXHR; + } + + // Timeout + if ( s.async && s.timeout > 0 ) { + timeoutTimer = window.setTimeout( function() { + jqXHR.abort( "timeout" ); + }, s.timeout ); + } + + try { + completed = false; + transport.send( requestHeaders, done ); + } catch ( e ) { + + // Rethrow post-completion exceptions + if ( completed ) { + throw e; + } + + // Propagate others as results + done( -1, e ); + } + } + + // Callback for when everything is done + function done( status, nativeStatusText, responses, headers ) { + var isSuccess, success, error, response, modified, + statusText = nativeStatusText; + + // Ignore repeat invocations + if ( completed ) { + return; + } + + completed = true; + + // Clear timeout if it exists + if ( timeoutTimer ) { + window.clearTimeout( timeoutTimer ); + } + + // Dereference transport for early garbage collection + // (no matter how long the jqXHR object will be used) + transport = undefined; + + // Cache response headers + responseHeadersString = headers || ""; + + // Set readyState + jqXHR.readyState = status > 0 ? 4 : 0; + + // Determine if successful + isSuccess = status >= 200 && status < 300 || status === 304; + + // Get response data + if ( responses ) { + response = ajaxHandleResponses( s, jqXHR, responses ); + } + + // Convert no matter what (that way responseXXX fields are always set) + response = ajaxConvert( s, response, jqXHR, isSuccess ); + + // If successful, handle type chaining + if ( isSuccess ) { + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + modified = jqXHR.getResponseHeader( "Last-Modified" ); + if ( modified ) { + jQuery.lastModified[ cacheURL ] = modified; + } + modified = jqXHR.getResponseHeader( "etag" ); + if ( modified ) { + jQuery.etag[ cacheURL ] = modified; + } + } + + // if no content + if ( status === 204 || s.type === "HEAD" ) { + statusText = "nocontent"; + + // if not modified + } else if ( status === 304 ) { + statusText = "notmodified"; + + // If we have data, let's convert it + } else { + statusText = response.state; + success = response.data; + error = response.error; + isSuccess = !error; + } + } else { + + // Extract error from statusText and normalize for non-aborts + error = statusText; + if ( status || !statusText ) { + statusText = "error"; + if ( status < 0 ) { + status = 0; + } + } + } + + // Set data for the fake xhr object + jqXHR.status = status; + jqXHR.statusText = ( nativeStatusText || statusText ) + ""; + + // Success/Error + if ( isSuccess ) { + deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); + } else { + deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); + } + + // Status-dependent callbacks + jqXHR.statusCode( statusCode ); + statusCode = undefined; + + if ( fireGlobals ) { + globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError", + [ jqXHR, s, isSuccess ? success : error ] ); + } + + // Complete + completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); + + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); + + // Handle the global AJAX counter + if ( !( --jQuery.active ) ) { + jQuery.event.trigger( "ajaxStop" ); + } + } + } + + return jqXHR; + }, + + getJSON: function( url, data, callback ) { + return jQuery.get( url, data, callback, "json" ); + }, + + getScript: function( url, callback ) { + return jQuery.get( url, undefined, callback, "script" ); + } +} ); + +jQuery.each( [ "get", "post" ], function( i, method ) { + jQuery[ method ] = function( url, data, callback, type ) { + + // Shift arguments if data argument was omitted + if ( isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } + + // The url can be an options object (which then must have .url) + return jQuery.ajax( jQuery.extend( { + url: url, + type: method, + dataType: type, + data: data, + success: callback + }, jQuery.isPlainObject( url ) && url ) ); + }; +} ); + + +jQuery._evalUrl = function( url ) { + return jQuery.ajax( { + url: url, + + // Make this explicit, since user can override this through ajaxSetup (#11264) + type: "GET", + dataType: "script", + cache: true, + async: false, + global: false, + "throws": true + } ); +}; + + +jQuery.fn.extend( { + wrapAll: function( html ) { + var wrap; + + if ( this[ 0 ] ) { + if ( isFunction( html ) ) { + html = html.call( this[ 0 ] ); + } + + // The elements to wrap the target around + wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); + + if ( this[ 0 ].parentNode ) { + wrap.insertBefore( this[ 0 ] ); + } + + wrap.map( function() { + var elem = this; + + while ( elem.firstElementChild ) { + elem = elem.firstElementChild; + } + + return elem; + } ).append( this ); + } + + return this; + }, + + wrapInner: function( html ) { + if ( isFunction( html ) ) { + return this.each( function( i ) { + jQuery( this ).wrapInner( html.call( this, i ) ); + } ); + } + + return this.each( function() { + var self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + } ); + }, + + wrap: function( html ) { + var htmlIsFunction = isFunction( html ); + + return this.each( function( i ) { + jQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html ); + } ); + }, + + unwrap: function( selector ) { + this.parent( selector ).not( "body" ).each( function() { + jQuery( this ).replaceWith( this.childNodes ); + } ); + return this; + } +} ); + + +jQuery.expr.pseudos.hidden = function( elem ) { + return !jQuery.expr.pseudos.visible( elem ); +}; +jQuery.expr.pseudos.visible = function( elem ) { + return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ); +}; + + + + +jQuery.ajaxSettings.xhr = function() { + try { + return new window.XMLHttpRequest(); + } catch ( e ) {} +}; + +var xhrSuccessStatus = { + + // File protocol always yields status code 0, assume 200 + 0: 200, + + // Support: IE <=9 only + // #1450: sometimes IE returns 1223 when it should be 204 + 1223: 204 + }, + xhrSupported = jQuery.ajaxSettings.xhr(); + +support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); +support.ajax = xhrSupported = !!xhrSupported; + +jQuery.ajaxTransport( function( options ) { + var callback, errorCallback; + + // Cross domain only allowed if supported through XMLHttpRequest + if ( support.cors || xhrSupported && !options.crossDomain ) { + return { + send: function( headers, complete ) { + var i, + xhr = options.xhr(); + + xhr.open( + options.type, + options.url, + options.async, + options.username, + options.password + ); + + // Apply custom fields if provided + if ( options.xhrFields ) { + for ( i in options.xhrFields ) { + xhr[ i ] = options.xhrFields[ i ]; + } + } + + // Override mime type if needed + if ( options.mimeType && xhr.overrideMimeType ) { + xhr.overrideMimeType( options.mimeType ); + } + + // X-Requested-With header + // For cross-domain requests, seeing as conditions for a preflight are + // akin to a jigsaw puzzle, we simply never set it to be sure. + // (it can always be set on a per-request basis or even using ajaxSetup) + // For same-domain requests, won't change header if already provided. + if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) { + headers[ "X-Requested-With" ] = "XMLHttpRequest"; + } + + // Set headers + for ( i in headers ) { + xhr.setRequestHeader( i, headers[ i ] ); + } + + // Callback + callback = function( type ) { + return function() { + if ( callback ) { + callback = errorCallback = xhr.onload = + xhr.onerror = xhr.onabort = xhr.ontimeout = + xhr.onreadystatechange = null; + + if ( type === "abort" ) { + xhr.abort(); + } else if ( type === "error" ) { + + // Support: IE <=9 only + // On a manual native abort, IE9 throws + // errors on any property access that is not readyState + if ( typeof xhr.status !== "number" ) { + complete( 0, "error" ); + } else { + complete( + + // File: protocol always yields status 0; see #8605, #14207 + xhr.status, + xhr.statusText + ); + } + } else { + complete( + xhrSuccessStatus[ xhr.status ] || xhr.status, + xhr.statusText, + + // Support: IE <=9 only + // IE9 has no XHR2 but throws on binary (trac-11426) + // For XHR2 non-text, let the caller handle it (gh-2498) + ( xhr.responseType || "text" ) !== "text" || + typeof xhr.responseText !== "string" ? + { binary: xhr.response } : + { text: xhr.responseText }, + xhr.getAllResponseHeaders() + ); + } + } + }; + }; + + // Listen to events + xhr.onload = callback(); + errorCallback = xhr.onerror = xhr.ontimeout = callback( "error" ); + + // Support: IE 9 only + // Use onreadystatechange to replace onabort + // to handle uncaught aborts + if ( xhr.onabort !== undefined ) { + xhr.onabort = errorCallback; + } else { + xhr.onreadystatechange = function() { + + // Check readyState before timeout as it changes + if ( xhr.readyState === 4 ) { + + // Allow onerror to be called first, + // but that will not handle a native abort + // Also, save errorCallback to a variable + // as xhr.onerror cannot be accessed + window.setTimeout( function() { + if ( callback ) { + errorCallback(); + } + } ); + } + }; + } + + // Create the abort callback + callback = callback( "abort" ); + + try { + + // Do send the request (this may raise an exception) + xhr.send( options.hasContent && options.data || null ); + } catch ( e ) { + + // #14683: Only rethrow if this hasn't been notified as an error yet + if ( callback ) { + throw e; + } + } + }, + + abort: function() { + if ( callback ) { + callback(); + } + } + }; + } +} ); + + + + +// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432) +jQuery.ajaxPrefilter( function( s ) { + if ( s.crossDomain ) { + s.contents.script = false; + } +} ); + +// Install script dataType +jQuery.ajaxSetup( { + accepts: { + script: "text/javascript, application/javascript, " + + "application/ecmascript, application/x-ecmascript" + }, + contents: { + script: /\b(?:java|ecma)script\b/ + }, + converters: { + "text script": function( text ) { + jQuery.globalEval( text ); + return text; + } + } +} ); + +// Handle cache's special case and crossDomain +jQuery.ajaxPrefilter( "script", function( s ) { + if ( s.cache === undefined ) { + s.cache = false; + } + if ( s.crossDomain ) { + s.type = "GET"; + } +} ); + +// Bind script tag hack transport +jQuery.ajaxTransport( "script", function( s ) { + + // This transport only deals with cross domain requests + if ( s.crossDomain ) { + var script, callback; + return { + send: function( _, complete ) { + script = jQuery( "<script>" ).prop( { + charset: s.scriptCharset, + src: s.url + } ).on( + "load error", + callback = function( evt ) { + script.remove(); + callback = null; + if ( evt ) { + complete( evt.type === "error" ? 404 : 200, evt.type ); + } + } + ); + + // Use native DOM manipulation to avoid our domManip AJAX trickery + document.head.appendChild( script[ 0 ] ); + }, + abort: function() { + if ( callback ) { + callback(); + } + } + }; + } +} ); + + + + +var oldCallbacks = [], + rjsonp = /(=)\?(?=&|$)|\?\?/; + +// Default jsonp settings +jQuery.ajaxSetup( { + jsonp: "callback", + jsonpCallback: function() { + var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce++ ) ); + this[ callback ] = true; + return callback; + } +} ); + +// Detect, normalize options and install callbacks for jsonp requests +jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) { + + var callbackName, overwritten, responseContainer, + jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ? + "url" : + typeof s.data === "string" && + ( s.contentType || "" ) + .indexOf( "application/x-www-form-urlencoded" ) === 0 && + rjsonp.test( s.data ) && "data" + ); + + // Handle iff the expected data type is "jsonp" or we have a parameter to set + if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) { + + // Get callback name, remembering preexisting value associated with it + callbackName = s.jsonpCallback = isFunction( s.jsonpCallback ) ? + s.jsonpCallback() : + s.jsonpCallback; + + // Insert callback into url or form data + if ( jsonProp ) { + s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName ); + } else if ( s.jsonp !== false ) { + s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName; + } + + // Use data converter to retrieve json after script execution + s.converters[ "script json" ] = function() { + if ( !responseContainer ) { + jQuery.error( callbackName + " was not called" ); + } + return responseContainer[ 0 ]; + }; + + // Force json dataType + s.dataTypes[ 0 ] = "json"; + + // Install callback + overwritten = window[ callbackName ]; + window[ callbackName ] = function() { + responseContainer = arguments; + }; + + // Clean-up function (fires after converters) + jqXHR.always( function() { + + // If previous value didn't exist - remove it + if ( overwritten === undefined ) { + jQuery( window ).removeProp( callbackName ); + + // Otherwise restore preexisting value + } else { + window[ callbackName ] = overwritten; + } + + // Save back as free + if ( s[ callbackName ] ) { + + // Make sure that re-using the options doesn't screw things around + s.jsonpCallback = originalSettings.jsonpCallback; + + // Save the callback name for future use + oldCallbacks.push( callbackName ); + } + + // Call if it was a function and we have a response + if ( responseContainer && isFunction( overwritten ) ) { + overwritten( responseContainer[ 0 ] ); + } + + responseContainer = overwritten = undefined; + } ); + + // Delegate to script + return "script"; + } +} ); + + + + +// Support: Safari 8 only +// In Safari 8 documents created via document.implementation.createHTMLDocument +// collapse sibling forms: the second one becomes a child of the first one. +// Because of that, this security measure has to be disabled in Safari 8. +// https://bugs.webkit.org/show_bug.cgi?id=137337 +support.createHTMLDocument = ( function() { + var body = document.implementation.createHTMLDocument( "" ).body; + body.innerHTML = "<form></form><form></form>"; + return body.childNodes.length === 2; +} )(); + + +// Argument "data" should be string of html +// context (optional): If specified, the fragment will be created in this context, +// defaults to document +// keepScripts (optional): If true, will include scripts passed in the html string +jQuery.parseHTML = function( data, context, keepScripts ) { + if ( typeof data !== "string" ) { + return []; + } + if ( typeof context === "boolean" ) { + keepScripts = context; + context = false; + } + + var base, parsed, scripts; + + if ( !context ) { + + // Stop scripts or inline event handlers from being executed immediately + // by using document.implementation + if ( support.createHTMLDocument ) { + context = document.implementation.createHTMLDocument( "" ); + + // Set the base href for the created document + // so any parsed elements with URLs + // are based on the document's URL (gh-2965) + base = context.createElement( "base" ); + base.href = document.location.href; + context.head.appendChild( base ); + } else { + context = document; + } + } + + parsed = rsingleTag.exec( data ); + scripts = !keepScripts && []; + + // Single tag + if ( parsed ) { + return [ context.createElement( parsed[ 1 ] ) ]; + } + + parsed = buildFragment( [ data ], context, scripts ); + + if ( scripts && scripts.length ) { + jQuery( scripts ).remove(); + } + + return jQuery.merge( [], parsed.childNodes ); +}; + + +/** + * Load a url into a page + */ +jQuery.fn.load = function( url, params, callback ) { + var selector, type, response, + self = this, + off = url.indexOf( " " ); + + if ( off > -1 ) { + selector = stripAndCollapse( url.slice( off ) ); + url = url.slice( 0, off ); + } + + // If it's a function + if ( isFunction( params ) ) { + + // We assume that it's the callback + callback = params; + params = undefined; + + // Otherwise, build a param string + } else if ( params && typeof params === "object" ) { + type = "POST"; + } + + // If we have elements to modify, make the request + if ( self.length > 0 ) { + jQuery.ajax( { + url: url, + + // If "type" variable is undefined, then "GET" method will be used. + // Make value of this field explicit since + // user can override it through ajaxSetup method + type: type || "GET", + dataType: "html", + data: params + } ).done( function( responseText ) { + + // Save response for use in complete callback + response = arguments; + + self.html( selector ? + + // If a selector was specified, locate the right elements in a dummy div + // Exclude scripts to avoid IE 'Permission Denied' errors + jQuery( "<div>" ).append( jQuery.parseHTML( responseText ) ).find( selector ) : + + // Otherwise use the full result + responseText ); + + // If the request succeeds, this function gets "data", "status", "jqXHR" + // but they are ignored because response was set above. + // If it fails, this function gets "jqXHR", "status", "error" + } ).always( callback && function( jqXHR, status ) { + self.each( function() { + callback.apply( this, response || [ jqXHR.responseText, status, jqXHR ] ); + } ); + } ); + } + + return this; +}; + + + + +// Attach a bunch of functions for handling common AJAX events +jQuery.each( [ + "ajaxStart", + "ajaxStop", + "ajaxComplete", + "ajaxError", + "ajaxSuccess", + "ajaxSend" +], function( i, type ) { + jQuery.fn[ type ] = function( fn ) { + return this.on( type, fn ); + }; +} ); + + + + +jQuery.expr.pseudos.animated = function( elem ) { + return jQuery.grep( jQuery.timers, function( fn ) { + return elem === fn.elem; + } ).length; +}; + + + + +jQuery.offset = { + setOffset: function( elem, options, i ) { + var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition, + position = jQuery.css( elem, "position" ), + curElem = jQuery( elem ), + props = {}; + + // Set position first, in-case top/left are set even on static elem + if ( position === "static" ) { + elem.style.position = "relative"; + } + + curOffset = curElem.offset(); + curCSSTop = jQuery.css( elem, "top" ); + curCSSLeft = jQuery.css( elem, "left" ); + calculatePosition = ( position === "absolute" || position === "fixed" ) && + ( curCSSTop + curCSSLeft ).indexOf( "auto" ) > -1; + + // Need to be able to calculate position if either + // top or left is auto and position is either absolute or fixed + if ( calculatePosition ) { + curPosition = curElem.position(); + curTop = curPosition.top; + curLeft = curPosition.left; + + } else { + curTop = parseFloat( curCSSTop ) || 0; + curLeft = parseFloat( curCSSLeft ) || 0; + } + + if ( isFunction( options ) ) { + + // Use jQuery.extend here to allow modification of coordinates argument (gh-1848) + options = options.call( elem, i, jQuery.extend( {}, curOffset ) ); + } + + if ( options.top != null ) { + props.top = ( options.top - curOffset.top ) + curTop; + } + if ( options.left != null ) { + props.left = ( options.left - curOffset.left ) + curLeft; + } + + if ( "using" in options ) { + options.using.call( elem, props ); + + } else { + curElem.css( props ); + } + } +}; + +jQuery.fn.extend( { + + // offset() relates an element's border box to the document origin + offset: function( options ) { + + // Preserve chaining for setter + if ( arguments.length ) { + return options === undefined ? + this : + this.each( function( i ) { + jQuery.offset.setOffset( this, options, i ); + } ); + } + + var rect, win, + elem = this[ 0 ]; + + if ( !elem ) { + return; + } + + // Return zeros for disconnected and hidden (display: none) elements (gh-2310) + // Support: IE <=11 only + // Running getBoundingClientRect on a + // disconnected node in IE throws an error + if ( !elem.getClientRects().length ) { + return { top: 0, left: 0 }; + } + + // Get document-relative position by adding viewport scroll to viewport-relative gBCR + rect = elem.getBoundingClientRect(); + win = elem.ownerDocument.defaultView; + return { + top: rect.top + win.pageYOffset, + left: rect.left + win.pageXOffset + }; + }, + + // position() relates an element's margin box to its offset parent's padding box + // This corresponds to the behavior of CSS absolute positioning + position: function() { + if ( !this[ 0 ] ) { + return; + } + + var offsetParent, offset, doc, + elem = this[ 0 ], + parentOffset = { top: 0, left: 0 }; + + // position:fixed elements are offset from the viewport, which itself always has zero offset + if ( jQuery.css( elem, "position" ) === "fixed" ) { + + // Assume position:fixed implies availability of getBoundingClientRect + offset = elem.getBoundingClientRect(); + + } else { + offset = this.offset(); + + // Account for the *real* offset parent, which can be the document or its root element + // when a statically positioned element is identified + doc = elem.ownerDocument; + offsetParent = elem.offsetParent || doc.documentElement; + while ( offsetParent && + ( offsetParent === doc.body || offsetParent === doc.documentElement ) && + jQuery.css( offsetParent, "position" ) === "static" ) { + + offsetParent = offsetParent.parentNode; + } + if ( offsetParent && offsetParent !== elem && offsetParent.nodeType === 1 ) { + + // Incorporate borders into its offset, since they are outside its content origin + parentOffset = jQuery( offsetParent ).offset(); + parentOffset.top += jQuery.css( offsetParent, "borderTopWidth", true ); + parentOffset.left += jQuery.css( offsetParent, "borderLeftWidth", true ); + } + } + + // Subtract parent offsets and element margins + return { + top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ), + left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true ) + }; + }, + + // This method will return documentElement in the following cases: + // 1) For the element inside the iframe without offsetParent, this method will return + // documentElement of the parent window + // 2) For the hidden or detached element + // 3) For body or html element, i.e. in case of the html node - it will return itself + // + // but those exceptions were never presented as a real life use-cases + // and might be considered as more preferable results. + // + // This logic, however, is not guaranteed and can change at any point in the future + offsetParent: function() { + return this.map( function() { + var offsetParent = this.offsetParent; + + while ( offsetParent && jQuery.css( offsetParent, "position" ) === "static" ) { + offsetParent = offsetParent.offsetParent; + } + + return offsetParent || documentElement; + } ); + } +} ); + +// Create scrollLeft and scrollTop methods +jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( method, prop ) { + var top = "pageYOffset" === prop; + + jQuery.fn[ method ] = function( val ) { + return access( this, function( elem, method, val ) { + + // Coalesce documents and windows + var win; + if ( isWindow( elem ) ) { + win = elem; + } else if ( elem.nodeType === 9 ) { + win = elem.defaultView; + } + + if ( val === undefined ) { + return win ? win[ prop ] : elem[ method ]; + } + + if ( win ) { + win.scrollTo( + !top ? val : win.pageXOffset, + top ? val : win.pageYOffset + ); + + } else { + elem[ method ] = val; + } + }, method, val, arguments.length ); + }; +} ); + +// Support: Safari <=7 - 9.1, Chrome <=37 - 49 +// Add the top/left cssHooks using jQuery.fn.position +// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084 +// Blink bug: https://bugs.chromium.org/p/chromium/issues/detail?id=589347 +// getComputedStyle returns percent when specified for top/left/bottom/right; +// rather than make the css module depend on the offset module, just check for it here +jQuery.each( [ "top", "left" ], function( i, prop ) { + jQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition, + function( elem, computed ) { + if ( computed ) { + computed = curCSS( elem, prop ); + + // If curCSS returns percentage, fallback to offset + return rnumnonpx.test( computed ) ? + jQuery( elem ).position()[ prop ] + "px" : + computed; + } + } + ); +} ); + + +// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods +jQuery.each( { Height: "height", Width: "width" }, function( name, type ) { + jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, + function( defaultExtra, funcName ) { + + // Margin is only for outerHeight, outerWidth + jQuery.fn[ funcName ] = function( margin, value ) { + var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ), + extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" ); + + return access( this, function( elem, type, value ) { + var doc; + + if ( isWindow( elem ) ) { + + // $( window ).outerWidth/Height return w/h including scrollbars (gh-1729) + return funcName.indexOf( "outer" ) === 0 ? + elem[ "inner" + name ] : + elem.document.documentElement[ "client" + name ]; + } + + // Get document width or height + if ( elem.nodeType === 9 ) { + doc = elem.documentElement; + + // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], + // whichever is greatest + return Math.max( + elem.body[ "scroll" + name ], doc[ "scroll" + name ], + elem.body[ "offset" + name ], doc[ "offset" + name ], + doc[ "client" + name ] + ); + } + + return value === undefined ? + + // Get width or height on the element, requesting but not forcing parseFloat + jQuery.css( elem, type, extra ) : + + // Set width or height on the element + jQuery.style( elem, type, value, extra ); + }, type, chainable ? margin : undefined, chainable ); + }; + } ); +} ); + + +jQuery.each( ( "blur focus focusin focusout resize scroll click dblclick " + + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + + "change select submit keydown keypress keyup contextmenu" ).split( " " ), + function( i, name ) { + + // Handle event binding + jQuery.fn[ name ] = function( data, fn ) { + return arguments.length > 0 ? + this.on( name, null, data, fn ) : + this.trigger( name ); + }; +} ); + +jQuery.fn.extend( { + hover: function( fnOver, fnOut ) { + return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); + } +} ); + + + + +jQuery.fn.extend( { + + bind: function( types, data, fn ) { + return this.on( types, null, data, fn ); + }, + unbind: function( types, fn ) { + return this.off( types, null, fn ); + }, + + delegate: function( selector, types, data, fn ) { + return this.on( types, selector, data, fn ); + }, + undelegate: function( selector, types, fn ) { + + // ( namespace ) or ( selector, types [, fn] ) + return arguments.length === 1 ? + this.off( selector, "**" ) : + this.off( types, selector || "**", fn ); + } +} ); + +// Bind a function to a context, optionally partially applying any +// arguments. +// jQuery.proxy is deprecated to promote standards (specifically Function#bind) +// However, it is not slated for removal any time soon +jQuery.proxy = function( fn, context ) { + var tmp, args, proxy; + + if ( typeof context === "string" ) { + tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + args = slice.call( arguments, 2 ); + proxy = function() { + return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || jQuery.guid++; + + return proxy; +}; + +jQuery.holdReady = function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } +}; +jQuery.isArray = Array.isArray; +jQuery.parseJSON = JSON.parse; +jQuery.nodeName = nodeName; +jQuery.isFunction = isFunction; +jQuery.isWindow = isWindow; +jQuery.camelCase = camelCase; +jQuery.type = toType; + +jQuery.now = Date.now; + +jQuery.isNumeric = function( obj ) { + + // As of jQuery 3.0, isNumeric is limited to + // strings and numbers (primitives or objects) + // that can be coerced to finite numbers (gh-2662) + var type = jQuery.type( obj ); + return ( type === "number" || type === "string" ) && + + // parseFloat NaNs numeric-cast false positives ("") + // ...but misinterprets leading-number strings, particularly hex literals ("0x...") + // subtraction forces infinities to NaN + !isNaN( obj - parseFloat( obj ) ); +}; + + + + +// Register as a named AMD module, since jQuery can be concatenated with other +// files that may use define, but not via a proper concatenation script that +// understands anonymous AMD modules. A named AMD is safest and most robust +// way to register. Lowercase jquery is used because AMD module names are +// derived from file names, and jQuery is normally delivered in a lowercase +// file name. Do this after creating the global so that if an AMD module wants +// to call noConflict to hide this version of jQuery, it will work. + +// Note that for maximum portability, libraries that are not jQuery should +// declare themselves as anonymous modules, and avoid setting a global if an +// AMD loader is present. jQuery is a special case. For more information, see +// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon + +if ( typeof define === "function" && define.amd ) { + define( "jquery", [], function() { + return jQuery; + } ); +} + + + + +var + + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + + // Map over the $ in case of overwrite + _$ = window.$; + +jQuery.noConflict = function( deep ) { + if ( window.$ === jQuery ) { + window.$ = _$; + } + + if ( deep && window.jQuery === jQuery ) { + window.jQuery = _jQuery; + } + + return jQuery; +}; + +// Expose jQuery and $ identifiers, even in AMD +// (#7102#comment:10, https://github.com/jquery/jquery/pull/557) +// and CommonJS for browser emulators (#13566) +if ( !noGlobal ) { + window.jQuery = window.$ = jQuery; +} + + + + +return jQuery; +} ); diff --git a/plugins/javadoc/src/main/resources/static_res/jquery/jquery-migrate-3.0.1.js b/plugins/javadoc/src/main/resources/static_res/jquery/jquery-migrate-3.0.1.js new file mode 100644 index 00000000..6c1d4ff2 --- /dev/null +++ b/plugins/javadoc/src/main/resources/static_res/jquery/jquery-migrate-3.0.1.js @@ -0,0 +1,628 @@ +/*! + * jQuery Migrate - v3.0.1 - 2017-09-26 + * Copyright jQuery Foundation and other contributors + */ +;( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ "jquery" ], window, factory ); + } else if ( typeof module === "object" && module.exports ) { + + // Node/CommonJS + // eslint-disable-next-line no-undef + module.exports = factory( require( "jquery" ), window ); + } else { + + // Browser globals + factory( jQuery, window ); + } +} )( function( jQuery, window ) { +"use strict"; + + +jQuery.migrateVersion = "3.0.1"; + +jQuery.migrateMute = true; + +/* exported migrateWarn, migrateWarnFunc, migrateWarnProp */ + +( function() { + + var rbadVersions = /^[12]\./; + + // Support: IE9 only + // IE9 only creates console object when dev tools are first opened + // IE9 console is a host object, callable but doesn't have .apply() + if ( !window.console || !window.console.log ) { + return; + } + + // Need jQuery 3.0.0+ and no older Migrate loaded + if ( !jQuery || rbadVersions.test( jQuery.fn.jquery ) ) { + window.console.log( "JQMIGRATE: jQuery 3.0.0+ REQUIRED" ); + } + if ( jQuery.migrateWarnings ) { + window.console.log( "JQMIGRATE: Migrate plugin loaded multiple times" ); + } + + // Show a message on the console so devs know we're active + window.console.log( "JQMIGRATE: Migrate is installed" + + ( jQuery.migrateMute ? "" : " with logging active" ) + + ", version " + jQuery.migrateVersion ); + +} )(); + +var warnedAbout = {}; + +// List of warnings already given; public read only +jQuery.migrateWarnings = []; + +// Set to false to disable traces that appear with warnings +if ( jQuery.migrateTrace === undefined ) { + jQuery.migrateTrace = true; +} + +// Forget any warnings we've already given; public +jQuery.migrateReset = function() { + warnedAbout = {}; + jQuery.migrateWarnings.length = 0; +}; + +function migrateWarn( msg ) { + var console = window.console; + if ( !warnedAbout[ msg ] ) { + warnedAbout[ msg ] = true; + jQuery.migrateWarnings.push( msg ); + if ( console && console.warn && !jQuery.migrateMute ) { + console.warn( "JQMIGRATE: " + msg ); + if ( jQuery.migrateTrace && console.trace ) { + console.trace(); + } + } + } +} + +function migrateWarnProp( obj, prop, value, msg ) { + Object.defineProperty( obj, prop, { + configurable: true, + enumerable: true, + get: function() { + migrateWarn( msg ); + return value; + }, + set: function( newValue ) { + migrateWarn( msg ); + value = newValue; + } + } ); +} + +function migrateWarnFunc( obj, prop, newFunc, msg ) { + obj[ prop ] = function() { + migrateWarn( msg ); + return newFunc.apply( this, arguments ); + }; +} + +if ( window.document.compatMode === "BackCompat" ) { + + // JQuery has never supported or tested Quirks Mode + migrateWarn( "jQuery is not compatible with Quirks Mode" ); +} + + +var oldInit = jQuery.fn.init, + oldIsNumeric = jQuery.isNumeric, + oldFind = jQuery.find, + rattrHashTest = /\[(\s*[-\w]+\s*)([~|^$*]?=)\s*([-\w#]*?#[-\w#]*)\s*\]/, + rattrHashGlob = /\[(\s*[-\w]+\s*)([~|^$*]?=)\s*([-\w#]*?#[-\w#]*)\s*\]/g; + +jQuery.fn.init = function( arg1 ) { + var args = Array.prototype.slice.call( arguments ); + + if ( typeof arg1 === "string" && arg1 === "#" ) { + + // JQuery( "#" ) is a bogus ID selector, but it returned an empty set before jQuery 3.0 + migrateWarn( "jQuery( '#' ) is not a valid selector" ); + args[ 0 ] = []; + } + + return oldInit.apply( this, args ); +}; +jQuery.fn.init.prototype = jQuery.fn; + +jQuery.find = function( selector ) { + var args = Array.prototype.slice.call( arguments ); + + // Support: PhantomJS 1.x + // String#match fails to match when used with a //g RegExp, only on some strings + if ( typeof selector === "string" && rattrHashTest.test( selector ) ) { + + // The nonstandard and undocumented unquoted-hash was removed in jQuery 1.12.0 + // First see if qS thinks it's a valid selector, if so avoid a false positive + try { + window.document.querySelector( selector ); + } catch ( err1 ) { + + // Didn't *look* valid to qSA, warn and try quoting what we think is the value + selector = selector.replace( rattrHashGlob, function( _, attr, op, value ) { + return "[" + attr + op + "\"" + value + "\"]"; + } ); + + // If the regexp *may* have created an invalid selector, don't update it + // Note that there may be false alarms if selector uses jQuery extensions + try { + window.document.querySelector( selector ); + migrateWarn( "Attribute selector with '#' must be quoted: " + args[ 0 ] ); + args[ 0 ] = selector; + } catch ( err2 ) { + migrateWarn( "Attribute selector with '#' was not fixed: " + args[ 0 ] ); + } + } + } + + return oldFind.apply( this, args ); +}; + +// Copy properties attached to original jQuery.find method (e.g. .attr, .isXML) +var findProp; +for ( findProp in oldFind ) { + if ( Object.prototype.hasOwnProperty.call( oldFind, findProp ) ) { + jQuery.find[ findProp ] = oldFind[ findProp ]; + } +} + +// The number of elements contained in the matched element set +jQuery.fn.size = function() { + migrateWarn( "jQuery.fn.size() is deprecated and removed; use the .length property" ); + return this.length; +}; + +jQuery.parseJSON = function() { + migrateWarn( "jQuery.parseJSON is deprecated; use JSON.parse" ); + return JSON.parse.apply( null, arguments ); +}; + +jQuery.isNumeric = function( val ) { + + // The jQuery 2.2.3 implementation of isNumeric + function isNumeric2( obj ) { + var realStringObj = obj && obj.toString(); + return !jQuery.isArray( obj ) && ( realStringObj - parseFloat( realStringObj ) + 1 ) >= 0; + } + + var newValue = oldIsNumeric( val ), + oldValue = isNumeric2( val ); + + if ( newValue !== oldValue ) { + migrateWarn( "jQuery.isNumeric() should not be called on constructed objects" ); + } + + return oldValue; +}; + +migrateWarnFunc( jQuery, "holdReady", jQuery.holdReady, + "jQuery.holdReady is deprecated" ); + +migrateWarnFunc( jQuery, "unique", jQuery.uniqueSort, + "jQuery.unique is deprecated; use jQuery.uniqueSort" ); + +// Now jQuery.expr.pseudos is the standard incantation +migrateWarnProp( jQuery.expr, "filters", jQuery.expr.pseudos, + "jQuery.expr.filters is deprecated; use jQuery.expr.pseudos" ); +migrateWarnProp( jQuery.expr, ":", jQuery.expr.pseudos, + "jQuery.expr[':'] is deprecated; use jQuery.expr.pseudos" ); + + +var oldAjax = jQuery.ajax; + +jQuery.ajax = function( ) { + var jQXHR = oldAjax.apply( this, arguments ); + + // Be sure we got a jQXHR (e.g., not sync) + if ( jQXHR.promise ) { + migrateWarnFunc( jQXHR, "success", jQXHR.done, + "jQXHR.success is deprecated and removed" ); + migrateWarnFunc( jQXHR, "error", jQXHR.fail, + "jQXHR.error is deprecated and removed" ); + migrateWarnFunc( jQXHR, "complete", jQXHR.always, + "jQXHR.complete is deprecated and removed" ); + } + + return jQXHR; +}; + + +var oldRemoveAttr = jQuery.fn.removeAttr, + oldToggleClass = jQuery.fn.toggleClass, + rmatchNonSpace = /\S+/g; + +jQuery.fn.removeAttr = function( name ) { + var self = this; + + jQuery.each( name.match( rmatchNonSpace ), function( i, attr ) { + if ( jQuery.expr.match.bool.test( attr ) ) { + migrateWarn( "jQuery.fn.removeAttr no longer sets boolean properties: " + attr ); + self.prop( attr, false ); + } + } ); + + return oldRemoveAttr.apply( this, arguments ); +}; + +jQuery.fn.toggleClass = function( state ) { + + // Only deprecating no-args or single boolean arg + if ( state !== undefined && typeof state !== "boolean" ) { + return oldToggleClass.apply( this, arguments ); + } + + migrateWarn( "jQuery.fn.toggleClass( boolean ) is deprecated" ); + + // Toggle entire class name of each element + return this.each( function() { + var className = this.getAttribute && this.getAttribute( "class" ) || ""; + + if ( className ) { + jQuery.data( this, "__className__", className ); + } + + // If the element has a class name or if we're passed `false`, + // then remove the whole classname (if there was one, the above saved it). + // Otherwise bring back whatever was previously saved (if anything), + // falling back to the empty string if nothing was stored. + if ( this.setAttribute ) { + this.setAttribute( "class", + className || state === false ? + "" : + jQuery.data( this, "__className__" ) || "" + ); + } + } ); +}; + + +var internalSwapCall = false; + +// If this version of jQuery has .swap(), don't false-alarm on internal uses +if ( jQuery.swap ) { + jQuery.each( [ "height", "width", "reliableMarginRight" ], function( _, name ) { + var oldHook = jQuery.cssHooks[ name ] && jQuery.cssHooks[ name ].get; + + if ( oldHook ) { + jQuery.cssHooks[ name ].get = function() { + var ret; + + internalSwapCall = true; + ret = oldHook.apply( this, arguments ); + internalSwapCall = false; + return ret; + }; + } + } ); +} + +jQuery.swap = function( elem, options, callback, args ) { + var ret, name, + old = {}; + + if ( !internalSwapCall ) { + migrateWarn( "jQuery.swap() is undocumented and deprecated" ); + } + + // Remember the old values, and insert the new ones + for ( name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + ret = callback.apply( elem, args || [] ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + + return ret; +}; + +var oldData = jQuery.data; + +jQuery.data = function( elem, name, value ) { + var curData; + + // Name can be an object, and each entry in the object is meant to be set as data + if ( name && typeof name === "object" && arguments.length === 2 ) { + curData = jQuery.hasData( elem ) && oldData.call( this, elem ); + var sameKeys = {}; + for ( var key in name ) { + if ( key !== jQuery.camelCase( key ) ) { + migrateWarn( "jQuery.data() always sets/gets camelCased names: " + key ); + curData[ key ] = name[ key ]; + } else { + sameKeys[ key ] = name[ key ]; + } + } + + oldData.call( this, elem, sameKeys ); + + return name; + } + + // If the name is transformed, look for the un-transformed name in the data object + if ( name && typeof name === "string" && name !== jQuery.camelCase( name ) ) { + curData = jQuery.hasData( elem ) && oldData.call( this, elem ); + if ( curData && name in curData ) { + migrateWarn( "jQuery.data() always sets/gets camelCased names: " + name ); + if ( arguments.length > 2 ) { + curData[ name ] = value; + } + return curData[ name ]; + } + } + + return oldData.apply( this, arguments ); +}; + +var oldTweenRun = jQuery.Tween.prototype.run; +var linearEasing = function( pct ) { + return pct; + }; + +jQuery.Tween.prototype.run = function( ) { + if ( jQuery.easing[ this.easing ].length > 1 ) { + migrateWarn( + "'jQuery.easing." + this.easing.toString() + "' should use only one argument" + ); + + jQuery.easing[ this.easing ] = linearEasing; + } + + oldTweenRun.apply( this, arguments ); +}; + +jQuery.fx.interval = jQuery.fx.interval || 13; + +// Support: IE9, Android <=4.4 +// Avoid false positives on browsers that lack rAF +if ( window.requestAnimationFrame ) { + migrateWarnProp( jQuery.fx, "interval", jQuery.fx.interval, + "jQuery.fx.interval is deprecated" ); +} + +var oldLoad = jQuery.fn.load, + oldEventAdd = jQuery.event.add, + originalFix = jQuery.event.fix; + +jQuery.event.props = []; +jQuery.event.fixHooks = {}; + +migrateWarnProp( jQuery.event.props, "concat", jQuery.event.props.concat, + "jQuery.event.props.concat() is deprecated and removed" ); + +jQuery.event.fix = function( originalEvent ) { + var event, + type = originalEvent.type, + fixHook = this.fixHooks[ type ], + props = jQuery.event.props; + + if ( props.length ) { + migrateWarn( "jQuery.event.props are deprecated and removed: " + props.join() ); + while ( props.length ) { + jQuery.event.addProp( props.pop() ); + } + } + + if ( fixHook && !fixHook._migrated_ ) { + fixHook._migrated_ = true; + migrateWarn( "jQuery.event.fixHooks are deprecated and removed: " + type ); + if ( ( props = fixHook.props ) && props.length ) { + while ( props.length ) { + jQuery.event.addProp( props.pop() ); + } + } + } + + event = originalFix.call( this, originalEvent ); + + return fixHook && fixHook.filter ? fixHook.filter( event, originalEvent ) : event; +}; + +jQuery.event.add = function( elem, types ) { + + // This misses the multiple-types case but that seems awfully rare + if ( elem === window && types === "load" && window.document.readyState === "complete" ) { + migrateWarn( "jQuery(window).on('load'...) called after load event occurred" ); + } + return oldEventAdd.apply( this, arguments ); +}; + +jQuery.each( [ "load", "unload", "error" ], function( _, name ) { + + jQuery.fn[ name ] = function() { + var args = Array.prototype.slice.call( arguments, 0 ); + + // If this is an ajax load() the first arg should be the string URL; + // technically this could also be the "Anything" arg of the event .load() + // which just goes to show why this dumb signature has been deprecated! + // jQuery custom builds that exclude the Ajax module justifiably die here. + if ( name === "load" && typeof args[ 0 ] === "string" ) { + return oldLoad.apply( this, args ); + } + + migrateWarn( "jQuery.fn." + name + "() is deprecated" ); + + args.splice( 0, 0, name ); + if ( arguments.length ) { + return this.on.apply( this, args ); + } + + // Use .triggerHandler here because: + // - load and unload events don't need to bubble, only applied to window or image + // - error event should not bubble to window, although it does pre-1.7 + // See http://bugs.jquery.com/ticket/11820 + this.triggerHandler.apply( this, args ); + return this; + }; + +} ); + +jQuery.each( ( "blur focus focusin focusout resize scroll click dblclick " + + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + + "change select submit keydown keypress keyup contextmenu" ).split( " " ), + function( i, name ) { + + // Handle event binding + jQuery.fn[ name ] = function( data, fn ) { + migrateWarn( "jQuery.fn." + name + "() event shorthand is deprecated" ); + return arguments.length > 0 ? + this.on( name, null, data, fn ) : + this.trigger( name ); + }; +} ); + +// Trigger "ready" event only once, on document ready +jQuery( function() { + jQuery( window.document ).triggerHandler( "ready" ); +} ); + +jQuery.event.special.ready = { + setup: function() { + if ( this === window.document ) { + migrateWarn( "'ready' event is deprecated" ); + } + } +}; + +jQuery.fn.extend( { + + bind: function( types, data, fn ) { + migrateWarn( "jQuery.fn.bind() is deprecated" ); + return this.on( types, null, data, fn ); + }, + unbind: function( types, fn ) { + migrateWarn( "jQuery.fn.unbind() is deprecated" ); + return this.off( types, null, fn ); + }, + delegate: function( selector, types, data, fn ) { + migrateWarn( "jQuery.fn.delegate() is deprecated" ); + return this.on( types, selector, data, fn ); + }, + undelegate: function( selector, types, fn ) { + migrateWarn( "jQuery.fn.undelegate() is deprecated" ); + return arguments.length === 1 ? + this.off( selector, "**" ) : + this.off( types, selector || "**", fn ); + }, + hover: function( fnOver, fnOut ) { + migrateWarn( "jQuery.fn.hover() is deprecated" ); + return this.on( "mouseenter", fnOver ).on( "mouseleave", fnOut || fnOver ); + } +} ); + + +var oldOffset = jQuery.fn.offset; + +jQuery.fn.offset = function() { + var docElem, + elem = this[ 0 ], + origin = { top: 0, left: 0 }; + + if ( !elem || !elem.nodeType ) { + migrateWarn( "jQuery.fn.offset() requires a valid DOM element" ); + return origin; + } + + docElem = ( elem.ownerDocument || window.document ).documentElement; + if ( !jQuery.contains( docElem, elem ) ) { + migrateWarn( "jQuery.fn.offset() requires an element connected to a document" ); + return origin; + } + + return oldOffset.apply( this, arguments ); +}; + + +var oldParam = jQuery.param; + +jQuery.param = function( data, traditional ) { + var ajaxTraditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional; + + if ( traditional === undefined && ajaxTraditional ) { + + migrateWarn( "jQuery.param() no longer uses jQuery.ajaxSettings.traditional" ); + traditional = ajaxTraditional; + } + + return oldParam.call( this, data, traditional ); +}; + +var oldSelf = jQuery.fn.andSelf || jQuery.fn.addBack; + +jQuery.fn.andSelf = function() { + migrateWarn( "jQuery.fn.andSelf() is deprecated and removed, use jQuery.fn.addBack()" ); + return oldSelf.apply( this, arguments ); +}; + + +var oldDeferred = jQuery.Deferred, + tuples = [ + + // Action, add listener, callbacks, .then handlers, final state + [ "resolve", "done", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), "resolved" ], + [ "reject", "fail", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), "rejected" ], + [ "notify", "progress", jQuery.Callbacks( "memory" ), + jQuery.Callbacks( "memory" ) ] + ]; + +jQuery.Deferred = function( func ) { + var deferred = oldDeferred(), + promise = deferred.promise(); + + deferred.pipe = promise.pipe = function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + + migrateWarn( "deferred.pipe() is deprecated" ); + + return jQuery.Deferred( function( newDefer ) { + jQuery.each( tuples, function( i, tuple ) { + var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; + + // Deferred.done(function() { bind to newDefer or newDefer.resolve }) + // deferred.fail(function() { bind to newDefer or newDefer.reject }) + // deferred.progress(function() { bind to newDefer or newDefer.notify }) + deferred[ tuple[ 1 ] ]( function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise() + .done( newDefer.resolve ) + .fail( newDefer.reject ) + .progress( newDefer.notify ); + } else { + newDefer[ tuple[ 0 ] + "With" ]( + this === promise ? newDefer.promise() : this, + fn ? [ returned ] : arguments + ); + } + } ); + } ); + fns = null; + } ).promise(); + + }; + + if ( func ) { + func.call( deferred, deferred ); + } + + return deferred; +}; + +// Preserve handler of uncaught exceptions in promise chains +jQuery.Deferred.exceptionHook = oldDeferred.exceptionHook; + +return jQuery; +} ); diff --git a/plugins/javadoc/src/main/resources/static_res/jquery/jquery-ui.css b/plugins/javadoc/src/main/resources/static_res/jquery/jquery-ui.css new file mode 100644 index 00000000..c4487b41 --- /dev/null +++ b/plugins/javadoc/src/main/resources/static_res/jquery/jquery-ui.css @@ -0,0 +1,582 @@ +/*! jQuery UI - v1.12.1 - 2018-12-06 +* http://jqueryui.com +* Includes: core.css, autocomplete.css, menu.css, theme.css +* To view and modify this theme, visit http://jqueryui.com/themeroller/?scope=&folderName=custom-theme&bgImgOpacityError=95&bgImgOpacityHighlight=55&bgImgOpacityActive=65&bgImgOpacityHover=75&bgImgOpacityDefault=75&bgImgOpacityContent=75&bgImgOpacityHeader=75&cornerRadiusShadow=8px&offsetLeftShadow=-8px&offsetTopShadow=-8px&thicknessShadow=8px&opacityShadow=30&bgImgOpacityShadow=0&bgTextureShadow=flat&bgColorShadow=%23aaaaaa&opacityOverlay=30&bgImgOpacityOverlay=0&bgTextureOverlay=flat&bgColorOverlay=%23aaaaaa&iconColorError=%23cd0a0a&fcError=%23cd0a0a&borderColorError=%23cd0a0a&bgTextureError=glass&bgColorError=%23fef1ec&iconColorHighlight=%232e83ff&fcHighlight=%23363636&borderColorHighlight=%23fcefa1&bgTextureHighlight=glass&bgColorHighlight=%23fbf9ee&iconColorActive=%23454545&fcActive=%23212121&borderColorActive=%23aaaaaa&bgTextureActive=glass&bgColorActive=%23dadada&iconColorHover=%23454545&fcHover=%23212121&borderColorHover=%23999999&bgTextureHover=glass&bgColorHover=%23dadada&iconColorDefault=%23888888&fcDefault=%23555555&borderColorDefault=%23d3d3d3&bgTextureDefault=glass&bgColorDefault=%23e6e6e6&iconColorContent=%23222222&fcContent=%23222222&borderColorContent=%23aaaaaa&bgTextureContent=flat&bgColorContent=%23ffffff&iconColorHeader=%23222222&fcHeader=%23222222&borderColorHeader=%23aaaaaa&bgTextureHeader=highlight_soft&bgColorHeader=%23cccccc&cornerRadius=4px&fwDefault=normal&fsDefault=1.1em&ffDefault=Verdana%2CArial%2Csans-serif +* Copyright jQuery Foundation and other contributors; Licensed MIT */ + +/* Layout helpers +----------------------------------*/ +.ui-helper-hidden { + display: none; +} +.ui-helper-hidden-accessible { + border: 0; + clip: rect(0 0 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; +} +.ui-helper-reset { + margin: 0; + padding: 0; + border: 0; + outline: 0; + line-height: 1.3; + text-decoration: none; + font-size: 100%; + list-style: none; +} +.ui-helper-clearfix:before, +.ui-helper-clearfix:after { + content: ""; + display: table; + border-collapse: collapse; +} +.ui-helper-clearfix:after { + clear: both; +} +.ui-helper-zfix { + width: 100%; + height: 100%; + top: 0; + left: 0; + position: absolute; + opacity: 0; + filter:Alpha(Opacity=0); /* support: IE8 */ +} + +.ui-front { + z-index: 100; +} + + +/* Interaction Cues +----------------------------------*/ +.ui-state-disabled { + cursor: default !important; + pointer-events: none; +} + + +/* Icons +----------------------------------*/ +.ui-icon { + display: inline-block; + vertical-align: middle; + margin-top: -.25em; + position: relative; + text-indent: -99999px; + overflow: hidden; + background-repeat: no-repeat; +} + +.ui-widget-icon-block { + left: 50%; + margin-left: -8px; + display: block; +} + +/* Misc visuals +----------------------------------*/ + +/* Overlays */ +.ui-widget-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; +} +.ui-autocomplete { + position: absolute; + top: 0; + left: 0; + cursor: default; +} +.ui-menu { + list-style: none; + padding: 0; + margin: 0; + display: block; + outline: 0; +} +.ui-menu .ui-menu { + position: absolute; +} +.ui-menu .ui-menu-item { + margin: 0; + cursor: pointer; + /* support: IE10, see #8844 */ + list-style-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"); +} +.ui-menu .ui-menu-item-wrapper { + position: relative; + padding: 3px 1em 3px .4em; +} +.ui-menu .ui-menu-divider { + margin: 5px 0; + height: 0; + font-size: 0; + line-height: 0; + border-width: 1px 0 0 0; +} +.ui-menu .ui-state-focus, +.ui-menu .ui-state-active { + margin: -1px; +} + +/* icon support */ +.ui-menu-icons { + position: relative; +} +.ui-menu-icons .ui-menu-item-wrapper { + padding-left: 2em; +} + +/* left-aligned */ +.ui-menu .ui-icon { + position: absolute; + top: 0; + bottom: 0; + left: .2em; + margin: auto 0; +} + +/* right-aligned */ +.ui-menu .ui-menu-icon { + left: auto; + right: 0; +} + +/* Component containers +----------------------------------*/ +.ui-widget { + font-family: Verdana,Arial,sans-serif; + font-size: 1.1em; +} +.ui-widget .ui-widget { + font-size: 1em; +} +.ui-widget input, +.ui-widget select, +.ui-widget textarea, +.ui-widget button { + font-family: Verdana,Arial,sans-serif; + font-size: 1em; +} +.ui-widget.ui-widget-content { + border: 1px solid #d3d3d3; +} +.ui-widget-content { + border: 1px solid #aaaaaa; + background: #ffffff; + color: #222222; +} +.ui-widget-content a { + color: #222222; +} +.ui-widget-header { + border: 1px solid #aaaaaa; + background: #cccccc url("images/ui-bg_highlight-soft_75_cccccc_1x100.png") 50% 50% repeat-x; + color: #222222; + font-weight: bold; +} +.ui-widget-header a { + color: #222222; +} + +/* Interaction states +----------------------------------*/ +.ui-state-default, +.ui-widget-content .ui-state-default, +.ui-widget-header .ui-state-default, +.ui-button, + +/* We use html here because we need a greater specificity to make sure disabled +works properly when clicked or hovered */ +html .ui-button.ui-state-disabled:hover, +html .ui-button.ui-state-disabled:active { + border: 1px solid #d3d3d3; + background: #e6e6e6 url("images/ui-bg_glass_75_e6e6e6_1x400.png") 50% 50% repeat-x; + font-weight: normal; + color: #555555; +} +.ui-state-default a, +.ui-state-default a:link, +.ui-state-default a:visited, +a.ui-button, +a:link.ui-button, +a:visited.ui-button, +.ui-button { + color: #555555; + text-decoration: none; +} +.ui-state-hover, +.ui-widget-content .ui-state-hover, +.ui-widget-header .ui-state-hover, +.ui-state-focus, +.ui-widget-content .ui-state-focus, +.ui-widget-header .ui-state-focus, +.ui-button:hover, +.ui-button:focus { + border: 1px solid #999999; + background: #dadada url("images/ui-bg_glass_75_dadada_1x400.png") 50% 50% repeat-x; + font-weight: normal; + color: #212121; +} +.ui-state-hover a, +.ui-state-hover a:hover, +.ui-state-hover a:link, +.ui-state-hover a:visited, +.ui-state-focus a, +.ui-state-focus a:hover, +.ui-state-focus a:link, +.ui-state-focus a:visited, +a.ui-button:hover, +a.ui-button:focus { + color: #212121; + text-decoration: none; +} + +.ui-visual-focus { + box-shadow: 0 0 3px 1px rgb(94, 158, 214); +} +.ui-state-active, +.ui-widget-content .ui-state-active, +.ui-widget-header .ui-state-active, +a.ui-button:active, +.ui-button:active, +.ui-button.ui-state-active:hover { + border: 1px solid #aaaaaa; + background: #dadada url("images/ui-bg_glass_65_dadada_1x400.png") 50% 50% repeat-x; + font-weight: normal; + color: #212121; +} +.ui-icon-background, +.ui-state-active .ui-icon-background { + border: #aaaaaa; + background-color: #212121; +} +.ui-state-active a, +.ui-state-active a:link, +.ui-state-active a:visited { + color: #212121; + text-decoration: none; +} + +/* Interaction Cues +----------------------------------*/ +.ui-state-highlight, +.ui-widget-content .ui-state-highlight, +.ui-widget-header .ui-state-highlight { + border: 1px solid #fcefa1; + background: #fbf9ee url("images/ui-bg_glass_55_fbf9ee_1x400.png") 50% 50% repeat-x; + color: #363636; +} +.ui-state-checked { + border: 1px solid #fcefa1; + background: #fbf9ee; +} +.ui-state-highlight a, +.ui-widget-content .ui-state-highlight a, +.ui-widget-header .ui-state-highlight a { + color: #363636; +} +.ui-state-error, +.ui-widget-content .ui-state-error, +.ui-widget-header .ui-state-error { + border: 1px solid #cd0a0a; + background: #fef1ec url("images/ui-bg_glass_95_fef1ec_1x400.png") 50% 50% repeat-x; + color: #cd0a0a; +} +.ui-state-error a, +.ui-widget-content .ui-state-error a, +.ui-widget-header .ui-state-error a { + color: #cd0a0a; +} +.ui-state-error-text, +.ui-widget-content .ui-state-error-text, +.ui-widget-header .ui-state-error-text { + color: #cd0a0a; +} +.ui-priority-primary, +.ui-widget-content .ui-priority-primary, +.ui-widget-header .ui-priority-primary { + font-weight: bold; +} +.ui-priority-secondary, +.ui-widget-content .ui-priority-secondary, +.ui-widget-header .ui-priority-secondary { + opacity: .7; + filter:Alpha(Opacity=70); /* support: IE8 */ + font-weight: normal; +} +.ui-state-disabled, +.ui-widget-content .ui-state-disabled, +.ui-widget-header .ui-state-disabled { + opacity: .35; + filter:Alpha(Opacity=35); /* support: IE8 */ + background-image: none; +} +.ui-state-disabled .ui-icon { + filter:Alpha(Opacity=35); /* support: IE8 - See #6059 */ +} + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { + width: 16px; + height: 16px; +} +.ui-icon, +.ui-widget-content .ui-icon { + background-image: url("images/ui-icons_222222_256x240.png"); +} +.ui-widget-header .ui-icon { + background-image: url("images/ui-icons_222222_256x240.png"); +} +.ui-state-hover .ui-icon, +.ui-state-focus .ui-icon, +.ui-button:hover .ui-icon, +.ui-button:focus .ui-icon { + background-image: url("images/ui-icons_454545_256x240.png"); +} +.ui-state-active .ui-icon, +.ui-button:active .ui-icon { + background-image: url("images/ui-icons_454545_256x240.png"); +} +.ui-state-highlight .ui-icon, +.ui-button .ui-state-highlight.ui-icon { + background-image: url("images/ui-icons_2e83ff_256x240.png"); +} +.ui-state-error .ui-icon, +.ui-state-error-text .ui-icon { + background-image: url("images/ui-icons_cd0a0a_256x240.png"); +} +.ui-button .ui-icon { + background-image: url("images/ui-icons_888888_256x240.png"); +} + +/* positioning */ +.ui-icon-blank { background-position: 16px 16px; } +.ui-icon-caret-1-n { background-position: 0 0; } +.ui-icon-caret-1-ne { background-position: -16px 0; } +.ui-icon-caret-1-e { background-position: -32px 0; } +.ui-icon-caret-1-se { background-position: -48px 0; } +.ui-icon-caret-1-s { background-position: -65px 0; } +.ui-icon-caret-1-sw { background-position: -80px 0; } +.ui-icon-caret-1-w { background-position: -96px 0; } +.ui-icon-caret-1-nw { background-position: -112px 0; } +.ui-icon-caret-2-n-s { background-position: -128px 0; } +.ui-icon-caret-2-e-w { background-position: -144px 0; } +.ui-icon-triangle-1-n { background-position: 0 -16px; } +.ui-icon-triangle-1-ne { background-position: -16px -16px; } +.ui-icon-triangle-1-e { background-position: -32px -16px; } +.ui-icon-triangle-1-se { background-position: -48px -16px; } +.ui-icon-triangle-1-s { background-position: -65px -16px; } +.ui-icon-triangle-1-sw { background-position: -80px -16px; } +.ui-icon-triangle-1-w { background-position: -96px -16px; } +.ui-icon-triangle-1-nw { background-position: -112px -16px; } +.ui-icon-triangle-2-n-s { background-position: -128px -16px; } +.ui-icon-triangle-2-e-w { background-position: -144px -16px; } +.ui-icon-arrow-1-n { background-position: 0 -32px; } +.ui-icon-arrow-1-ne { background-position: -16px -32px; } +.ui-icon-arrow-1-e { background-position: -32px -32px; } +.ui-icon-arrow-1-se { background-position: -48px -32px; } +.ui-icon-arrow-1-s { background-position: -65px -32px; } +.ui-icon-arrow-1-sw { background-position: -80px -32px; } +.ui-icon-arrow-1-w { background-position: -96px -32px; } +.ui-icon-arrow-1-nw { background-position: -112px -32px; } +.ui-icon-arrow-2-n-s { background-position: -128px -32px; } +.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } +.ui-icon-arrow-2-e-w { background-position: -160px -32px; } +.ui-icon-arrow-2-se-nw { background-position: -176px -32px; } +.ui-icon-arrowstop-1-n { background-position: -192px -32px; } +.ui-icon-arrowstop-1-e { background-position: -208px -32px; } +.ui-icon-arrowstop-1-s { background-position: -224px -32px; } +.ui-icon-arrowstop-1-w { background-position: -240px -32px; } +.ui-icon-arrowthick-1-n { background-position: 1px -48px; } +.ui-icon-arrowthick-1-ne { background-position: -16px -48px; } +.ui-icon-arrowthick-1-e { background-position: -32px -48px; } +.ui-icon-arrowthick-1-se { background-position: -48px -48px; } +.ui-icon-arrowthick-1-s { background-position: -64px -48px; } +.ui-icon-arrowthick-1-sw { background-position: -80px -48px; } +.ui-icon-arrowthick-1-w { background-position: -96px -48px; } +.ui-icon-arrowthick-1-nw { background-position: -112px -48px; } +.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } +.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } +.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } +.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } +.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } +.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } +.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } +.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } +.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } +.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } +.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } +.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } +.ui-icon-arrowreturn-1-w { background-position: -64px -64px; } +.ui-icon-arrowreturn-1-n { background-position: -80px -64px; } +.ui-icon-arrowreturn-1-e { background-position: -96px -64px; } +.ui-icon-arrowreturn-1-s { background-position: -112px -64px; } +.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } +.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } +.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } +.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } +.ui-icon-arrow-4 { background-position: 0 -80px; } +.ui-icon-arrow-4-diag { background-position: -16px -80px; } +.ui-icon-extlink { background-position: -32px -80px; } +.ui-icon-newwin { background-position: -48px -80px; } +.ui-icon-refresh { background-position: -64px -80px; } +.ui-icon-shuffle { background-position: -80px -80px; } +.ui-icon-transfer-e-w { background-position: -96px -80px; } +.ui-icon-transferthick-e-w { background-position: -112px -80px; } +.ui-icon-folder-collapsed { background-position: 0 -96px; } +.ui-icon-folder-open { background-position: -16px -96px; } +.ui-icon-document { background-position: -32px -96px; } +.ui-icon-document-b { background-position: -48px -96px; } +.ui-icon-note { background-position: -64px -96px; } +.ui-icon-mail-closed { background-position: -80px -96px; } +.ui-icon-mail-open { background-position: -96px -96px; } +.ui-icon-suitcase { background-position: -112px -96px; } +.ui-icon-comment { background-position: -128px -96px; } +.ui-icon-person { background-position: -144px -96px; } +.ui-icon-print { background-position: -160px -96px; } +.ui-icon-trash { background-position: -176px -96px; } +.ui-icon-locked { background-position: -192px -96px; } +.ui-icon-unlocked { background-position: -208px -96px; } +.ui-icon-bookmark { background-position: -224px -96px; } +.ui-icon-tag { background-position: -240px -96px; } +.ui-icon-home { background-position: 0 -112px; } +.ui-icon-flag { background-position: -16px -112px; } +.ui-icon-calendar { background-position: -32px -112px; } +.ui-icon-cart { background-position: -48px -112px; } +.ui-icon-pencil { background-position: -64px -112px; } +.ui-icon-clock { background-position: -80px -112px; } +.ui-icon-disk { background-position: -96px -112px; } +.ui-icon-calculator { background-position: -112px -112px; } +.ui-icon-zoomin { background-position: -128px -112px; } +.ui-icon-zoomout { background-position: -144px -112px; } +.ui-icon-search { background-position: -160px -112px; } +.ui-icon-wrench { background-position: -176px -112px; } +.ui-icon-gear { background-position: -192px -112px; } +.ui-icon-heart { background-position: -208px -112px; } +.ui-icon-star { background-position: -224px -112px; } +.ui-icon-link { background-position: -240px -112px; } +.ui-icon-cancel { background-position: 0 -128px; } +.ui-icon-plus { background-position: -16px -128px; } +.ui-icon-plusthick { background-position: -32px -128px; } +.ui-icon-minus { background-position: -48px -128px; } +.ui-icon-minusthick { background-position: -64px -128px; } +.ui-icon-close { background-position: -80px -128px; } +.ui-icon-closethick { background-position: -96px -128px; } +.ui-icon-key { background-position: -112px -128px; } +.ui-icon-lightbulb { background-position: -128px -128px; } +.ui-icon-scissors { background-position: -144px -128px; } +.ui-icon-clipboard { background-position: -160px -128px; } +.ui-icon-copy { background-position: -176px -128px; } +.ui-icon-contact { background-position: -192px -128px; } +.ui-icon-image { background-position: -208px -128px; } +.ui-icon-video { background-position: -224px -128px; } +.ui-icon-script { background-position: -240px -128px; } +.ui-icon-alert { background-position: 0 -144px; } +.ui-icon-info { background-position: -16px -144px; } +.ui-icon-notice { background-position: -32px -144px; } +.ui-icon-help { background-position: -48px -144px; } +.ui-icon-check { background-position: -64px -144px; } +.ui-icon-bullet { background-position: -80px -144px; } +.ui-icon-radio-on { background-position: -96px -144px; } +.ui-icon-radio-off { background-position: -112px -144px; } +.ui-icon-pin-w { background-position: -128px -144px; } +.ui-icon-pin-s { background-position: -144px -144px; } +.ui-icon-play { background-position: 0 -160px; } +.ui-icon-pause { background-position: -16px -160px; } +.ui-icon-seek-next { background-position: -32px -160px; } +.ui-icon-seek-prev { background-position: -48px -160px; } +.ui-icon-seek-end { background-position: -64px -160px; } +.ui-icon-seek-start { background-position: -80px -160px; } +/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ +.ui-icon-seek-first { background-position: -80px -160px; } +.ui-icon-stop { background-position: -96px -160px; } +.ui-icon-eject { background-position: -112px -160px; } +.ui-icon-volume-off { background-position: -128px -160px; } +.ui-icon-volume-on { background-position: -144px -160px; } +.ui-icon-power { background-position: 0 -176px; } +.ui-icon-signal-diag { background-position: -16px -176px; } +.ui-icon-signal { background-position: -32px -176px; } +.ui-icon-battery-0 { background-position: -48px -176px; } +.ui-icon-battery-1 { background-position: -64px -176px; } +.ui-icon-battery-2 { background-position: -80px -176px; } +.ui-icon-battery-3 { background-position: -96px -176px; } +.ui-icon-circle-plus { background-position: 0 -192px; } +.ui-icon-circle-minus { background-position: -16px -192px; } +.ui-icon-circle-close { background-position: -32px -192px; } +.ui-icon-circle-triangle-e { background-position: -48px -192px; } +.ui-icon-circle-triangle-s { background-position: -64px -192px; } +.ui-icon-circle-triangle-w { background-position: -80px -192px; } +.ui-icon-circle-triangle-n { background-position: -96px -192px; } +.ui-icon-circle-arrow-e { background-position: -112px -192px; } +.ui-icon-circle-arrow-s { background-position: -128px -192px; } +.ui-icon-circle-arrow-w { background-position: -144px -192px; } +.ui-icon-circle-arrow-n { background-position: -160px -192px; } +.ui-icon-circle-zoomin { background-position: -176px -192px; } +.ui-icon-circle-zoomout { background-position: -192px -192px; } +.ui-icon-circle-check { background-position: -208px -192px; } +.ui-icon-circlesmall-plus { background-position: 0 -208px; } +.ui-icon-circlesmall-minus { background-position: -16px -208px; } +.ui-icon-circlesmall-close { background-position: -32px -208px; } +.ui-icon-squaresmall-plus { background-position: -48px -208px; } +.ui-icon-squaresmall-minus { background-position: -64px -208px; } +.ui-icon-squaresmall-close { background-position: -80px -208px; } +.ui-icon-grip-dotted-vertical { background-position: 0 -224px; } +.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } +.ui-icon-grip-solid-vertical { background-position: -32px -224px; } +.ui-icon-grip-solid-horizontal { background-position: -48px -224px; } +.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } +.ui-icon-grip-diagonal-se { background-position: -80px -224px; } + + +/* Misc visuals +----------------------------------*/ + +/* Corner radius */ +.ui-corner-all, +.ui-corner-top, +.ui-corner-left, +.ui-corner-tl { + border-top-left-radius: 4px; +} +.ui-corner-all, +.ui-corner-top, +.ui-corner-right, +.ui-corner-tr { + border-top-right-radius: 4px; +} +.ui-corner-all, +.ui-corner-bottom, +.ui-corner-left, +.ui-corner-bl { + border-bottom-left-radius: 4px; +} +.ui-corner-all, +.ui-corner-bottom, +.ui-corner-right, +.ui-corner-br { + border-bottom-right-radius: 4px; +} + +/* Overlays */ +.ui-widget-overlay { + background: #aaaaaa; + opacity: .3; + filter: Alpha(Opacity=30); /* support: IE8 */ +} +.ui-widget-shadow { + -webkit-box-shadow: -8px -8px 8px #aaaaaa; + box-shadow: -8px -8px 8px #aaaaaa; +} diff --git a/plugins/javadoc/src/main/resources/static_res/jquery/jquery-ui.js b/plugins/javadoc/src/main/resources/static_res/jquery/jquery-ui.js new file mode 100644 index 00000000..87fb2143 --- /dev/null +++ b/plugins/javadoc/src/main/resources/static_res/jquery/jquery-ui.js @@ -0,0 +1,2659 @@ +/*! jQuery UI - v1.12.1 - 2018-12-06 +* http://jqueryui.com +* Includes: widget.js, position.js, keycode.js, unique-id.js, widgets/autocomplete.js, widgets/menu.js +* Copyright jQuery Foundation and other contributors; Licensed MIT */ + +(function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define([ "jquery" ], factory ); + } else { + + // Browser globals + factory( jQuery ); + } +}(function( $ ) { + +$.ui = $.ui || {}; + +var version = $.ui.version = "1.12.1"; + + +/*! + * jQuery UI Widget 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Widget +//>>group: Core +//>>description: Provides a factory for creating stateful widgets with a common API. +//>>docs: http://api.jqueryui.com/jQuery.widget/ +//>>demos: http://jqueryui.com/widget/ + + + +var widgetUuid = 0; +var widgetSlice = Array.prototype.slice; + +$.cleanData = ( function( orig ) { + return function( elems ) { + var events, elem, i; + for ( i = 0; ( elem = elems[ i ] ) != null; i++ ) { + try { + + // Only trigger remove when necessary to save time + events = $._data( elem, "events" ); + if ( events && events.remove ) { + $( elem ).triggerHandler( "remove" ); + } + + // Http://bugs.jquery.com/ticket/8235 + } catch ( e ) {} + } + orig( elems ); + }; +} )( $.cleanData ); + +$.widget = function( name, base, prototype ) { + var existingConstructor, constructor, basePrototype; + + // ProxiedPrototype allows the provided prototype to remain unmodified + // so that it can be used as a mixin for multiple widgets (#8876) + var proxiedPrototype = {}; + + var namespace = name.split( "." )[ 0 ]; + name = name.split( "." )[ 1 ]; + var fullName = namespace + "-" + name; + + if ( !prototype ) { + prototype = base; + base = $.Widget; + } + + if ( $.isArray( prototype ) ) { + prototype = $.extend.apply( null, [ {} ].concat( prototype ) ); + } + + // Create selector for plugin + $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) { + return !!$.data( elem, fullName ); + }; + + $[ namespace ] = $[ namespace ] || {}; + existingConstructor = $[ namespace ][ name ]; + constructor = $[ namespace ][ name ] = function( options, element ) { + + // Allow instantiation without "new" keyword + if ( !this._createWidget ) { + return new constructor( options, element ); + } + + // Allow instantiation without initializing for simple inheritance + // must use "new" keyword (the code above always passes args) + if ( arguments.length ) { + this._createWidget( options, element ); + } + }; + + // Extend with the existing constructor to carry over any static properties + $.extend( constructor, existingConstructor, { + version: prototype.version, + + // Copy the object used to create the prototype in case we need to + // redefine the widget later + _proto: $.extend( {}, prototype ), + + // Track widgets that inherit from this widget in case this widget is + // redefined after a widget inherits from it + _childConstructors: [] + } ); + + basePrototype = new base(); + + // We need to make the options hash a property directly on the new instance + // otherwise we'll modify the options hash on the prototype that we're + // inheriting from + basePrototype.options = $.widget.extend( {}, basePrototype.options ); + $.each( prototype, function( prop, value ) { + if ( !$.isFunction( value ) ) { + proxiedPrototype[ prop ] = value; + return; + } + proxiedPrototype[ prop ] = ( function() { + function _super() { + return base.prototype[ prop ].apply( this, arguments ); + } + + function _superApply( args ) { + return base.prototype[ prop ].apply( this, args ); + } + + return function() { + var __super = this._super; + var __superApply = this._superApply; + var returnValue; + + this._super = _super; + this._superApply = _superApply; + + returnValue = value.apply( this, arguments ); + + this._super = __super; + this._superApply = __superApply; + + return returnValue; + }; + } )(); + } ); + constructor.prototype = $.widget.extend( basePrototype, { + + // TODO: remove support for widgetEventPrefix + // always use the name + a colon as the prefix, e.g., draggable:start + // don't prefix for widgets that aren't DOM-based + widgetEventPrefix: existingConstructor ? ( basePrototype.widgetEventPrefix || name ) : name + }, proxiedPrototype, { + constructor: constructor, + namespace: namespace, + widgetName: name, + widgetFullName: fullName + } ); + + // If this widget is being redefined then we need to find all widgets that + // are inheriting from it and redefine all of them so that they inherit from + // the new version of this widget. We're essentially trying to replace one + // level in the prototype chain. + if ( existingConstructor ) { + $.each( existingConstructor._childConstructors, function( i, child ) { + var childPrototype = child.prototype; + + // Redefine the child widget using the same prototype that was + // originally used, but inherit from the new version of the base + $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, + child._proto ); + } ); + + // Remove the list of existing child constructors from the old constructor + // so the old child constructors can be garbage collected + delete existingConstructor._childConstructors; + } else { + base._childConstructors.push( constructor ); + } + + $.widget.bridge( name, constructor ); + + return constructor; +}; + +$.widget.extend = function( target ) { + var input = widgetSlice.call( arguments, 1 ); + var inputIndex = 0; + var inputLength = input.length; + var key; + var value; + + for ( ; inputIndex < inputLength; inputIndex++ ) { + for ( key in input[ inputIndex ] ) { + value = input[ inputIndex ][ key ]; + if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) { + + // Clone objects + if ( $.isPlainObject( value ) ) { + target[ key ] = $.isPlainObject( target[ key ] ) ? + $.widget.extend( {}, target[ key ], value ) : + + // Don't extend strings, arrays, etc. with objects + $.widget.extend( {}, value ); + + // Copy everything else by reference + } else { + target[ key ] = value; + } + } + } + } + return target; +}; + +$.widget.bridge = function( name, object ) { + var fullName = object.prototype.widgetFullName || name; + $.fn[ name ] = function( options ) { + var isMethodCall = typeof options === "string"; + var args = widgetSlice.call( arguments, 1 ); + var returnValue = this; + + if ( isMethodCall ) { + + // If this is an empty collection, we need to have the instance method + // return undefined instead of the jQuery instance + if ( !this.length && options === "instance" ) { + returnValue = undefined; + } else { + this.each( function() { + var methodValue; + var instance = $.data( this, fullName ); + + if ( options === "instance" ) { + returnValue = instance; + return false; + } + + if ( !instance ) { + return $.error( "cannot call methods on " + name + + " prior to initialization; " + + "attempted to call method '" + options + "'" ); + } + + if ( !$.isFunction( instance[ options ] ) || options.charAt( 0 ) === "_" ) { + return $.error( "no such method '" + options + "' for " + name + + " widget instance" ); + } + + methodValue = instance[ options ].apply( instance, args ); + + if ( methodValue !== instance && methodValue !== undefined ) { + returnValue = methodValue && methodValue.jquery ? + returnValue.pushStack( methodValue.get() ) : + methodValue; + return false; + } + } ); + } + } else { + + // Allow multiple hashes to be passed on init + if ( args.length ) { + options = $.widget.extend.apply( null, [ options ].concat( args ) ); + } + + this.each( function() { + var instance = $.data( this, fullName ); + if ( instance ) { + instance.option( options || {} ); + if ( instance._init ) { + instance._init(); + } + } else { + $.data( this, fullName, new object( options, this ) ); + } + } ); + } + + return returnValue; + }; +}; + +$.Widget = function( /* options, element */ ) {}; +$.Widget._childConstructors = []; + +$.Widget.prototype = { + widgetName: "widget", + widgetEventPrefix: "", + defaultElement: "<div>", + + options: { + classes: {}, + disabled: false, + + // Callbacks + create: null + }, + + _createWidget: function( options, element ) { + element = $( element || this.defaultElement || this )[ 0 ]; + this.element = $( element ); + this.uuid = widgetUuid++; + this.eventNamespace = "." + this.widgetName + this.uuid; + + this.bindings = $(); + this.hoverable = $(); + this.focusable = $(); + this.classesElementLookup = {}; + + if ( element !== this ) { + $.data( element, this.widgetFullName, this ); + this._on( true, this.element, { + remove: function( event ) { + if ( event.target === element ) { + this.destroy(); + } + } + } ); + this.document = $( element.style ? + + // Element within the document + element.ownerDocument : + + // Element is window or document + element.document || element ); + this.window = $( this.document[ 0 ].defaultView || this.document[ 0 ].parentWindow ); + } + + this.options = $.widget.extend( {}, + this.options, + this._getCreateOptions(), + options ); + + this._create(); + + if ( this.options.disabled ) { + this._setOptionDisabled( this.options.disabled ); + } + + this._trigger( "create", null, this._getCreateEventData() ); + this._init(); + }, + + _getCreateOptions: function() { + return {}; + }, + + _getCreateEventData: $.noop, + + _create: $.noop, + + _init: $.noop, + + destroy: function() { + var that = this; + + this._destroy(); + $.each( this.classesElementLookup, function( key, value ) { + that._removeClass( value, key ); + } ); + + // We can probably remove the unbind calls in 2.0 + // all event bindings should go through this._on() + this.element + .off( this.eventNamespace ) + .removeData( this.widgetFullName ); + this.widget() + .off( this.eventNamespace ) + .removeAttr( "aria-disabled" ); + + // Clean up events and states + this.bindings.off( this.eventNamespace ); + }, + + _destroy: $.noop, + + widget: function() { + return this.element; + }, + + option: function( key, value ) { + var options = key; + var parts; + var curOption; + var i; + + if ( arguments.length === 0 ) { + + // Don't return a reference to the internal hash + return $.widget.extend( {}, this.options ); + } + + if ( typeof key === "string" ) { + + // Handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } } + options = {}; + parts = key.split( "." ); + key = parts.shift(); + if ( parts.length ) { + curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] ); + for ( i = 0; i < parts.length - 1; i++ ) { + curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {}; + curOption = curOption[ parts[ i ] ]; + } + key = parts.pop(); + if ( arguments.length === 1 ) { + return curOption[ key ] === undefined ? null : curOption[ key ]; + } + curOption[ key ] = value; + } else { + if ( arguments.length === 1 ) { + return this.options[ key ] === undefined ? null : this.options[ key ]; + } + options[ key ] = value; + } + } + + this._setOptions( options ); + + return this; + }, + + _setOptions: function( options ) { + var key; + + for ( key in options ) { + this._setOption( key, options[ key ] ); + } + + return this; + }, + + _setOption: function( key, value ) { + if ( key === "classes" ) { + this._setOptionClasses( value ); + } + + this.options[ key ] = value; + + if ( key === "disabled" ) { + this._setOptionDisabled( value ); + } + + return this; + }, + + _setOptionClasses: function( value ) { + var classKey, elements, currentElements; + + for ( classKey in value ) { + currentElements = this.classesElementLookup[ classKey ]; + if ( value[ classKey ] === this.options.classes[ classKey ] || + !currentElements || + !currentElements.length ) { + continue; + } + + // We are doing this to create a new jQuery object because the _removeClass() call + // on the next line is going to destroy the reference to the current elements being + // tracked. We need to save a copy of this collection so that we can add the new classes + // below. + elements = $( currentElements.get() ); + this._removeClass( currentElements, classKey ); + + // We don't use _addClass() here, because that uses this.options.classes + // for generating the string of classes. We want to use the value passed in from + // _setOption(), this is the new value of the classes option which was passed to + // _setOption(). We pass this value directly to _classes(). + elements.addClass( this._classes( { + element: elements, + keys: classKey, + classes: value, + add: true + } ) ); + } + }, + + _setOptionDisabled: function( value ) { + this._toggleClass( this.widget(), this.widgetFullName + "-disabled", null, !!value ); + + // If the widget is becoming disabled, then nothing is interactive + if ( value ) { + this._removeClass( this.hoverable, null, "ui-state-hover" ); + this._removeClass( this.focusable, null, "ui-state-focus" ); + } + }, + + enable: function() { + return this._setOptions( { disabled: false } ); + }, + + disable: function() { + return this._setOptions( { disabled: true } ); + }, + + _classes: function( options ) { + var full = []; + var that = this; + + options = $.extend( { + element: this.element, + classes: this.options.classes || {} + }, options ); + + function processClassString( classes, checkOption ) { + var current, i; + for ( i = 0; i < classes.length; i++ ) { + current = that.classesElementLookup[ classes[ i ] ] || $(); + if ( options.add ) { + current = $( $.unique( current.get().concat( options.element.get() ) ) ); + } else { + current = $( current.not( options.element ).get() ); + } + that.classesElementLookup[ classes[ i ] ] = current; + full.push( classes[ i ] ); + if ( checkOption && options.classes[ classes[ i ] ] ) { + full.push( options.classes[ classes[ i ] ] ); + } + } + } + + this._on( options.element, { + "remove": "_untrackClassesElement" + } ); + + if ( options.keys ) { + processClassString( options.keys.match( /\S+/g ) || [], true ); + } + if ( options.extra ) { + processClassString( options.extra.match( /\S+/g ) || [] ); + } + + return full.join( " " ); + }, + + _untrackClassesElement: function( event ) { + var that = this; + $.each( that.classesElementLookup, function( key, value ) { + if ( $.inArray( event.target, value ) !== -1 ) { + that.classesElementLookup[ key ] = $( value.not( event.target ).get() ); + } + } ); + }, + + _removeClass: function( element, keys, extra ) { + return this._toggleClass( element, keys, extra, false ); + }, + + _addClass: function( element, keys, extra ) { + return this._toggleClass( element, keys, extra, true ); + }, + + _toggleClass: function( element, keys, extra, add ) { + add = ( typeof add === "boolean" ) ? add : extra; + var shift = ( typeof element === "string" || element === null ), + options = { + extra: shift ? keys : extra, + keys: shift ? element : keys, + element: shift ? this.element : element, + add: add + }; + options.element.toggleClass( this._classes( options ), add ); + return this; + }, + + _on: function( suppressDisabledCheck, element, handlers ) { + var delegateElement; + var instance = this; + + // No suppressDisabledCheck flag, shuffle arguments + if ( typeof suppressDisabledCheck !== "boolean" ) { + handlers = element; + element = suppressDisabledCheck; + suppressDisabledCheck = false; + } + + // No element argument, shuffle and use this.element + if ( !handlers ) { + handlers = element; + element = this.element; + delegateElement = this.widget(); + } else { + element = delegateElement = $( element ); + this.bindings = this.bindings.add( element ); + } + + $.each( handlers, function( event, handler ) { + function handlerProxy() { + + // Allow widgets to customize the disabled handling + // - disabled as an array instead of boolean + // - disabled class as method for disabling individual parts + if ( !suppressDisabledCheck && + ( instance.options.disabled === true || + $( this ).hasClass( "ui-state-disabled" ) ) ) { + return; + } + return ( typeof handler === "string" ? instance[ handler ] : handler ) + .apply( instance, arguments ); + } + + // Copy the guid so direct unbinding works + if ( typeof handler !== "string" ) { + handlerProxy.guid = handler.guid = + handler.guid || handlerProxy.guid || $.guid++; + } + + var match = event.match( /^([\w:-]*)\s*(.*)$/ ); + var eventName = match[ 1 ] + instance.eventNamespace; + var selector = match[ 2 ]; + + if ( selector ) { + delegateElement.on( eventName, selector, handlerProxy ); + } else { + element.on( eventName, handlerProxy ); + } + } ); + }, + + _off: function( element, eventName ) { + eventName = ( eventName || "" ).split( " " ).join( this.eventNamespace + " " ) + + this.eventNamespace; + element.off( eventName ).off( eventName ); + + // Clear the stack to avoid memory leaks (#10056) + this.bindings = $( this.bindings.not( element ).get() ); + this.focusable = $( this.focusable.not( element ).get() ); + this.hoverable = $( this.hoverable.not( element ).get() ); + }, + + _delay: function( handler, delay ) { + function handlerProxy() { + return ( typeof handler === "string" ? instance[ handler ] : handler ) + .apply( instance, arguments ); + } + var instance = this; + return setTimeout( handlerProxy, delay || 0 ); + }, + + _hoverable: function( element ) { + this.hoverable = this.hoverable.add( element ); + this._on( element, { + mouseenter: function( event ) { + this._addClass( $( event.currentTarget ), null, "ui-state-hover" ); + }, + mouseleave: function( event ) { + this._removeClass( $( event.currentTarget ), null, "ui-state-hover" ); + } + } ); + }, + + _focusable: function( element ) { + this.focusable = this.focusable.add( element ); + this._on( element, { + focusin: function( event ) { + this._addClass( $( event.currentTarget ), null, "ui-state-focus" ); + }, + focusout: function( event ) { + this._removeClass( $( event.currentTarget ), null, "ui-state-focus" ); + } + } ); + }, + + _trigger: function( type, event, data ) { + var prop, orig; + var callback = this.options[ type ]; + + data = data || {}; + event = $.Event( event ); + event.type = ( type === this.widgetEventPrefix ? + type : + this.widgetEventPrefix + type ).toLowerCase(); + + // The original event may come from any element + // so we need to reset the target on the new event + event.target = this.element[ 0 ]; + + // Copy original event properties over to the new event + orig = event.originalEvent; + if ( orig ) { + for ( prop in orig ) { + if ( !( prop in event ) ) { + event[ prop ] = orig[ prop ]; + } + } + } + + this.element.trigger( event, data ); + return !( $.isFunction( callback ) && + callback.apply( this.element[ 0 ], [ event ].concat( data ) ) === false || + event.isDefaultPrevented() ); + } +}; + +$.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) { + $.Widget.prototype[ "_" + method ] = function( element, options, callback ) { + if ( typeof options === "string" ) { + options = { effect: options }; + } + + var hasOptions; + var effectName = !options ? + method : + options === true || typeof options === "number" ? + defaultEffect : + options.effect || defaultEffect; + + options = options || {}; + if ( typeof options === "number" ) { + options = { duration: options }; + } + + hasOptions = !$.isEmptyObject( options ); + options.complete = callback; + + if ( options.delay ) { + element.delay( options.delay ); + } + + if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) { + element[ method ]( options ); + } else if ( effectName !== method && element[ effectName ] ) { + element[ effectName ]( options.duration, options.easing, callback ); + } else { + element.queue( function( next ) { + $( this )[ method ](); + if ( callback ) { + callback.call( element[ 0 ] ); + } + next(); + } ); + } + }; +} ); + +var widget = $.widget; + + +/*! + * jQuery UI Position 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/position/ + */ + +//>>label: Position +//>>group: Core +//>>description: Positions elements relative to other elements. +//>>docs: http://api.jqueryui.com/position/ +//>>demos: http://jqueryui.com/position/ + + +( function() { +var cachedScrollbarWidth, + max = Math.max, + abs = Math.abs, + rhorizontal = /left|center|right/, + rvertical = /top|center|bottom/, + roffset = /[\+\-]\d+(\.[\d]+)?%?/, + rposition = /^\w+/, + rpercent = /%$/, + _position = $.fn.position; + +function getOffsets( offsets, width, height ) { + return [ + parseFloat( offsets[ 0 ] ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ), + parseFloat( offsets[ 1 ] ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 ) + ]; +} + +function parseCss( element, property ) { + return parseInt( $.css( element, property ), 10 ) || 0; +} + +function getDimensions( elem ) { + var raw = elem[ 0 ]; + if ( raw.nodeType === 9 ) { + return { + width: elem.width(), + height: elem.height(), + offset: { top: 0, left: 0 } + }; + } + if ( $.isWindow( raw ) ) { + return { + width: elem.width(), + height: elem.height(), + offset: { top: elem.scrollTop(), left: elem.scrollLeft() } + }; + } + if ( raw.preventDefault ) { + return { + width: 0, + height: 0, + offset: { top: raw.pageY, left: raw.pageX } + }; + } + return { + width: elem.outerWidth(), + height: elem.outerHeight(), + offset: elem.offset() + }; +} + +$.position = { + scrollbarWidth: function() { + if ( cachedScrollbarWidth !== undefined ) { + return cachedScrollbarWidth; + } + var w1, w2, + div = $( "<div " + + "style='display:block;position:absolute;width:50px;height:50px;overflow:hidden;'>" + + "<div style='height:100px;width:auto;'></div></div>" ), + innerDiv = div.children()[ 0 ]; + + $( "body" ).append( div ); + w1 = innerDiv.offsetWidth; + div.css( "overflow", "scroll" ); + + w2 = innerDiv.offsetWidth; + + if ( w1 === w2 ) { + w2 = div[ 0 ].clientWidth; + } + + div.remove(); + + return ( cachedScrollbarWidth = w1 - w2 ); + }, + getScrollInfo: function( within ) { + var overflowX = within.isWindow || within.isDocument ? "" : + within.element.css( "overflow-x" ), + overflowY = within.isWindow || within.isDocument ? "" : + within.element.css( "overflow-y" ), + hasOverflowX = overflowX === "scroll" || + ( overflowX === "auto" && within.width < within.element[ 0 ].scrollWidth ), + hasOverflowY = overflowY === "scroll" || + ( overflowY === "auto" && within.height < within.element[ 0 ].scrollHeight ); + return { + width: hasOverflowY ? $.position.scrollbarWidth() : 0, + height: hasOverflowX ? $.position.scrollbarWidth() : 0 + }; + }, + getWithinInfo: function( element ) { + var withinElement = $( element || window ), + isWindow = $.isWindow( withinElement[ 0 ] ), + isDocument = !!withinElement[ 0 ] && withinElement[ 0 ].nodeType === 9, + hasOffset = !isWindow && !isDocument; + return { + element: withinElement, + isWindow: isWindow, + isDocument: isDocument, + offset: hasOffset ? $( element ).offset() : { left: 0, top: 0 }, + scrollLeft: withinElement.scrollLeft(), + scrollTop: withinElement.scrollTop(), + width: withinElement.outerWidth(), + height: withinElement.outerHeight() + }; + } +}; + +$.fn.position = function( options ) { + if ( !options || !options.of ) { + return _position.apply( this, arguments ); + } + + // Make a copy, we don't want to modify arguments + options = $.extend( {}, options ); + + var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions, + target = $( options.of ), + within = $.position.getWithinInfo( options.within ), + scrollInfo = $.position.getScrollInfo( within ), + collision = ( options.collision || "flip" ).split( " " ), + offsets = {}; + + dimensions = getDimensions( target ); + if ( target[ 0 ].preventDefault ) { + + // Force left top to allow flipping + options.at = "left top"; + } + targetWidth = dimensions.width; + targetHeight = dimensions.height; + targetOffset = dimensions.offset; + + // Clone to reuse original targetOffset later + basePosition = $.extend( {}, targetOffset ); + + // Force my and at to have valid horizontal and vertical positions + // if a value is missing or invalid, it will be converted to center + $.each( [ "my", "at" ], function() { + var pos = ( options[ this ] || "" ).split( " " ), + horizontalOffset, + verticalOffset; + + if ( pos.length === 1 ) { + pos = rhorizontal.test( pos[ 0 ] ) ? + pos.concat( [ "center" ] ) : + rvertical.test( pos[ 0 ] ) ? + [ "center" ].concat( pos ) : + [ "center", "center" ]; + } + pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center"; + pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center"; + + // Calculate offsets + horizontalOffset = roffset.exec( pos[ 0 ] ); + verticalOffset = roffset.exec( pos[ 1 ] ); + offsets[ this ] = [ + horizontalOffset ? horizontalOffset[ 0 ] : 0, + verticalOffset ? verticalOffset[ 0 ] : 0 + ]; + + // Reduce to just the positions without the offsets + options[ this ] = [ + rposition.exec( pos[ 0 ] )[ 0 ], + rposition.exec( pos[ 1 ] )[ 0 ] + ]; + } ); + + // Normalize collision option + if ( collision.length === 1 ) { + collision[ 1 ] = collision[ 0 ]; + } + + if ( options.at[ 0 ] === "right" ) { + basePosition.left += targetWidth; + } else if ( options.at[ 0 ] === "center" ) { + basePosition.left += targetWidth / 2; + } + + if ( options.at[ 1 ] === "bottom" ) { + basePosition.top += targetHeight; + } else if ( options.at[ 1 ] === "center" ) { + basePosition.top += targetHeight / 2; + } + + atOffset = getOffsets( offsets.at, targetWidth, targetHeight ); + basePosition.left += atOffset[ 0 ]; + basePosition.top += atOffset[ 1 ]; + + return this.each( function() { + var collisionPosition, using, + elem = $( this ), + elemWidth = elem.outerWidth(), + elemHeight = elem.outerHeight(), + marginLeft = parseCss( this, "marginLeft" ), + marginTop = parseCss( this, "marginTop" ), + collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) + + scrollInfo.width, + collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) + + scrollInfo.height, + position = $.extend( {}, basePosition ), + myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() ); + + if ( options.my[ 0 ] === "right" ) { + position.left -= elemWidth; + } else if ( options.my[ 0 ] === "center" ) { + position.left -= elemWidth / 2; + } + + if ( options.my[ 1 ] === "bottom" ) { + position.top -= elemHeight; + } else if ( options.my[ 1 ] === "center" ) { + position.top -= elemHeight / 2; + } + + position.left += myOffset[ 0 ]; + position.top += myOffset[ 1 ]; + + collisionPosition = { + marginLeft: marginLeft, + marginTop: marginTop + }; + + $.each( [ "left", "top" ], function( i, dir ) { + if ( $.ui.position[ collision[ i ] ] ) { + $.ui.position[ collision[ i ] ][ dir ]( position, { + targetWidth: targetWidth, + targetHeight: targetHeight, + elemWidth: elemWidth, + elemHeight: elemHeight, + collisionPosition: collisionPosition, + collisionWidth: collisionWidth, + collisionHeight: collisionHeight, + offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ], + my: options.my, + at: options.at, + within: within, + elem: elem + } ); + } + } ); + + if ( options.using ) { + + // Adds feedback as second argument to using callback, if present + using = function( props ) { + var left = targetOffset.left - position.left, + right = left + targetWidth - elemWidth, + top = targetOffset.top - position.top, + bottom = top + targetHeight - elemHeight, + feedback = { + target: { + element: target, + left: targetOffset.left, + top: targetOffset.top, + width: targetWidth, + height: targetHeight + }, + element: { + element: elem, + left: position.left, + top: position.top, + width: elemWidth, + height: elemHeight + }, + horizontal: right < 0 ? "left" : left > 0 ? "right" : "center", + vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle" + }; + if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) { + feedback.horizontal = "center"; + } + if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) { + feedback.vertical = "middle"; + } + if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) { + feedback.important = "horizontal"; + } else { + feedback.important = "vertical"; + } + options.using.call( this, props, feedback ); + }; + } + + elem.offset( $.extend( position, { using: using } ) ); + } ); +}; + +$.ui.position = { + fit: { + left: function( position, data ) { + var within = data.within, + withinOffset = within.isWindow ? within.scrollLeft : within.offset.left, + outerWidth = within.width, + collisionPosLeft = position.left - data.collisionPosition.marginLeft, + overLeft = withinOffset - collisionPosLeft, + overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset, + newOverRight; + + // Element is wider than within + if ( data.collisionWidth > outerWidth ) { + + // Element is initially over the left side of within + if ( overLeft > 0 && overRight <= 0 ) { + newOverRight = position.left + overLeft + data.collisionWidth - outerWidth - + withinOffset; + position.left += overLeft - newOverRight; + + // Element is initially over right side of within + } else if ( overRight > 0 && overLeft <= 0 ) { + position.left = withinOffset; + + // Element is initially over both left and right sides of within + } else { + if ( overLeft > overRight ) { + position.left = withinOffset + outerWidth - data.collisionWidth; + } else { + position.left = withinOffset; + } + } + + // Too far left -> align with left edge + } else if ( overLeft > 0 ) { + position.left += overLeft; + + // Too far right -> align with right edge + } else if ( overRight > 0 ) { + position.left -= overRight; + + // Adjust based on position and margin + } else { + position.left = max( position.left - collisionPosLeft, position.left ); + } + }, + top: function( position, data ) { + var within = data.within, + withinOffset = within.isWindow ? within.scrollTop : within.offset.top, + outerHeight = data.within.height, + collisionPosTop = position.top - data.collisionPosition.marginTop, + overTop = withinOffset - collisionPosTop, + overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset, + newOverBottom; + + // Element is taller than within + if ( data.collisionHeight > outerHeight ) { + + // Element is initially over the top of within + if ( overTop > 0 && overBottom <= 0 ) { + newOverBottom = position.top + overTop + data.collisionHeight - outerHeight - + withinOffset; + position.top += overTop - newOverBottom; + + // Element is initially over bottom of within + } else if ( overBottom > 0 && overTop <= 0 ) { + position.top = withinOffset; + + // Element is initially over both top and bottom of within + } else { + if ( overTop > overBottom ) { + position.top = withinOffset + outerHeight - data.collisionHeight; + } else { + position.top = withinOffset; + } + } + + // Too far up -> align with top + } else if ( overTop > 0 ) { + position.top += overTop; + + // Too far down -> align with bottom edge + } else if ( overBottom > 0 ) { + position.top -= overBottom; + + // Adjust based on position and margin + } else { + position.top = max( position.top - collisionPosTop, position.top ); + } + } + }, + flip: { + left: function( position, data ) { + var within = data.within, + withinOffset = within.offset.left + within.scrollLeft, + outerWidth = within.width, + offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left, + collisionPosLeft = position.left - data.collisionPosition.marginLeft, + overLeft = collisionPosLeft - offsetLeft, + overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft, + myOffset = data.my[ 0 ] === "left" ? + -data.elemWidth : + data.my[ 0 ] === "right" ? + data.elemWidth : + 0, + atOffset = data.at[ 0 ] === "left" ? + data.targetWidth : + data.at[ 0 ] === "right" ? + -data.targetWidth : + 0, + offset = -2 * data.offset[ 0 ], + newOverRight, + newOverLeft; + + if ( overLeft < 0 ) { + newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth - + outerWidth - withinOffset; + if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) { + position.left += myOffset + atOffset + offset; + } + } else if ( overRight > 0 ) { + newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset + + atOffset + offset - offsetLeft; + if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) { + position.left += myOffset + atOffset + offset; + } + } + }, + top: function( position, data ) { + var within = data.within, + withinOffset = within.offset.top + within.scrollTop, + outerHeight = within.height, + offsetTop = within.isWindow ? within.scrollTop : within.offset.top, + collisionPosTop = position.top - data.collisionPosition.marginTop, + overTop = collisionPosTop - offsetTop, + overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop, + top = data.my[ 1 ] === "top", + myOffset = top ? + -data.elemHeight : + data.my[ 1 ] === "bottom" ? + data.elemHeight : + 0, + atOffset = data.at[ 1 ] === "top" ? + data.targetHeight : + data.at[ 1 ] === "bottom" ? + -data.targetHeight : + 0, + offset = -2 * data.offset[ 1 ], + newOverTop, + newOverBottom; + if ( overTop < 0 ) { + newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight - + outerHeight - withinOffset; + if ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) { + position.top += myOffset + atOffset + offset; + } + } else if ( overBottom > 0 ) { + newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset + + offset - offsetTop; + if ( newOverTop > 0 || abs( newOverTop ) < overBottom ) { + position.top += myOffset + atOffset + offset; + } + } + } + }, + flipfit: { + left: function() { + $.ui.position.flip.left.apply( this, arguments ); + $.ui.position.fit.left.apply( this, arguments ); + }, + top: function() { + $.ui.position.flip.top.apply( this, arguments ); + $.ui.position.fit.top.apply( this, arguments ); + } + } +}; + +} )(); + +var position = $.ui.position; + + +/*! + * jQuery UI Keycode 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Keycode +//>>group: Core +//>>description: Provide keycodes as keynames +//>>docs: http://api.jqueryui.com/jQuery.ui.keyCode/ + + +var keycode = $.ui.keyCode = { + BACKSPACE: 8, + COMMA: 188, + DELETE: 46, + DOWN: 40, + END: 35, + ENTER: 13, + ESCAPE: 27, + HOME: 36, + LEFT: 37, + PAGE_DOWN: 34, + PAGE_UP: 33, + PERIOD: 190, + RIGHT: 39, + SPACE: 32, + TAB: 9, + UP: 38 +}; + + +/*! + * jQuery UI Unique ID 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: uniqueId +//>>group: Core +//>>description: Functions to generate and remove uniqueId's +//>>docs: http://api.jqueryui.com/uniqueId/ + + + +var uniqueId = $.fn.extend( { + uniqueId: ( function() { + var uuid = 0; + + return function() { + return this.each( function() { + if ( !this.id ) { + this.id = "ui-id-" + ( ++uuid ); + } + } ); + }; + } )(), + + removeUniqueId: function() { + return this.each( function() { + if ( /^ui-id-\d+$/.test( this.id ) ) { + $( this ).removeAttr( "id" ); + } + } ); + } +} ); + + + +var safeActiveElement = $.ui.safeActiveElement = function( document ) { + var activeElement; + + // Support: IE 9 only + // IE9 throws an "Unspecified error" accessing document.activeElement from an <iframe> + try { + activeElement = document.activeElement; + } catch ( error ) { + activeElement = document.body; + } + + // Support: IE 9 - 11 only + // IE may return null instead of an element + // Interestingly, this only seems to occur when NOT in an iframe + if ( !activeElement ) { + activeElement = document.body; + } + + // Support: IE 11 only + // IE11 returns a seemingly empty object in some cases when accessing + // document.activeElement from an <iframe> + if ( !activeElement.nodeName ) { + activeElement = document.body; + } + + return activeElement; +}; + + +/*! + * jQuery UI Menu 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Menu +//>>group: Widgets +//>>description: Creates nestable menus. +//>>docs: http://api.jqueryui.com/menu/ +//>>demos: http://jqueryui.com/menu/ +//>>css.structure: ../../themes/base/core.css +//>>css.structure: ../../themes/base/menu.css +//>>css.theme: ../../themes/base/theme.css + + + +var widgetsMenu = $.widget( "ui.menu", { + version: "1.12.1", + defaultElement: "<ul>", + delay: 300, + options: { + icons: { + submenu: "ui-icon-caret-1-e" + }, + items: "> *", + menus: "ul", + position: { + my: "left top", + at: "right top" + }, + role: "menu", + + // Callbacks + blur: null, + focus: null, + select: null + }, + + _create: function() { + this.activeMenu = this.element; + + // Flag used to prevent firing of the click handler + // as the event bubbles up through nested menus + this.mouseHandled = false; + this.element + .uniqueId() + .attr( { + role: this.options.role, + tabIndex: 0 + } ); + + this._addClass( "ui-menu", "ui-widget ui-widget-content" ); + this._on( { + + // Prevent focus from sticking to links inside menu after clicking + // them (focus should always stay on UL during navigation). + "mousedown .ui-menu-item": function( event ) { + event.preventDefault(); + }, + "click .ui-menu-item": function( event ) { + var target = $( event.target ); + var active = $( $.ui.safeActiveElement( this.document[ 0 ] ) ); + if ( !this.mouseHandled && target.not( ".ui-state-disabled" ).length ) { + this.select( event ); + + // Only set the mouseHandled flag if the event will bubble, see #9469. + if ( !event.isPropagationStopped() ) { + this.mouseHandled = true; + } + + // Open submenu on click + if ( target.has( ".ui-menu" ).length ) { + this.expand( event ); + } else if ( !this.element.is( ":focus" ) && + active.closest( ".ui-menu" ).length ) { + + // Redirect focus to the menu + this.element.trigger( "focus", [ true ] ); + + // If the active item is on the top level, let it stay active. + // Otherwise, blur the active item since it is no longer visible. + if ( this.active && this.active.parents( ".ui-menu" ).length === 1 ) { + clearTimeout( this.timer ); + } + } + } + }, + "mouseenter .ui-menu-item": function( event ) { + + // Ignore mouse events while typeahead is active, see #10458. + // Prevents focusing the wrong item when typeahead causes a scroll while the mouse + // is over an item in the menu + if ( this.previousFilter ) { + return; + } + + var actualTarget = $( event.target ).closest( ".ui-menu-item" ), + target = $( event.currentTarget ); + + // Ignore bubbled events on parent items, see #11641 + if ( actualTarget[ 0 ] !== target[ 0 ] ) { + return; + } + + // Remove ui-state-active class from siblings of the newly focused menu item + // to avoid a jump caused by adjacent elements both having a class with a border + this._removeClass( target.siblings().children( ".ui-state-active" ), + null, "ui-state-active" ); + this.focus( event, target ); + }, + mouseleave: "collapseAll", + "mouseleave .ui-menu": "collapseAll", + focus: function( event, keepActiveItem ) { + + // If there's already an active item, keep it active + // If not, activate the first item + var item = this.active || this.element.find( this.options.items ).eq( 0 ); + + if ( !keepActiveItem ) { + this.focus( event, item ); + } + }, + blur: function( event ) { + this._delay( function() { + var notContained = !$.contains( + this.element[ 0 ], + $.ui.safeActiveElement( this.document[ 0 ] ) + ); + if ( notContained ) { + this.collapseAll( event ); + } + } ); + }, + keydown: "_keydown" + } ); + + this.refresh(); + + // Clicks outside of a menu collapse any open menus + this._on( this.document, { + click: function( event ) { + if ( this._closeOnDocumentClick( event ) ) { + this.collapseAll( event ); + } + + // Reset the mouseHandled flag + this.mouseHandled = false; + } + } ); + }, + + _destroy: function() { + var items = this.element.find( ".ui-menu-item" ) + .removeAttr( "role aria-disabled" ), + submenus = items.children( ".ui-menu-item-wrapper" ) + .removeUniqueId() + .removeAttr( "tabIndex role aria-haspopup" ); + + // Destroy (sub)menus + this.element + .removeAttr( "aria-activedescendant" ) + .find( ".ui-menu" ).addBack() + .removeAttr( "role aria-labelledby aria-expanded aria-hidden aria-disabled " + + "tabIndex" ) + .removeUniqueId() + .show(); + + submenus.children().each( function() { + var elem = $( this ); + if ( elem.data( "ui-menu-submenu-caret" ) ) { + elem.remove(); + } + } ); + }, + + _keydown: function( event ) { + var match, prev, character, skip, + preventDefault = true; + + switch ( event.keyCode ) { + case $.ui.keyCode.PAGE_UP: + this.previousPage( event ); + break; + case $.ui.keyCode.PAGE_DOWN: + this.nextPage( event ); + break; + case $.ui.keyCode.HOME: + this._move( "first", "first", event ); + break; + case $.ui.keyCode.END: + this._move( "last", "last", event ); + break; + case $.ui.keyCode.UP: + this.previous( event ); + break; + case $.ui.keyCode.DOWN: + this.next( event ); + break; + case $.ui.keyCode.LEFT: + this.collapse( event ); + break; + case $.ui.keyCode.RIGHT: + if ( this.active && !this.active.is( ".ui-state-disabled" ) ) { + this.expand( event ); + } + break; + case $.ui.keyCode.ENTER: + case $.ui.keyCode.SPACE: + this._activate( event ); + break; + case $.ui.keyCode.ESCAPE: + this.collapse( event ); + break; + default: + preventDefault = false; + prev = this.previousFilter || ""; + skip = false; + + // Support number pad values + character = event.keyCode >= 96 && event.keyCode <= 105 ? + ( event.keyCode - 96 ).toString() : String.fromCharCode( event.keyCode ); + + clearTimeout( this.filterTimer ); + + if ( character === prev ) { + skip = true; + } else { + character = prev + character; + } + + match = this._filterMenuItems( character ); + match = skip && match.index( this.active.next() ) !== -1 ? + this.active.nextAll( ".ui-menu-item" ) : + match; + + // If no matches on the current filter, reset to the last character pressed + // to move down the menu to the first item that starts with that character + if ( !match.length ) { + character = String.fromCharCode( event.keyCode ); + match = this._filterMenuItems( character ); + } + + if ( match.length ) { + this.focus( event, match ); + this.previousFilter = character; + this.filterTimer = this._delay( function() { + delete this.previousFilter; + }, 1000 ); + } else { + delete this.previousFilter; + } + } + + if ( preventDefault ) { + event.preventDefault(); + } + }, + + _activate: function( event ) { + if ( this.active && !this.active.is( ".ui-state-disabled" ) ) { + if ( this.active.children( "[aria-haspopup='true']" ).length ) { + this.expand( event ); + } else { + this.select( event ); + } + } + }, + + refresh: function() { + var menus, items, newSubmenus, newItems, newWrappers, + that = this, + icon = this.options.icons.submenu, + submenus = this.element.find( this.options.menus ); + + this._toggleClass( "ui-menu-icons", null, !!this.element.find( ".ui-icon" ).length ); + + // Initialize nested menus + newSubmenus = submenus.filter( ":not(.ui-menu)" ) + .hide() + .attr( { + role: this.options.role, + "aria-hidden": "true", + "aria-expanded": "false" + } ) + .each( function() { + var menu = $( this ), + item = menu.prev(), + submenuCaret = $( "<span>" ).data( "ui-menu-submenu-caret", true ); + + that._addClass( submenuCaret, "ui-menu-icon", "ui-icon " + icon ); + item + .attr( "aria-haspopup", "true" ) + .prepend( submenuCaret ); + menu.attr( "aria-labelledby", item.attr( "id" ) ); + } ); + + this._addClass( newSubmenus, "ui-menu", "ui-widget ui-widget-content ui-front" ); + + menus = submenus.add( this.element ); + items = menus.find( this.options.items ); + + // Initialize menu-items containing spaces and/or dashes only as dividers + items.not( ".ui-menu-item" ).each( function() { + var item = $( this ); + if ( that._isDivider( item ) ) { + that._addClass( item, "ui-menu-divider", "ui-widget-content" ); + } + } ); + + // Don't refresh list items that are already adapted + newItems = items.not( ".ui-menu-item, .ui-menu-divider" ); + newWrappers = newItems.children() + .not( ".ui-menu" ) + .uniqueId() + .attr( { + tabIndex: -1, + role: this._itemRole() + } ); + this._addClass( newItems, "ui-menu-item" ) + ._addClass( newWrappers, "ui-menu-item-wrapper" ); + + // Add aria-disabled attribute to any disabled menu item + items.filter( ".ui-state-disabled" ).attr( "aria-disabled", "true" ); + + // If the active item has been removed, blur the menu + if ( this.active && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) { + this.blur(); + } + }, + + _itemRole: function() { + return { + menu: "menuitem", + listbox: "option" + }[ this.options.role ]; + }, + + _setOption: function( key, value ) { + if ( key === "icons" ) { + var icons = this.element.find( ".ui-menu-icon" ); + this._removeClass( icons, null, this.options.icons.submenu ) + ._addClass( icons, null, value.submenu ); + } + this._super( key, value ); + }, + + _setOptionDisabled: function( value ) { + this._super( value ); + + this.element.attr( "aria-disabled", String( value ) ); + this._toggleClass( null, "ui-state-disabled", !!value ); + }, + + focus: function( event, item ) { + var nested, focused, activeParent; + this.blur( event, event && event.type === "focus" ); + + this._scrollIntoView( item ); + + this.active = item.first(); + + focused = this.active.children( ".ui-menu-item-wrapper" ); + this._addClass( focused, null, "ui-state-active" ); + + // Only update aria-activedescendant if there's a role + // otherwise we assume focus is managed elsewhere + if ( this.options.role ) { + this.element.attr( "aria-activedescendant", focused.attr( "id" ) ); + } + + // Highlight active parent menu item, if any + activeParent = this.active + .parent() + .closest( ".ui-menu-item" ) + .children( ".ui-menu-item-wrapper" ); + this._addClass( activeParent, null, "ui-state-active" ); + + if ( event && event.type === "keydown" ) { + this._close(); + } else { + this.timer = this._delay( function() { + this._close(); + }, this.delay ); + } + + nested = item.children( ".ui-menu" ); + if ( nested.length && event && ( /^mouse/.test( event.type ) ) ) { + this._startOpening( nested ); + } + this.activeMenu = item.parent(); + + this._trigger( "focus", event, { item: item } ); + }, + + _scrollIntoView: function( item ) { + var borderTop, paddingTop, offset, scroll, elementHeight, itemHeight; + if ( this._hasScroll() ) { + borderTop = parseFloat( $.css( this.activeMenu[ 0 ], "borderTopWidth" ) ) || 0; + paddingTop = parseFloat( $.css( this.activeMenu[ 0 ], "paddingTop" ) ) || 0; + offset = item.offset().top - this.activeMenu.offset().top - borderTop - paddingTop; + scroll = this.activeMenu.scrollTop(); + elementHeight = this.activeMenu.height(); + itemHeight = item.outerHeight(); + + if ( offset < 0 ) { + this.activeMenu.scrollTop( scroll + offset ); + } else if ( offset + itemHeight > elementHeight ) { + this.activeMenu.scrollTop( scroll + offset - elementHeight + itemHeight ); + } + } + }, + + blur: function( event, fromFocus ) { + if ( !fromFocus ) { + clearTimeout( this.timer ); + } + + if ( !this.active ) { + return; + } + + this._removeClass( this.active.children( ".ui-menu-item-wrapper" ), + null, "ui-state-active" ); + + this._trigger( "blur", event, { item: this.active } ); + this.active = null; + }, + + _startOpening: function( submenu ) { + clearTimeout( this.timer ); + + // Don't open if already open fixes a Firefox bug that caused a .5 pixel + // shift in the submenu position when mousing over the caret icon + if ( submenu.attr( "aria-hidden" ) !== "true" ) { + return; + } + + this.timer = this._delay( function() { + this._close(); + this._open( submenu ); + }, this.delay ); + }, + + _open: function( submenu ) { + var position = $.extend( { + of: this.active + }, this.options.position ); + + clearTimeout( this.timer ); + this.element.find( ".ui-menu" ).not( submenu.parents( ".ui-menu" ) ) + .hide() + .attr( "aria-hidden", "true" ); + + submenu + .show() + .removeAttr( "aria-hidden" ) + .attr( "aria-expanded", "true" ) + .position( position ); + }, + + collapseAll: function( event, all ) { + clearTimeout( this.timer ); + this.timer = this._delay( function() { + + // If we were passed an event, look for the submenu that contains the event + var currentMenu = all ? this.element : + $( event && event.target ).closest( this.element.find( ".ui-menu" ) ); + + // If we found no valid submenu ancestor, use the main menu to close all + // sub menus anyway + if ( !currentMenu.length ) { + currentMenu = this.element; + } + + this._close( currentMenu ); + + this.blur( event ); + + // Work around active item staying active after menu is blurred + this._removeClass( currentMenu.find( ".ui-state-active" ), null, "ui-state-active" ); + + this.activeMenu = currentMenu; + }, this.delay ); + }, + + // With no arguments, closes the currently active menu - if nothing is active + // it closes all menus. If passed an argument, it will search for menus BELOW + _close: function( startMenu ) { + if ( !startMenu ) { + startMenu = this.active ? this.active.parent() : this.element; + } + + startMenu.find( ".ui-menu" ) + .hide() + .attr( "aria-hidden", "true" ) + .attr( "aria-expanded", "false" ); + }, + + _closeOnDocumentClick: function( event ) { + return !$( event.target ).closest( ".ui-menu" ).length; + }, + + _isDivider: function( item ) { + + // Match hyphen, em dash, en dash + return !/[^\-\u2014\u2013\s]/.test( item.text() ); + }, + + collapse: function( event ) { + var newItem = this.active && + this.active.parent().closest( ".ui-menu-item", this.element ); + if ( newItem && newItem.length ) { + this._close(); + this.focus( event, newItem ); + } + }, + + expand: function( event ) { + var newItem = this.active && + this.active + .children( ".ui-menu " ) + .find( this.options.items ) + .first(); + + if ( newItem && newItem.length ) { + this._open( newItem.parent() ); + + // Delay so Firefox will not hide activedescendant change in expanding submenu from AT + this._delay( function() { + this.focus( event, newItem ); + } ); + } + }, + + next: function( event ) { + this._move( "next", "first", event ); + }, + + previous: function( event ) { + this._move( "prev", "last", event ); + }, + + isFirstItem: function() { + return this.active && !this.active.prevAll( ".ui-menu-item" ).length; + }, + + isLastItem: function() { + return this.active && !this.active.nextAll( ".ui-menu-item" ).length; + }, + + _move: function( direction, filter, event ) { + var next; + if ( this.active ) { + if ( direction === "first" || direction === "last" ) { + next = this.active + [ direction === "first" ? "prevAll" : "nextAll" ]( ".ui-menu-item" ) + .eq( -1 ); + } else { + next = this.active + [ direction + "All" ]( ".ui-menu-item" ) + .eq( 0 ); + } + } + if ( !next || !next.length || !this.active ) { + next = this.activeMenu.find( this.options.items )[ filter ](); + } + + this.focus( event, next ); + }, + + nextPage: function( event ) { + var item, base, height; + + if ( !this.active ) { + this.next( event ); + return; + } + if ( this.isLastItem() ) { + return; + } + if ( this._hasScroll() ) { + base = this.active.offset().top; + height = this.element.height(); + this.active.nextAll( ".ui-menu-item" ).each( function() { + item = $( this ); + return item.offset().top - base - height < 0; + } ); + + this.focus( event, item ); + } else { + this.focus( event, this.activeMenu.find( this.options.items ) + [ !this.active ? "first" : "last" ]() ); + } + }, + + previousPage: function( event ) { + var item, base, height; + if ( !this.active ) { + this.next( event ); + return; + } + if ( this.isFirstItem() ) { + return; + } + if ( this._hasScroll() ) { + base = this.active.offset().top; + height = this.element.height(); + this.active.prevAll( ".ui-menu-item" ).each( function() { + item = $( this ); + return item.offset().top - base + height > 0; + } ); + + this.focus( event, item ); + } else { + this.focus( event, this.activeMenu.find( this.options.items ).first() ); + } + }, + + _hasScroll: function() { + return this.element.outerHeight() < this.element.prop( "scrollHeight" ); + }, + + select: function( event ) { + + // TODO: It should never be possible to not have an active item at this + // point, but the tests don't trigger mouseenter before click. + this.active = this.active || $( event.target ).closest( ".ui-menu-item" ); + var ui = { item: this.active }; + if ( !this.active.has( ".ui-menu" ).length ) { + this.collapseAll( event, true ); + } + this._trigger( "select", event, ui ); + }, + + _filterMenuItems: function( character ) { + var escapedCharacter = character.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" ), + regex = new RegExp( "^" + escapedCharacter, "i" ); + + return this.activeMenu + .find( this.options.items ) + + // Only match on items, not dividers or other content (#10571) + .filter( ".ui-menu-item" ) + .filter( function() { + return regex.test( + $.trim( $( this ).children( ".ui-menu-item-wrapper" ).text() ) ); + } ); + } +} ); + + +/*! + * jQuery UI Autocomplete 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */ + +//>>label: Autocomplete +//>>group: Widgets +//>>description: Lists suggested words as the user is typing. +//>>docs: http://api.jqueryui.com/autocomplete/ +//>>demos: http://jqueryui.com/autocomplete/ +//>>css.structure: ../../themes/base/core.css +//>>css.structure: ../../themes/base/autocomplete.css +//>>css.theme: ../../themes/base/theme.css + + + +$.widget( "ui.autocomplete", { + version: "1.12.1", + defaultElement: "<input>", + options: { + appendTo: null, + autoFocus: false, + delay: 300, + minLength: 1, + position: { + my: "left top", + at: "left bottom", + collision: "none" + }, + source: null, + + // Callbacks + change: null, + close: null, + focus: null, + open: null, + response: null, + search: null, + select: null + }, + + requestIndex: 0, + pending: 0, + + _create: function() { + + // Some browsers only repeat keydown events, not keypress events, + // so we use the suppressKeyPress flag to determine if we've already + // handled the keydown event. #7269 + // Unfortunately the code for & in keypress is the same as the up arrow, + // so we use the suppressKeyPressRepeat flag to avoid handling keypress + // events when we know the keydown event was used to modify the + // search term. #7799 + var suppressKeyPress, suppressKeyPressRepeat, suppressInput, + nodeName = this.element[ 0 ].nodeName.toLowerCase(), + isTextarea = nodeName === "textarea", + isInput = nodeName === "input"; + + // Textareas are always multi-line + // Inputs are always single-line, even if inside a contentEditable element + // IE also treats inputs as contentEditable + // All other element types are determined by whether or not they're contentEditable + this.isMultiLine = isTextarea || !isInput && this._isContentEditable( this.element ); + + this.valueMethod = this.element[ isTextarea || isInput ? "val" : "text" ]; + this.isNewMenu = true; + + this._addClass( "ui-autocomplete-input" ); + this.element.attr( "autocomplete", "off" ); + + this._on( this.element, { + keydown: function( event ) { + if ( this.element.prop( "readOnly" ) ) { + suppressKeyPress = true; + suppressInput = true; + suppressKeyPressRepeat = true; + return; + } + + suppressKeyPress = false; + suppressInput = false; + suppressKeyPressRepeat = false; + var keyCode = $.ui.keyCode; + switch ( event.keyCode ) { + case keyCode.PAGE_UP: + suppressKeyPress = true; + this._move( "previousPage", event ); + break; + case keyCode.PAGE_DOWN: + suppressKeyPress = true; + this._move( "nextPage", event ); + break; + case keyCode.UP: + suppressKeyPress = true; + this._keyEvent( "previous", event ); + break; + case keyCode.DOWN: + suppressKeyPress = true; + this._keyEvent( "next", event ); + break; + case keyCode.ENTER: + + // when menu is open and has focus + if ( this.menu.active ) { + + // #6055 - Opera still allows the keypress to occur + // which causes forms to submit + suppressKeyPress = true; + event.preventDefault(); + this.menu.select( event ); + } + break; + case keyCode.TAB: + if ( this.menu.active ) { + this.menu.select( event ); + } + break; + case keyCode.ESCAPE: + if ( this.menu.element.is( ":visible" ) ) { + if ( !this.isMultiLine ) { + this._value( this.term ); + } + this.close( event ); + + // Different browsers have different default behavior for escape + // Single press can mean undo or clear + // Double press in IE means clear the whole form + event.preventDefault(); + } + break; + default: + suppressKeyPressRepeat = true; + + // search timeout should be triggered before the input value is changed + this._searchTimeout( event ); + break; + } + }, + keypress: function( event ) { + if ( suppressKeyPress ) { + suppressKeyPress = false; + if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) { + event.preventDefault(); + } + return; + } + if ( suppressKeyPressRepeat ) { + return; + } + + // Replicate some key handlers to allow them to repeat in Firefox and Opera + var keyCode = $.ui.keyCode; + switch ( event.keyCode ) { + case keyCode.PAGE_UP: + this._move( "previousPage", event ); + break; + case keyCode.PAGE_DOWN: + this._move( "nextPage", event ); + break; + case keyCode.UP: + this._keyEvent( "previous", event ); + break; + case keyCode.DOWN: + this._keyEvent( "next", event ); + break; + } + }, + input: function( event ) { + if ( suppressInput ) { + suppressInput = false; + event.preventDefault(); + return; + } + this._searchTimeout( event ); + }, + focus: function() { + this.selectedItem = null; + this.previous = this._value(); + }, + blur: function( event ) { + if ( this.cancelBlur ) { + delete this.cancelBlur; + return; + } + + clearTimeout( this.searching ); + this.close( event ); + this._change( event ); + } + } ); + + this._initSource(); + this.menu = $( "<ul>" ) + .appendTo( this._appendTo() ) + .menu( { + + // disable ARIA support, the live region takes care of that + role: null + } ) + .hide() + .menu( "instance" ); + + this._addClass( this.menu.element, "ui-autocomplete", "ui-front" ); + this._on( this.menu.element, { + mousedown: function( event ) { + + // prevent moving focus out of the text field + event.preventDefault(); + + // IE doesn't prevent moving focus even with event.preventDefault() + // so we set a flag to know when we should ignore the blur event + this.cancelBlur = true; + this._delay( function() { + delete this.cancelBlur; + + // Support: IE 8 only + // Right clicking a menu item or selecting text from the menu items will + // result in focus moving out of the input. However, we've already received + // and ignored the blur event because of the cancelBlur flag set above. So + // we restore focus to ensure that the menu closes properly based on the user's + // next actions. + if ( this.element[ 0 ] !== $.ui.safeActiveElement( this.document[ 0 ] ) ) { + this.element.trigger( "focus" ); + } + } ); + }, + menufocus: function( event, ui ) { + var label, item; + + // support: Firefox + // Prevent accidental activation of menu items in Firefox (#7024 #9118) + if ( this.isNewMenu ) { + this.isNewMenu = false; + if ( event.originalEvent && /^mouse/.test( event.originalEvent.type ) ) { + this.menu.blur(); + + this.document.one( "mousemove", function() { + $( event.target ).trigger( event.originalEvent ); + } ); + + return; + } + } + + item = ui.item.data( "ui-autocomplete-item" ); + if ( false !== this._trigger( "focus", event, { item: item } ) ) { + + // use value to match what will end up in the input, if it was a key event + if ( event.originalEvent && /^key/.test( event.originalEvent.type ) ) { + this._value( item.value ); + } + } + + // Announce the value in the liveRegion + label = ui.item.attr( "aria-label" ) || item.value; + if ( label && $.trim( label ).length ) { + this.liveRegion.children().hide(); + $( "<div>" ).text( label ).appendTo( this.liveRegion ); + } + }, + menuselect: function( event, ui ) { + var item = ui.item.data( "ui-autocomplete-item" ), + previous = this.previous; + + // Only trigger when focus was lost (click on menu) + if ( this.element[ 0 ] !== $.ui.safeActiveElement( this.document[ 0 ] ) ) { + this.element.trigger( "focus" ); + this.previous = previous; + + // #6109 - IE triggers two focus events and the second + // is asynchronous, so we need to reset the previous + // term synchronously and asynchronously :-( + this._delay( function() { + this.previous = previous; + this.selectedItem = item; + } ); + } + + if ( false !== this._trigger( "select", event, { item: item } ) ) { + this._value( item.value ); + } + + // reset the term after the select event + // this allows custom select handling to work properly + this.term = this._value(); + + this.close( event ); + this.selectedItem = item; + } + } ); + + this.liveRegion = $( "<div>", { + role: "status", + "aria-live": "assertive", + "aria-relevant": "additions" + } ) + .appendTo( this.document[ 0 ].body ); + + this._addClass( this.liveRegion, null, "ui-helper-hidden-accessible" ); + + // Turning off autocomplete prevents the browser from remembering the + // value when navigating through history, so we re-enable autocomplete + // if the page is unloaded before the widget is destroyed. #7790 + this._on( this.window, { + beforeunload: function() { + this.element.removeAttr( "autocomplete" ); + } + } ); + }, + + _destroy: function() { + clearTimeout( this.searching ); + this.element.removeAttr( "autocomplete" ); + this.menu.element.remove(); + this.liveRegion.remove(); + }, + + _setOption: function( key, value ) { + this._super( key, value ); + if ( key === "source" ) { + this._initSource(); + } + if ( key === "appendTo" ) { + this.menu.element.appendTo( this._appendTo() ); + } + if ( key === "disabled" && value && this.xhr ) { + this.xhr.abort(); + } + }, + + _isEventTargetInWidget: function( event ) { + var menuElement = this.menu.element[ 0 ]; + + return event.target === this.element[ 0 ] || + event.target === menuElement || + $.contains( menuElement, event.target ); + }, + + _closeOnClickOutside: function( event ) { + if ( !this._isEventTargetInWidget( event ) ) { + this.close(); + } + }, + + _appendTo: function() { + var element = this.options.appendTo; + + if ( element ) { + element = element.jquery || element.nodeType ? + $( element ) : + this.document.find( element ).eq( 0 ); + } + + if ( !element || !element[ 0 ] ) { + element = this.element.closest( ".ui-front, dialog" ); + } + + if ( !element.length ) { + element = this.document[ 0 ].body; + } + + return element; + }, + + _initSource: function() { + var array, url, + that = this; + if ( $.isArray( this.options.source ) ) { + array = this.options.source; + this.source = function( request, response ) { + response( $.ui.autocomplete.filter( array, request.term ) ); + }; + } else if ( typeof this.options.source === "string" ) { + url = this.options.source; + this.source = function( request, response ) { + if ( that.xhr ) { + that.xhr.abort(); + } + that.xhr = $.ajax( { + url: url, + data: request, + dataType: "json", + success: function( data ) { + response( data ); + }, + error: function() { + response( [] ); + } + } ); + }; + } else { + this.source = this.options.source; + } + }, + + _searchTimeout: function( event ) { + clearTimeout( this.searching ); + this.searching = this._delay( function() { + + // Search if the value has changed, or if the user retypes the same value (see #7434) + var equalValues = this.term === this._value(), + menuVisible = this.menu.element.is( ":visible" ), + modifierKey = event.altKey || event.ctrlKey || event.metaKey || event.shiftKey; + + if ( !equalValues || ( equalValues && !menuVisible && !modifierKey ) ) { + this.selectedItem = null; + this.search( null, event ); + } + }, this.options.delay ); + }, + + search: function( value, event ) { + value = value != null ? value : this._value(); + + // Always save the actual value, not the one passed as an argument + this.term = this._value(); + + if ( value.length < this.options.minLength ) { + return this.close( event ); + } + + if ( this._trigger( "search", event ) === false ) { + return; + } + + return this._search( value ); + }, + + _search: function( value ) { + this.pending++; + this._addClass( "ui-autocomplete-loading" ); + this.cancelSearch = false; + + this.source( { term: value }, this._response() ); + }, + + _response: function() { + var index = ++this.requestIndex; + + return $.proxy( function( content ) { + if ( index === this.requestIndex ) { + this.__response( content ); + } + + this.pending--; + if ( !this.pending ) { + this._removeClass( "ui-autocomplete-loading" ); + } + }, this ); + }, + + __response: function( content ) { + if ( content ) { + content = this._normalize( content ); + } + this._trigger( "response", null, { content: content } ); + if ( !this.options.disabled && content && content.length && !this.cancelSearch ) { + this._suggest( content ); + this._trigger( "open" ); + } else { + + // use ._close() instead of .close() so we don't cancel future searches + this._close(); + } + }, + + close: function( event ) { + this.cancelSearch = true; + this._close( event ); + }, + + _close: function( event ) { + + // Remove the handler that closes the menu on outside clicks + this._off( this.document, "mousedown" ); + + if ( this.menu.element.is( ":visible" ) ) { + this.menu.element.hide(); + this.menu.blur(); + this.isNewMenu = true; + this._trigger( "close", event ); + } + }, + + _change: function( event ) { + if ( this.previous !== this._value() ) { + this._trigger( "change", event, { item: this.selectedItem } ); + } + }, + + _normalize: function( items ) { + + // assume all items have the right format when the first item is complete + if ( items.length && items[ 0 ].label && items[ 0 ].value ) { + return items; + } + return $.map( items, function( item ) { + if ( typeof item === "string" ) { + return { + label: item, + value: item + }; + } + return $.extend( {}, item, { + label: item.label || item.value, + value: item.value || item.label + } ); + } ); + }, + + _suggest: function( items ) { + var ul = this.menu.element.empty(); + this._renderMenu( ul, items ); + this.isNewMenu = true; + this.menu.refresh(); + + // Size and position menu + ul.show(); + this._resizeMenu(); + ul.position( $.extend( { + of: this.element + }, this.options.position ) ); + + if ( this.options.autoFocus ) { + this.menu.next(); + } + + // Listen for interactions outside of the widget (#6642) + this._on( this.document, { + mousedown: "_closeOnClickOutside" + } ); + }, + + _resizeMenu: function() { + var ul = this.menu.element; + ul.outerWidth( Math.max( + + // Firefox wraps long text (possibly a rounding bug) + // so we add 1px to avoid the wrapping (#7513) + ul.width( "" ).outerWidth() + 1, + this.element.outerWidth() + ) ); + }, + + _renderMenu: function( ul, items ) { + var that = this; + $.each( items, function( index, item ) { + that._renderItemData( ul, item ); + } ); + }, + + _renderItemData: function( ul, item ) { + return this._renderItem( ul, item ).data( "ui-autocomplete-item", item ); + }, + + _renderItem: function( ul, item ) { + return $( "<li>" ) + .append( $( "<div>" ).text( item.label ) ) + .appendTo( ul ); + }, + + _move: function( direction, event ) { + if ( !this.menu.element.is( ":visible" ) ) { + this.search( null, event ); + return; + } + if ( this.menu.isFirstItem() && /^previous/.test( direction ) || + this.menu.isLastItem() && /^next/.test( direction ) ) { + + if ( !this.isMultiLine ) { + this._value( this.term ); + } + + this.menu.blur(); + return; + } + this.menu[ direction ]( event ); + }, + + widget: function() { + return this.menu.element; + }, + + _value: function() { + return this.valueMethod.apply( this.element, arguments ); + }, + + _keyEvent: function( keyEvent, event ) { + if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) { + this._move( keyEvent, event ); + + // Prevents moving cursor to beginning/end of the text field in some browsers + event.preventDefault(); + } + }, + + // Support: Chrome <=50 + // We should be able to just use this.element.prop( "isContentEditable" ) + // but hidden elements always report false in Chrome. + // https://code.google.com/p/chromium/issues/detail?id=313082 + _isContentEditable: function( element ) { + if ( !element.length ) { + return false; + } + + var editable = element.prop( "contentEditable" ); + + if ( editable === "inherit" ) { + return this._isContentEditable( element.parent() ); + } + + return editable === "true"; + } +} ); + +$.extend( $.ui.autocomplete, { + escapeRegex: function( value ) { + return value.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" ); + }, + filter: function( array, term ) { + var matcher = new RegExp( $.ui.autocomplete.escapeRegex( term ), "i" ); + return $.grep( array, function( value ) { + return matcher.test( value.label || value.value || value ); + } ); + } +} ); + +// Live region extension, adding a `messages` option +// NOTE: This is an experimental API. We are still investigating +// a full solution for string manipulation and internationalization. +$.widget( "ui.autocomplete", $.ui.autocomplete, { + options: { + messages: { + noResults: "No search results.", + results: function( amount ) { + return amount + ( amount > 1 ? " results are" : " result is" ) + + " available, use up and down arrow keys to navigate."; + } + } + }, + + __response: function( content ) { + var message; + this._superApply( arguments ); + if ( this.options.disabled || this.cancelSearch ) { + return; + } + if ( content && content.length ) { + message = this.options.messages.results( content.length ); + } else { + message = this.options.messages.noResults; + } + this.liveRegion.children().hide(); + $( "<div>" ).text( message ).appendTo( this.liveRegion ); + } +} ); + +var widgetsAutocomplete = $.ui.autocomplete; + + + + +}));
\ No newline at end of file diff --git a/plugins/javadoc/src/main/resources/static_res/jquery/jquery-ui.min.css b/plugins/javadoc/src/main/resources/static_res/jquery/jquery-ui.min.css new file mode 100644 index 00000000..65427fba --- /dev/null +++ b/plugins/javadoc/src/main/resources/static_res/jquery/jquery-ui.min.css @@ -0,0 +1,7 @@ +/*! jQuery UI - v1.12.1 - 2018-12-06 +* http://jqueryui.com +* Includes: core.css, autocomplete.css, menu.css, theme.css +* To view and modify this theme, visit http://jqueryui.com/themeroller/?scope=&folderName=custom-theme&bgImgOpacityError=95&bgImgOpacityHighlight=55&bgImgOpacityActive=65&bgImgOpacityHover=75&bgImgOpacityDefault=75&bgImgOpacityContent=75&bgImgOpacityHeader=75&cornerRadiusShadow=8px&offsetLeftShadow=-8px&offsetTopShadow=-8px&thicknessShadow=8px&opacityShadow=30&bgImgOpacityShadow=0&bgTextureShadow=flat&bgColorShadow=%23aaaaaa&opacityOverlay=30&bgImgOpacityOverlay=0&bgTextureOverlay=flat&bgColorOverlay=%23aaaaaa&iconColorError=%23cd0a0a&fcError=%23cd0a0a&borderColorError=%23cd0a0a&bgTextureError=glass&bgColorError=%23fef1ec&iconColorHighlight=%232e83ff&fcHighlight=%23363636&borderColorHighlight=%23fcefa1&bgTextureHighlight=glass&bgColorHighlight=%23fbf9ee&iconColorActive=%23454545&fcActive=%23212121&borderColorActive=%23aaaaaa&bgTextureActive=glass&bgColorActive=%23dadada&iconColorHover=%23454545&fcHover=%23212121&borderColorHover=%23999999&bgTextureHover=glass&bgColorHover=%23dadada&iconColorDefault=%23888888&fcDefault=%23555555&borderColorDefault=%23d3d3d3&bgTextureDefault=glass&bgColorDefault=%23e6e6e6&iconColorContent=%23222222&fcContent=%23222222&borderColorContent=%23aaaaaa&bgTextureContent=flat&bgColorContent=%23ffffff&iconColorHeader=%23222222&fcHeader=%23222222&borderColorHeader=%23aaaaaa&bgTextureHeader=highlight_soft&bgColorHeader=%23cccccc&cornerRadius=4px&fwDefault=normal&fsDefault=1.1em&ffDefault=Verdana%2CArial%2Csans-serif +* Copyright jQuery Foundation and other contributors; Licensed MIT */ + +.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important;pointer-events:none}.ui-icon{display:inline-block;vertical-align:middle;margin-top:-.25em;position:relative;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-icon-block{left:50%;margin-left:-8px;display:block}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-autocomplete{position:absolute;top:0;left:0;cursor:default}.ui-menu{list-style:none;padding:0;margin:0;display:block;outline:0}.ui-menu .ui-menu{position:absolute}.ui-menu .ui-menu-item{margin:0;cursor:pointer;list-style-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")}.ui-menu .ui-menu-item-wrapper{position:relative;padding:3px 1em 3px .4em}.ui-menu .ui-menu-divider{margin:5px 0;height:0;font-size:0;line-height:0;border-width:1px 0 0 0}.ui-menu .ui-state-focus,.ui-menu .ui-state-active{margin:-1px}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item-wrapper{padding-left:2em}.ui-menu .ui-icon{position:absolute;top:0;bottom:0;left:.2em;margin:auto 0}.ui-menu .ui-menu-icon{left:auto;right:0}.ui-widget{font-family:Verdana,Arial,sans-serif;font-size:1.1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Verdana,Arial,sans-serif;font-size:1em}.ui-widget.ui-widget-content{border:1px solid #d3d3d3}.ui-widget-content{border:1px solid #aaa;background:#fff;color:#222}.ui-widget-content a{color:#222}.ui-widget-header{border:1px solid #aaa;background:#ccc url("images/ui-bg_highlight-soft_75_cccccc_1x100.png") 50% 50% repeat-x;color:#222;font-weight:bold} .ui-widget-header a{color:#222} .ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default,.ui-button,html .ui-button.ui-state-disabled:hover,html .ui-button.ui-state-disabled:active{border:1px solid #d3d3d3;background:#e6e6e6 url("images/ui-bg_glass_75_e6e6e6_1x400.png") 50% 50% repeat-x;font-weight:normal;color:#555} .ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited,a.ui-button,a:link.ui-button,a:visited.ui-button,.ui-button{color:#555;text-decoration:none} .ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus,.ui-button:hover,.ui-button:focus{border:1px solid #999;background:#dadada url("images/ui-bg_glass_75_dadada_1x400.png") 50% 50% repeat-x;font-weight:normal;color:#212121} .ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited,.ui-state-focus a,.ui-state-focus a:hover,.ui-state-focus a:link,.ui-state-focus a:visited,a.ui-button:hover,a.ui-button:focus{color:#212121;text-decoration:none} .ui-visual-focus{box-shadow:0 0 3px 1px rgb(94,158,214)} .ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active,a.ui-button:active,.ui-button:active,.ui-button.ui-state-active:hover{border:1px solid #aaa;background:#dadada url("images/ui-bg_glass_65_dadada_1x400.png") 50% 50% repeat-x;font-weight:normal;color:#212121} .ui-icon-background,.ui-state-active .ui-icon-background{border:#aaa;background-color:#212121} .ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#212121;text-decoration:none} .ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #fcefa1;background:#fbf9ee url("images/ui-bg_glass_55_fbf9ee_1x400.png") 50% 50% repeat-x;color:#363636} .ui-state-checked{border:1px solid #fcefa1;background:#fbf9ee} .ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#363636} .ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #cd0a0a;background:#fef1ec url("images/ui-bg_glass_95_fef1ec_1x400.png") 50% 50% repeat-x;color:#cd0a0a} .ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#cd0a0a} .ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#cd0a0a} .ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold} .ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:normal} .ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none} .ui-state-disabled .ui-icon{filter:Alpha(Opacity=35)} .ui-icon{width:16px;height:16px} .ui-icon,.ui-widget-content .ui-icon{background-image:url("images/ui-icons_222222_256x240.png")} .ui-widget-header .ui-icon{background-image:url("images/ui-icons_222222_256x240.png")} .ui-state-hover .ui-icon,.ui-state-focus .ui-icon,.ui-button:hover .ui-icon,.ui-button:focus .ui-icon{background-image:url("images/ui-icons_454545_256x240.png")} .ui-state-active .ui-icon,.ui-button:active .ui-icon{background-image:url("images/ui-icons_454545_256x240.png")} .ui-state-highlight .ui-icon,.ui-button .ui-state-highlight.ui-icon{background-image:url("images/ui-icons_2e83ff_256x240.png")} .ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url("images/ui-icons_cd0a0a_256x240.png")} .ui-button .ui-icon{background-image:url("images/ui-icons_888888_256x240.png")} .ui-icon-blank{background-position:16px 16px} .ui-icon-caret-1-n{background-position:0 0} .ui-icon-caret-1-ne{background-position:-16px 0} .ui-icon-caret-1-e{background-position:-32px 0} .ui-icon-caret-1-se{background-position:-48px 0} .ui-icon-caret-1-s{background-position:-65px 0} .ui-icon-caret-1-sw{background-position:-80px 0} .ui-icon-caret-1-w{background-position:-96px 0} .ui-icon-caret-1-nw{background-position:-112px 0} .ui-icon-caret-2-n-s{background-position:-128px 0} .ui-icon-caret-2-e-w{background-position:-144px 0} .ui-icon-triangle-1-n{background-position:0 -16px} .ui-icon-triangle-1-ne{background-position:-16px -16px} .ui-icon-triangle-1-e{background-position:-32px -16px} .ui-icon-triangle-1-se{background-position:-48px -16px} .ui-icon-triangle-1-s{background-position:-65px -16px} .ui-icon-triangle-1-sw{background-position:-80px -16px} .ui-icon-triangle-1-w{background-position:-96px -16px} .ui-icon-triangle-1-nw{background-position:-112px -16px} .ui-icon-triangle-2-n-s{background-position:-128px -16px} .ui-icon-triangle-2-e-w{background-position:-144px -16px} .ui-icon-arrow-1-n{background-position:0 -32px} .ui-icon-arrow-1-ne{background-position:-16px -32px} .ui-icon-arrow-1-e{background-position:-32px -32px} .ui-icon-arrow-1-se{background-position:-48px -32px} .ui-icon-arrow-1-s{background-position:-65px -32px} .ui-icon-arrow-1-sw{background-position:-80px -32px} .ui-icon-arrow-1-w{background-position:-96px -32px} .ui-icon-arrow-1-nw{background-position:-112px -32px} .ui-icon-arrow-2-n-s{background-position:-128px -32px} .ui-icon-arrow-2-ne-sw{background-position:-144px -32px} .ui-icon-arrow-2-e-w{background-position:-160px -32px} .ui-icon-arrow-2-se-nw{background-position:-176px -32px} .ui-icon-arrowstop-1-n{background-position:-192px -32px} .ui-icon-arrowstop-1-e{background-position:-208px -32px} .ui-icon-arrowstop-1-s{background-position:-224px -32px} .ui-icon-arrowstop-1-w{background-position:-240px -32px} .ui-icon-arrowthick-1-n{background-position:1px -48px} .ui-icon-arrowthick-1-ne{background-position:-16px -48px} .ui-icon-arrowthick-1-e{background-position:-32px -48px} .ui-icon-arrowthick-1-se{background-position:-48px -48px} .ui-icon-arrowthick-1-s{background-position:-64px -48px} .ui-icon-arrowthick-1-sw{background-position:-80px -48px} .ui-icon-arrowthick-1-w{background-position:-96px -48px} .ui-icon-arrowthick-1-nw{background-position:-112px -48px} .ui-icon-arrowthick-2-n-s{background-position:-128px -48px} .ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px} .ui-icon-arrowthick-2-e-w{background-position:-160px -48px} .ui-icon-arrowthick-2-se-nw{background-position:-176px -48px} .ui-icon-arrowthickstop-1-n{background-position:-192px -48px} .ui-icon-arrowthickstop-1-e{background-position:-208px -48px} .ui-icon-arrowthickstop-1-s{background-position:-224px -48px} .ui-icon-arrowthickstop-1-w{background-position:-240px -48px} .ui-icon-arrowreturnthick-1-w{background-position:0 -64px} .ui-icon-arrowreturnthick-1-n{background-position:-16px -64px} .ui-icon-arrowreturnthick-1-e{background-position:-32px -64px} .ui-icon-arrowreturnthick-1-s{background-position:-48px -64px} .ui-icon-arrowreturn-1-w{background-position:-64px -64px} .ui-icon-arrowreturn-1-n{background-position:-80px -64px} .ui-icon-arrowreturn-1-e{background-position:-96px -64px} .ui-icon-arrowreturn-1-s{background-position:-112px -64px} .ui-icon-arrowrefresh-1-w{background-position:-128px -64px} .ui-icon-arrowrefresh-1-n{background-position:-144px -64px} .ui-icon-arrowrefresh-1-e{background-position:-160px -64px} .ui-icon-arrowrefresh-1-s{background-position:-176px -64px} .ui-icon-arrow-4{background-position:0 -80px} .ui-icon-arrow-4-diag{background-position:-16px -80px} .ui-icon-extlink{background-position:-32px -80px} .ui-icon-newwin{background-position:-48px -80px} .ui-icon-refresh{background-position:-64px -80px} .ui-icon-shuffle{background-position:-80px -80px} .ui-icon-transfer-e-w{background-position:-96px -80px} .ui-icon-transferthick-e-w{background-position:-112px -80px} .ui-icon-folder-collapsed{background-position:0 -96px} .ui-icon-folder-open{background-position:-16px -96px} .ui-icon-document{background-position:-32px -96px} .ui-icon-document-b{background-position:-48px -96px} .ui-icon-note{background-position:-64px -96px} .ui-icon-mail-closed{background-position:-80px -96px} .ui-icon-mail-open{background-position:-96px -96px} .ui-icon-suitcase{background-position:-112px -96px} .ui-icon-comment{background-position:-128px -96px} .ui-icon-person{background-position:-144px -96px} .ui-icon-print{background-position:-160px -96px} .ui-icon-trash{background-position:-176px -96px} .ui-icon-locked{background-position:-192px -96px} .ui-icon-unlocked{background-position:-208px -96px} .ui-icon-bookmark{background-position:-224px -96px} .ui-icon-tag{background-position:-240px -96px} .ui-icon-home{background-position:0 -112px} .ui-icon-flag{background-position:-16px -112px} .ui-icon-calendar{background-position:-32px -112px} .ui-icon-cart{background-position:-48px -112px} .ui-icon-pencil{background-position:-64px -112px} .ui-icon-clock{background-position:-80px -112px} .ui-icon-disk{background-position:-96px -112px} .ui-icon-calculator{background-position:-112px -112px} .ui-icon-zoomin{background-position:-128px -112px} .ui-icon-zoomout{background-position:-144px -112px} .ui-icon-search{background-position:-160px -112px} .ui-icon-wrench{background-position:-176px -112px} .ui-icon-gear{background-position:-192px -112px} .ui-icon-heart{background-position:-208px -112px} .ui-icon-star{background-position:-224px -112px} .ui-icon-link{background-position:-240px -112px} .ui-icon-cancel{background-position:0 -128px} .ui-icon-plus{background-position:-16px -128px} .ui-icon-plusthick{background-position:-32px -128px} .ui-icon-minus{background-position:-48px -128px} .ui-icon-minusthick{background-position:-64px -128px} .ui-icon-close{background-position:-80px -128px} .ui-icon-closethick{background-position:-96px -128px} .ui-icon-key{background-position:-112px -128px} .ui-icon-lightbulb{background-position:-128px -128px} .ui-icon-scissors{background-position:-144px -128px} .ui-icon-clipboard{background-position:-160px -128px} .ui-icon-copy{background-position:-176px -128px} .ui-icon-contact{background-position:-192px -128px} .ui-icon-image{background-position:-208px -128px} .ui-icon-video{background-position:-224px -128px} .ui-icon-script{background-position:-240px -128px} .ui-icon-alert{background-position:0 -144px} .ui-icon-info{background-position:-16px -144px} .ui-icon-notice{background-position:-32px -144px} .ui-icon-help{background-position:-48px -144px} .ui-icon-check{background-position:-64px -144px} .ui-icon-bullet{background-position:-80px -144px} .ui-icon-radio-on{background-position:-96px -144px} .ui-icon-radio-off{background-position:-112px -144px} .ui-icon-pin-w{background-position:-128px -144px} .ui-icon-pin-s{background-position:-144px -144px} .ui-icon-play{background-position:0 -160px} .ui-icon-pause{background-position:-16px -160px} .ui-icon-seek-next{background-position:-32px -160px} .ui-icon-seek-prev{background-position:-48px -160px} .ui-icon-seek-end{background-position:-64px -160px} .ui-icon-seek-start{background-position:-80px -160px} .ui-icon-seek-first{background-position:-80px -160px} .ui-icon-stop{background-position:-96px -160px} .ui-icon-eject{background-position:-112px -160px} .ui-icon-volume-off{background-position:-128px -160px} .ui-icon-volume-on{background-position:-144px -160px} .ui-icon-power{background-position:0 -176px} .ui-icon-signal-diag{background-position:-16px -176px} .ui-icon-signal{background-position:-32px -176px} .ui-icon-battery-0{background-position:-48px -176px} .ui-icon-battery-1{background-position:-64px -176px} .ui-icon-battery-2{background-position:-80px -176px} .ui-icon-battery-3{background-position:-96px -176px} .ui-icon-circle-plus{background-position:0 -192px} .ui-icon-circle-minus{background-position:-16px -192px} .ui-icon-circle-close{background-position:-32px -192px} .ui-icon-circle-triangle-e{background-position:-48px -192px} .ui-icon-circle-triangle-s{background-position:-64px -192px} .ui-icon-circle-triangle-w{background-position:-80px -192px} .ui-icon-circle-triangle-n{background-position:-96px -192px} .ui-icon-circle-arrow-e{background-position:-112px -192px} .ui-icon-circle-arrow-s{background-position:-128px -192px} .ui-icon-circle-arrow-w{background-position:-144px -192px} .ui-icon-circle-arrow-n{background-position:-160px -192px} .ui-icon-circle-zoomin{background-position:-176px -192px} .ui-icon-circle-zoomout{background-position:-192px -192px} .ui-icon-circle-check{background-position:-208px -192px} .ui-icon-circlesmall-plus{background-position:0 -208px} .ui-icon-circlesmall-minus{background-position:-16px -208px} .ui-icon-circlesmall-close{background-position:-32px -208px} .ui-icon-squaresmall-plus{background-position:-48px -208px} .ui-icon-squaresmall-minus{background-position:-64px -208px} .ui-icon-squaresmall-close{background-position:-80px -208px} .ui-icon-grip-dotted-vertical{background-position:0 -224px} .ui-icon-grip-dotted-horizontal{background-position:-16px -224px} .ui-icon-grip-solid-vertical{background-position:-32px -224px} .ui-icon-grip-solid-horizontal{background-position:-48px -224px} .ui-icon-gripsmall-diagonal-se{background-position:-64px -224px} .ui-icon-grip-diagonal-se{background-position:-80px -224px} .ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{border-top-left-radius:4px} .ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{border-top-right-radius:4px} .ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{border-bottom-left-radius:4px} .ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{border-bottom-right-radius:4px} .ui-widget-overlay{background:#aaa;opacity:.3;filter:Alpha(Opacity=30)} .ui-widget-shadow{-webkit-box-shadow:-8px -8px 8px #aaa;box-shadow:-8px -8px 8px #aaa}
\ No newline at end of file diff --git a/plugins/javadoc/src/main/resources/static_res/jquery/jquery-ui.min.js b/plugins/javadoc/src/main/resources/static_res/jquery/jquery-ui.min.js new file mode 100644 index 00000000..54d4beb8 --- /dev/null +++ b/plugins/javadoc/src/main/resources/static_res/jquery/jquery-ui.min.js @@ -0,0 +1,6 @@ +/*! jQuery UI - v1.12.1 - 2018-12-06 +* http://jqueryui.com +* Includes: widget.js, position.js, keycode.js, unique-id.js, widgets/autocomplete.js, widgets/menu.js +* Copyright jQuery Foundation and other contributors; Licensed MIT */ + +(function(t){"function"==typeof define&&define.amd?define(["jquery"],t):t(jQuery)})(function(t){t.ui=t.ui||{},t.ui.version="1.12.1";var e=0,i=Array.prototype.slice;t.cleanData=function(e){return function(i){var s,n,o;for(o=0;null!=(n=i[o]);o++)try{s=t._data(n,"events"),s&&s.remove&&t(n).triggerHandler("remove")}catch(a){}e(i)}}(t.cleanData),t.widget=function(e,i,s){var n,o,a,r={},l=e.split(".")[0];e=e.split(".")[1];var h=l+"-"+e;return s||(s=i,i=t.Widget),t.isArray(s)&&(s=t.extend.apply(null,[{}].concat(s))),t.expr[":"][h.toLowerCase()]=function(e){return!!t.data(e,h)},t[l]=t[l]||{},n=t[l][e],o=t[l][e]=function(t,e){return this._createWidget?(arguments.length&&this._createWidget(t,e),void 0):new o(t,e)},t.extend(o,n,{version:s.version,_proto:t.extend({},s),_childConstructors:[]}),a=new i,a.options=t.widget.extend({},a.options),t.each(s,function(e,s){return t.isFunction(s)?(r[e]=function(){function t(){return i.prototype[e].apply(this,arguments)}function n(t){return i.prototype[e].apply(this,t)}return function(){var e,i=this._super,o=this._superApply;return this._super=t,this._superApply=n,e=s.apply(this,arguments),this._super=i,this._superApply=o,e}}(),void 0):(r[e]=s,void 0)}),o.prototype=t.widget.extend(a,{widgetEventPrefix:n?a.widgetEventPrefix||e:e},r,{constructor:o,namespace:l,widgetName:e,widgetFullName:h}),n?(t.each(n._childConstructors,function(e,i){var s=i.prototype;t.widget(s.namespace+"."+s.widgetName,o,i._proto)}),delete n._childConstructors):i._childConstructors.push(o),t.widget.bridge(e,o),o},t.widget.extend=function(e){for(var s,n,o=i.call(arguments,1),a=0,r=o.length;r>a;a++)for(s in o[a])n=o[a][s],o[a].hasOwnProperty(s)&&void 0!==n&&(e[s]=t.isPlainObject(n)?t.isPlainObject(e[s])?t.widget.extend({},e[s],n):t.widget.extend({},n):n);return e},t.widget.bridge=function(e,s){var n=s.prototype.widgetFullName||e;t.fn[e]=function(o){var a="string"==typeof o,r=i.call(arguments,1),l=this;return a?this.length||"instance"!==o?this.each(function(){var i,s=t.data(this,n);return"instance"===o?(l=s,!1):s?t.isFunction(s[o])&&"_"!==o.charAt(0)?(i=s[o].apply(s,r),i!==s&&void 0!==i?(l=i&&i.jquery?l.pushStack(i.get()):i,!1):void 0):t.error("no such method '"+o+"' for "+e+" widget instance"):t.error("cannot call methods on "+e+" prior to initialization; "+"attempted to call method '"+o+"'")}):l=void 0:(r.length&&(o=t.widget.extend.apply(null,[o].concat(r))),this.each(function(){var e=t.data(this,n);e?(e.option(o||{}),e._init&&e._init()):t.data(this,n,new s(o,this))})),l}},t.Widget=function(){},t.Widget._childConstructors=[],t.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"<div>",options:{classes:{},disabled:!1,create:null},_createWidget:function(i,s){s=t(s||this.defaultElement||this)[0],this.element=t(s),this.uuid=e++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=t(),this.hoverable=t(),this.focusable=t(),this.classesElementLookup={},s!==this&&(t.data(s,this.widgetFullName,this),this._on(!0,this.element,{remove:function(t){t.target===s&&this.destroy()}}),this.document=t(s.style?s.ownerDocument:s.document||s),this.window=t(this.document[0].defaultView||this.document[0].parentWindow)),this.options=t.widget.extend({},this.options,this._getCreateOptions(),i),this._create(),this.options.disabled&&this._setOptionDisabled(this.options.disabled),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:function(){return{}},_getCreateEventData:t.noop,_create:t.noop,_init:t.noop,destroy:function(){var e=this;this._destroy(),t.each(this.classesElementLookup,function(t,i){e._removeClass(i,t)}),this.element.off(this.eventNamespace).removeData(this.widgetFullName),this.widget().off(this.eventNamespace).removeAttr("aria-disabled"),this.bindings.off(this.eventNamespace)},_destroy:t.noop,widget:function(){return this.element},option:function(e,i){var s,n,o,a=e;if(0===arguments.length)return t.widget.extend({},this.options);if("string"==typeof e)if(a={},s=e.split("."),e=s.shift(),s.length){for(n=a[e]=t.widget.extend({},this.options[e]),o=0;s.length-1>o;o++)n[s[o]]=n[s[o]]||{},n=n[s[o]];if(e=s.pop(),1===arguments.length)return void 0===n[e]?null:n[e];n[e]=i}else{if(1===arguments.length)return void 0===this.options[e]?null:this.options[e];a[e]=i}return this._setOptions(a),this},_setOptions:function(t){var e;for(e in t)this._setOption(e,t[e]);return this},_setOption:function(t,e){return"classes"===t&&this._setOptionClasses(e),this.options[t]=e,"disabled"===t&&this._setOptionDisabled(e),this},_setOptionClasses:function(e){var i,s,n;for(i in e)n=this.classesElementLookup[i],e[i]!==this.options.classes[i]&&n&&n.length&&(s=t(n.get()),this._removeClass(n,i),s.addClass(this._classes({element:s,keys:i,classes:e,add:!0})))},_setOptionDisabled:function(t){this._toggleClass(this.widget(),this.widgetFullName+"-disabled",null,!!t),t&&(this._removeClass(this.hoverable,null,"ui-state-hover"),this._removeClass(this.focusable,null,"ui-state-focus"))},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_classes:function(e){function i(i,o){var a,r;for(r=0;i.length>r;r++)a=n.classesElementLookup[i[r]]||t(),a=e.add?t(t.unique(a.get().concat(e.element.get()))):t(a.not(e.element).get()),n.classesElementLookup[i[r]]=a,s.push(i[r]),o&&e.classes[i[r]]&&s.push(e.classes[i[r]])}var s=[],n=this;return e=t.extend({element:this.element,classes:this.options.classes||{}},e),this._on(e.element,{remove:"_untrackClassesElement"}),e.keys&&i(e.keys.match(/\S+/g)||[],!0),e.extra&&i(e.extra.match(/\S+/g)||[]),s.join(" ")},_untrackClassesElement:function(e){var i=this;t.each(i.classesElementLookup,function(s,n){-1!==t.inArray(e.target,n)&&(i.classesElementLookup[s]=t(n.not(e.target).get()))})},_removeClass:function(t,e,i){return this._toggleClass(t,e,i,!1)},_addClass:function(t,e,i){return this._toggleClass(t,e,i,!0)},_toggleClass:function(t,e,i,s){s="boolean"==typeof s?s:i;var n="string"==typeof t||null===t,o={extra:n?e:i,keys:n?t:e,element:n?this.element:t,add:s};return o.element.toggleClass(this._classes(o),s),this},_on:function(e,i,s){var n,o=this;"boolean"!=typeof e&&(s=i,i=e,e=!1),s?(i=n=t(i),this.bindings=this.bindings.add(i)):(s=i,i=this.element,n=this.widget()),t.each(s,function(s,a){function r(){return e||o.options.disabled!==!0&&!t(this).hasClass("ui-state-disabled")?("string"==typeof a?o[a]:a).apply(o,arguments):void 0}"string"!=typeof a&&(r.guid=a.guid=a.guid||r.guid||t.guid++);var l=s.match(/^([\w:-]*)\s*(.*)$/),h=l[1]+o.eventNamespace,c=l[2];c?n.on(h,c,r):i.on(h,r)})},_off:function(e,i){i=(i||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,e.off(i).off(i),this.bindings=t(this.bindings.not(e).get()),this.focusable=t(this.focusable.not(e).get()),this.hoverable=t(this.hoverable.not(e).get())},_delay:function(t,e){function i(){return("string"==typeof t?s[t]:t).apply(s,arguments)}var s=this;return setTimeout(i,e||0)},_hoverable:function(e){this.hoverable=this.hoverable.add(e),this._on(e,{mouseenter:function(e){this._addClass(t(e.currentTarget),null,"ui-state-hover")},mouseleave:function(e){this._removeClass(t(e.currentTarget),null,"ui-state-hover")}})},_focusable:function(e){this.focusable=this.focusable.add(e),this._on(e,{focusin:function(e){this._addClass(t(e.currentTarget),null,"ui-state-focus")},focusout:function(e){this._removeClass(t(e.currentTarget),null,"ui-state-focus")}})},_trigger:function(e,i,s){var n,o,a=this.options[e];if(s=s||{},i=t.Event(i),i.type=(e===this.widgetEventPrefix?e:this.widgetEventPrefix+e).toLowerCase(),i.target=this.element[0],o=i.originalEvent)for(n in o)n in i||(i[n]=o[n]);return this.element.trigger(i,s),!(t.isFunction(a)&&a.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},t.each({show:"fadeIn",hide:"fadeOut"},function(e,i){t.Widget.prototype["_"+e]=function(s,n,o){"string"==typeof n&&(n={effect:n});var a,r=n?n===!0||"number"==typeof n?i:n.effect||i:e;n=n||{},"number"==typeof n&&(n={duration:n}),a=!t.isEmptyObject(n),n.complete=o,n.delay&&s.delay(n.delay),a&&t.effects&&t.effects.effect[r]?s[e](n):r!==e&&s[r]?s[r](n.duration,n.easing,o):s.queue(function(i){t(this)[e](),o&&o.call(s[0]),i()})}}),t.widget,function(){function e(t,e,i){return[parseFloat(t[0])*(u.test(t[0])?e/100:1),parseFloat(t[1])*(u.test(t[1])?i/100:1)]}function i(e,i){return parseInt(t.css(e,i),10)||0}function s(e){var i=e[0];return 9===i.nodeType?{width:e.width(),height:e.height(),offset:{top:0,left:0}}:t.isWindow(i)?{width:e.width(),height:e.height(),offset:{top:e.scrollTop(),left:e.scrollLeft()}}:i.preventDefault?{width:0,height:0,offset:{top:i.pageY,left:i.pageX}}:{width:e.outerWidth(),height:e.outerHeight(),offset:e.offset()}}var n,o=Math.max,a=Math.abs,r=/left|center|right/,l=/top|center|bottom/,h=/[\+\-]\d+(\.[\d]+)?%?/,c=/^\w+/,u=/%$/,d=t.fn.position;t.position={scrollbarWidth:function(){if(void 0!==n)return n;var e,i,s=t("<div style='display:block;position:absolute;width:50px;height:50px;overflow:hidden;'><div style='height:100px;width:auto;'></div></div>"),o=s.children()[0];return t("body").append(s),e=o.offsetWidth,s.css("overflow","scroll"),i=o.offsetWidth,e===i&&(i=s[0].clientWidth),s.remove(),n=e-i},getScrollInfo:function(e){var i=e.isWindow||e.isDocument?"":e.element.css("overflow-x"),s=e.isWindow||e.isDocument?"":e.element.css("overflow-y"),n="scroll"===i||"auto"===i&&e.width<e.element[0].scrollWidth,o="scroll"===s||"auto"===s&&e.height<e.element[0].scrollHeight;return{width:o?t.position.scrollbarWidth():0,height:n?t.position.scrollbarWidth():0}},getWithinInfo:function(e){var i=t(e||window),s=t.isWindow(i[0]),n=!!i[0]&&9===i[0].nodeType,o=!s&&!n;return{element:i,isWindow:s,isDocument:n,offset:o?t(e).offset():{left:0,top:0},scrollLeft:i.scrollLeft(),scrollTop:i.scrollTop(),width:i.outerWidth(),height:i.outerHeight()}}},t.fn.position=function(n){if(!n||!n.of)return d.apply(this,arguments);n=t.extend({},n);var u,p,f,g,m,_,v=t(n.of),b=t.position.getWithinInfo(n.within),y=t.position.getScrollInfo(b),w=(n.collision||"flip").split(" "),k={};return _=s(v),v[0].preventDefault&&(n.at="left top"),p=_.width,f=_.height,g=_.offset,m=t.extend({},g),t.each(["my","at"],function(){var t,e,i=(n[this]||"").split(" ");1===i.length&&(i=r.test(i[0])?i.concat(["center"]):l.test(i[0])?["center"].concat(i):["center","center"]),i[0]=r.test(i[0])?i[0]:"center",i[1]=l.test(i[1])?i[1]:"center",t=h.exec(i[0]),e=h.exec(i[1]),k[this]=[t?t[0]:0,e?e[0]:0],n[this]=[c.exec(i[0])[0],c.exec(i[1])[0]]}),1===w.length&&(w[1]=w[0]),"right"===n.at[0]?m.left+=p:"center"===n.at[0]&&(m.left+=p/2),"bottom"===n.at[1]?m.top+=f:"center"===n.at[1]&&(m.top+=f/2),u=e(k.at,p,f),m.left+=u[0],m.top+=u[1],this.each(function(){var s,r,l=t(this),h=l.outerWidth(),c=l.outerHeight(),d=i(this,"marginLeft"),_=i(this,"marginTop"),x=h+d+i(this,"marginRight")+y.width,C=c+_+i(this,"marginBottom")+y.height,D=t.extend({},m),T=e(k.my,l.outerWidth(),l.outerHeight());"right"===n.my[0]?D.left-=h:"center"===n.my[0]&&(D.left-=h/2),"bottom"===n.my[1]?D.top-=c:"center"===n.my[1]&&(D.top-=c/2),D.left+=T[0],D.top+=T[1],s={marginLeft:d,marginTop:_},t.each(["left","top"],function(e,i){t.ui.position[w[e]]&&t.ui.position[w[e]][i](D,{targetWidth:p,targetHeight:f,elemWidth:h,elemHeight:c,collisionPosition:s,collisionWidth:x,collisionHeight:C,offset:[u[0]+T[0],u[1]+T[1]],my:n.my,at:n.at,within:b,elem:l})}),n.using&&(r=function(t){var e=g.left-D.left,i=e+p-h,s=g.top-D.top,r=s+f-c,u={target:{element:v,left:g.left,top:g.top,width:p,height:f},element:{element:l,left:D.left,top:D.top,width:h,height:c},horizontal:0>i?"left":e>0?"right":"center",vertical:0>r?"top":s>0?"bottom":"middle"};h>p&&p>a(e+i)&&(u.horizontal="center"),c>f&&f>a(s+r)&&(u.vertical="middle"),u.important=o(a(e),a(i))>o(a(s),a(r))?"horizontal":"vertical",n.using.call(this,t,u)}),l.offset(t.extend(D,{using:r}))})},t.ui.position={fit:{left:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollLeft:s.offset.left,a=s.width,r=t.left-e.collisionPosition.marginLeft,l=n-r,h=r+e.collisionWidth-a-n;e.collisionWidth>a?l>0&&0>=h?(i=t.left+l+e.collisionWidth-a-n,t.left+=l-i):t.left=h>0&&0>=l?n:l>h?n+a-e.collisionWidth:n:l>0?t.left+=l:h>0?t.left-=h:t.left=o(t.left-r,t.left)},top:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollTop:s.offset.top,a=e.within.height,r=t.top-e.collisionPosition.marginTop,l=n-r,h=r+e.collisionHeight-a-n;e.collisionHeight>a?l>0&&0>=h?(i=t.top+l+e.collisionHeight-a-n,t.top+=l-i):t.top=h>0&&0>=l?n:l>h?n+a-e.collisionHeight:n:l>0?t.top+=l:h>0?t.top-=h:t.top=o(t.top-r,t.top)}},flip:{left:function(t,e){var i,s,n=e.within,o=n.offset.left+n.scrollLeft,r=n.width,l=n.isWindow?n.scrollLeft:n.offset.left,h=t.left-e.collisionPosition.marginLeft,c=h-l,u=h+e.collisionWidth-r-l,d="left"===e.my[0]?-e.elemWidth:"right"===e.my[0]?e.elemWidth:0,p="left"===e.at[0]?e.targetWidth:"right"===e.at[0]?-e.targetWidth:0,f=-2*e.offset[0];0>c?(i=t.left+d+p+f+e.collisionWidth-r-o,(0>i||a(c)>i)&&(t.left+=d+p+f)):u>0&&(s=t.left-e.collisionPosition.marginLeft+d+p+f-l,(s>0||u>a(s))&&(t.left+=d+p+f))},top:function(t,e){var i,s,n=e.within,o=n.offset.top+n.scrollTop,r=n.height,l=n.isWindow?n.scrollTop:n.offset.top,h=t.top-e.collisionPosition.marginTop,c=h-l,u=h+e.collisionHeight-r-l,d="top"===e.my[1],p=d?-e.elemHeight:"bottom"===e.my[1]?e.elemHeight:0,f="top"===e.at[1]?e.targetHeight:"bottom"===e.at[1]?-e.targetHeight:0,g=-2*e.offset[1];0>c?(s=t.top+p+f+g+e.collisionHeight-r-o,(0>s||a(c)>s)&&(t.top+=p+f+g)):u>0&&(i=t.top-e.collisionPosition.marginTop+p+f+g-l,(i>0||u>a(i))&&(t.top+=p+f+g))}},flipfit:{left:function(){t.ui.position.flip.left.apply(this,arguments),t.ui.position.fit.left.apply(this,arguments)},top:function(){t.ui.position.flip.top.apply(this,arguments),t.ui.position.fit.top.apply(this,arguments)}}}}(),t.ui.position,t.ui.keyCode={BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38},t.fn.extend({uniqueId:function(){var t=0;return function(){return this.each(function(){this.id||(this.id="ui-id-"+ ++t)})}}(),removeUniqueId:function(){return this.each(function(){/^ui-id-\d+$/.test(this.id)&&t(this).removeAttr("id")})}}),t.ui.safeActiveElement=function(t){var e;try{e=t.activeElement}catch(i){e=t.body}return e||(e=t.body),e.nodeName||(e=t.body),e},t.widget("ui.menu",{version:"1.12.1",defaultElement:"<ul>",delay:300,options:{icons:{submenu:"ui-icon-caret-1-e"},items:"> *",menus:"ul",position:{my:"left top",at:"right top"},role:"menu",blur:null,focus:null,select:null},_create:function(){this.activeMenu=this.element,this.mouseHandled=!1,this.element.uniqueId().attr({role:this.options.role,tabIndex:0}),this._addClass("ui-menu","ui-widget ui-widget-content"),this._on({"mousedown .ui-menu-item":function(t){t.preventDefault()},"click .ui-menu-item":function(e){var i=t(e.target),s=t(t.ui.safeActiveElement(this.document[0]));!this.mouseHandled&&i.not(".ui-state-disabled").length&&(this.select(e),e.isPropagationStopped()||(this.mouseHandled=!0),i.has(".ui-menu").length?this.expand(e):!this.element.is(":focus")&&s.closest(".ui-menu").length&&(this.element.trigger("focus",[!0]),this.active&&1===this.active.parents(".ui-menu").length&&clearTimeout(this.timer)))},"mouseenter .ui-menu-item":function(e){if(!this.previousFilter){var i=t(e.target).closest(".ui-menu-item"),s=t(e.currentTarget);i[0]===s[0]&&(this._removeClass(s.siblings().children(".ui-state-active"),null,"ui-state-active"),this.focus(e,s))}},mouseleave:"collapseAll","mouseleave .ui-menu":"collapseAll",focus:function(t,e){var i=this.active||this.element.find(this.options.items).eq(0);e||this.focus(t,i)},blur:function(e){this._delay(function(){var i=!t.contains(this.element[0],t.ui.safeActiveElement(this.document[0]));i&&this.collapseAll(e)})},keydown:"_keydown"}),this.refresh(),this._on(this.document,{click:function(t){this._closeOnDocumentClick(t)&&this.collapseAll(t),this.mouseHandled=!1}})},_destroy:function(){var e=this.element.find(".ui-menu-item").removeAttr("role aria-disabled"),i=e.children(".ui-menu-item-wrapper").removeUniqueId().removeAttr("tabIndex role aria-haspopup");this.element.removeAttr("aria-activedescendant").find(".ui-menu").addBack().removeAttr("role aria-labelledby aria-expanded aria-hidden aria-disabled tabIndex").removeUniqueId().show(),i.children().each(function(){var e=t(this);e.data("ui-menu-submenu-caret")&&e.remove()})},_keydown:function(e){var i,s,n,o,a=!0;switch(e.keyCode){case t.ui.keyCode.PAGE_UP:this.previousPage(e);break;case t.ui.keyCode.PAGE_DOWN:this.nextPage(e);break;case t.ui.keyCode.HOME:this._move("first","first",e);break;case t.ui.keyCode.END:this._move("last","last",e);break;case t.ui.keyCode.UP:this.previous(e);break;case t.ui.keyCode.DOWN:this.next(e);break;case t.ui.keyCode.LEFT:this.collapse(e);break;case t.ui.keyCode.RIGHT:this.active&&!this.active.is(".ui-state-disabled")&&this.expand(e);break;case t.ui.keyCode.ENTER:case t.ui.keyCode.SPACE:this._activate(e);break;case t.ui.keyCode.ESCAPE:this.collapse(e);break;default:a=!1,s=this.previousFilter||"",o=!1,n=e.keyCode>=96&&105>=e.keyCode?""+(e.keyCode-96):String.fromCharCode(e.keyCode),clearTimeout(this.filterTimer),n===s?o=!0:n=s+n,i=this._filterMenuItems(n),i=o&&-1!==i.index(this.active.next())?this.active.nextAll(".ui-menu-item"):i,i.length||(n=String.fromCharCode(e.keyCode),i=this._filterMenuItems(n)),i.length?(this.focus(e,i),this.previousFilter=n,this.filterTimer=this._delay(function(){delete this.previousFilter},1e3)):delete this.previousFilter}a&&e.preventDefault()},_activate:function(t){this.active&&!this.active.is(".ui-state-disabled")&&(this.active.children("[aria-haspopup='true']").length?this.expand(t):this.select(t))},refresh:function(){var e,i,s,n,o,a=this,r=this.options.icons.submenu,l=this.element.find(this.options.menus);this._toggleClass("ui-menu-icons",null,!!this.element.find(".ui-icon").length),s=l.filter(":not(.ui-menu)").hide().attr({role:this.options.role,"aria-hidden":"true","aria-expanded":"false"}).each(function(){var e=t(this),i=e.prev(),s=t("<span>").data("ui-menu-submenu-caret",!0);a._addClass(s,"ui-menu-icon","ui-icon "+r),i.attr("aria-haspopup","true").prepend(s),e.attr("aria-labelledby",i.attr("id"))}),this._addClass(s,"ui-menu","ui-widget ui-widget-content ui-front"),e=l.add(this.element),i=e.find(this.options.items),i.not(".ui-menu-item").each(function(){var e=t(this);a._isDivider(e)&&a._addClass(e,"ui-menu-divider","ui-widget-content")}),n=i.not(".ui-menu-item, .ui-menu-divider"),o=n.children().not(".ui-menu").uniqueId().attr({tabIndex:-1,role:this._itemRole()}),this._addClass(n,"ui-menu-item")._addClass(o,"ui-menu-item-wrapper"),i.filter(".ui-state-disabled").attr("aria-disabled","true"),this.active&&!t.contains(this.element[0],this.active[0])&&this.blur()},_itemRole:function(){return{menu:"menuitem",listbox:"option"}[this.options.role]},_setOption:function(t,e){if("icons"===t){var i=this.element.find(".ui-menu-icon");this._removeClass(i,null,this.options.icons.submenu)._addClass(i,null,e.submenu)}this._super(t,e)},_setOptionDisabled:function(t){this._super(t),this.element.attr("aria-disabled",t+""),this._toggleClass(null,"ui-state-disabled",!!t)},focus:function(t,e){var i,s,n;this.blur(t,t&&"focus"===t.type),this._scrollIntoView(e),this.active=e.first(),s=this.active.children(".ui-menu-item-wrapper"),this._addClass(s,null,"ui-state-active"),this.options.role&&this.element.attr("aria-activedescendant",s.attr("id")),n=this.active.parent().closest(".ui-menu-item").children(".ui-menu-item-wrapper"),this._addClass(n,null,"ui-state-active"),t&&"keydown"===t.type?this._close():this.timer=this._delay(function(){this._close()},this.delay),i=e.children(".ui-menu"),i.length&&t&&/^mouse/.test(t.type)&&this._startOpening(i),this.activeMenu=e.parent(),this._trigger("focus",t,{item:e})},_scrollIntoView:function(e){var i,s,n,o,a,r;this._hasScroll()&&(i=parseFloat(t.css(this.activeMenu[0],"borderTopWidth"))||0,s=parseFloat(t.css(this.activeMenu[0],"paddingTop"))||0,n=e.offset().top-this.activeMenu.offset().top-i-s,o=this.activeMenu.scrollTop(),a=this.activeMenu.height(),r=e.outerHeight(),0>n?this.activeMenu.scrollTop(o+n):n+r>a&&this.activeMenu.scrollTop(o+n-a+r))},blur:function(t,e){e||clearTimeout(this.timer),this.active&&(this._removeClass(this.active.children(".ui-menu-item-wrapper"),null,"ui-state-active"),this._trigger("blur",t,{item:this.active}),this.active=null)},_startOpening:function(t){clearTimeout(this.timer),"true"===t.attr("aria-hidden")&&(this.timer=this._delay(function(){this._close(),this._open(t)},this.delay))},_open:function(e){var i=t.extend({of:this.active},this.options.position);clearTimeout(this.timer),this.element.find(".ui-menu").not(e.parents(".ui-menu")).hide().attr("aria-hidden","true"),e.show().removeAttr("aria-hidden").attr("aria-expanded","true").position(i)},collapseAll:function(e,i){clearTimeout(this.timer),this.timer=this._delay(function(){var s=i?this.element:t(e&&e.target).closest(this.element.find(".ui-menu"));s.length||(s=this.element),this._close(s),this.blur(e),this._removeClass(s.find(".ui-state-active"),null,"ui-state-active"),this.activeMenu=s},this.delay)},_close:function(t){t||(t=this.active?this.active.parent():this.element),t.find(".ui-menu").hide().attr("aria-hidden","true").attr("aria-expanded","false")},_closeOnDocumentClick:function(e){return!t(e.target).closest(".ui-menu").length},_isDivider:function(t){return!/[^\-\u2014\u2013\s]/.test(t.text())},collapse:function(t){var e=this.active&&this.active.parent().closest(".ui-menu-item",this.element);e&&e.length&&(this._close(),this.focus(t,e))},expand:function(t){var e=this.active&&this.active.children(".ui-menu ").find(this.options.items).first();e&&e.length&&(this._open(e.parent()),this._delay(function(){this.focus(t,e)}))},next:function(t){this._move("next","first",t)},previous:function(t){this._move("prev","last",t)},isFirstItem:function(){return this.active&&!this.active.prevAll(".ui-menu-item").length},isLastItem:function(){return this.active&&!this.active.nextAll(".ui-menu-item").length},_move:function(t,e,i){var s;this.active&&(s="first"===t||"last"===t?this.active["first"===t?"prevAll":"nextAll"](".ui-menu-item").eq(-1):this.active[t+"All"](".ui-menu-item").eq(0)),s&&s.length&&this.active||(s=this.activeMenu.find(this.options.items)[e]()),this.focus(i,s)},nextPage:function(e){var i,s,n;return this.active?(this.isLastItem()||(this._hasScroll()?(s=this.active.offset().top,n=this.element.height(),this.active.nextAll(".ui-menu-item").each(function(){return i=t(this),0>i.offset().top-s-n}),this.focus(e,i)):this.focus(e,this.activeMenu.find(this.options.items)[this.active?"last":"first"]())),void 0):(this.next(e),void 0)},previousPage:function(e){var i,s,n;return this.active?(this.isFirstItem()||(this._hasScroll()?(s=this.active.offset().top,n=this.element.height(),this.active.prevAll(".ui-menu-item").each(function(){return i=t(this),i.offset().top-s+n>0}),this.focus(e,i)):this.focus(e,this.activeMenu.find(this.options.items).first())),void 0):(this.next(e),void 0)},_hasScroll:function(){return this.element.outerHeight()<this.element.prop("scrollHeight")},select:function(e){this.active=this.active||t(e.target).closest(".ui-menu-item");var i={item:this.active};this.active.has(".ui-menu").length||this.collapseAll(e,!0),this._trigger("select",e,i)},_filterMenuItems:function(e){var i=e.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&"),s=RegExp("^"+i,"i");return this.activeMenu.find(this.options.items).filter(".ui-menu-item").filter(function(){return s.test(t.trim(t(this).children(".ui-menu-item-wrapper").text()))})}}),t.widget("ui.autocomplete",{version:"1.12.1",defaultElement:"<input>",options:{appendTo:null,autoFocus:!1,delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null,change:null,close:null,focus:null,open:null,response:null,search:null,select:null},requestIndex:0,pending:0,_create:function(){var e,i,s,n=this.element[0].nodeName.toLowerCase(),o="textarea"===n,a="input"===n;this.isMultiLine=o||!a&&this._isContentEditable(this.element),this.valueMethod=this.element[o||a?"val":"text"],this.isNewMenu=!0,this._addClass("ui-autocomplete-input"),this.element.attr("autocomplete","off"),this._on(this.element,{keydown:function(n){if(this.element.prop("readOnly"))return e=!0,s=!0,i=!0,void 0;e=!1,s=!1,i=!1;var o=t.ui.keyCode;switch(n.keyCode){case o.PAGE_UP:e=!0,this._move("previousPage",n);break;case o.PAGE_DOWN:e=!0,this._move("nextPage",n);break;case o.UP:e=!0,this._keyEvent("previous",n);break;case o.DOWN:e=!0,this._keyEvent("next",n);break;case o.ENTER:this.menu.active&&(e=!0,n.preventDefault(),this.menu.select(n));break;case o.TAB:this.menu.active&&this.menu.select(n);break;case o.ESCAPE:this.menu.element.is(":visible")&&(this.isMultiLine||this._value(this.term),this.close(n),n.preventDefault());break;default:i=!0,this._searchTimeout(n)}},keypress:function(s){if(e)return e=!1,(!this.isMultiLine||this.menu.element.is(":visible"))&&s.preventDefault(),void 0;if(!i){var n=t.ui.keyCode;switch(s.keyCode){case n.PAGE_UP:this._move("previousPage",s);break;case n.PAGE_DOWN:this._move("nextPage",s);break;case n.UP:this._keyEvent("previous",s);break;case n.DOWN:this._keyEvent("next",s)}}},input:function(t){return s?(s=!1,t.preventDefault(),void 0):(this._searchTimeout(t),void 0)},focus:function(){this.selectedItem=null,this.previous=this._value()},blur:function(t){return this.cancelBlur?(delete this.cancelBlur,void 0):(clearTimeout(this.searching),this.close(t),this._change(t),void 0)}}),this._initSource(),this.menu=t("<ul>").appendTo(this._appendTo()).menu({role:null}).hide().menu("instance"),this._addClass(this.menu.element,"ui-autocomplete","ui-front"),this._on(this.menu.element,{mousedown:function(e){e.preventDefault(),this.cancelBlur=!0,this._delay(function(){delete this.cancelBlur,this.element[0]!==t.ui.safeActiveElement(this.document[0])&&this.element.trigger("focus")})},menufocus:function(e,i){var s,n;return this.isNewMenu&&(this.isNewMenu=!1,e.originalEvent&&/^mouse/.test(e.originalEvent.type))?(this.menu.blur(),this.document.one("mousemove",function(){t(e.target).trigger(e.originalEvent)}),void 0):(n=i.item.data("ui-autocomplete-item"),!1!==this._trigger("focus",e,{item:n})&&e.originalEvent&&/^key/.test(e.originalEvent.type)&&this._value(n.value),s=i.item.attr("aria-label")||n.value,s&&t.trim(s).length&&(this.liveRegion.children().hide(),t("<div>").text(s).appendTo(this.liveRegion)),void 0)},menuselect:function(e,i){var s=i.item.data("ui-autocomplete-item"),n=this.previous;this.element[0]!==t.ui.safeActiveElement(this.document[0])&&(this.element.trigger("focus"),this.previous=n,this._delay(function(){this.previous=n,this.selectedItem=s})),!1!==this._trigger("select",e,{item:s})&&this._value(s.value),this.term=this._value(),this.close(e),this.selectedItem=s}}),this.liveRegion=t("<div>",{role:"status","aria-live":"assertive","aria-relevant":"additions"}).appendTo(this.document[0].body),this._addClass(this.liveRegion,null,"ui-helper-hidden-accessible"),this._on(this.window,{beforeunload:function(){this.element.removeAttr("autocomplete")}})},_destroy:function(){clearTimeout(this.searching),this.element.removeAttr("autocomplete"),this.menu.element.remove(),this.liveRegion.remove()},_setOption:function(t,e){this._super(t,e),"source"===t&&this._initSource(),"appendTo"===t&&this.menu.element.appendTo(this._appendTo()),"disabled"===t&&e&&this.xhr&&this.xhr.abort()},_isEventTargetInWidget:function(e){var i=this.menu.element[0];return e.target===this.element[0]||e.target===i||t.contains(i,e.target)},_closeOnClickOutside:function(t){this._isEventTargetInWidget(t)||this.close()},_appendTo:function(){var e=this.options.appendTo;return e&&(e=e.jquery||e.nodeType?t(e):this.document.find(e).eq(0)),e&&e[0]||(e=this.element.closest(".ui-front, dialog")),e.length||(e=this.document[0].body),e},_initSource:function(){var e,i,s=this;t.isArray(this.options.source)?(e=this.options.source,this.source=function(i,s){s(t.ui.autocomplete.filter(e,i.term))}):"string"==typeof this.options.source?(i=this.options.source,this.source=function(e,n){s.xhr&&s.xhr.abort(),s.xhr=t.ajax({url:i,data:e,dataType:"json",success:function(t){n(t)},error:function(){n([])}})}):this.source=this.options.source},_searchTimeout:function(t){clearTimeout(this.searching),this.searching=this._delay(function(){var e=this.term===this._value(),i=this.menu.element.is(":visible"),s=t.altKey||t.ctrlKey||t.metaKey||t.shiftKey;(!e||e&&!i&&!s)&&(this.selectedItem=null,this.search(null,t))},this.options.delay)},search:function(t,e){return t=null!=t?t:this._value(),this.term=this._value(),t.length<this.options.minLength?this.close(e):this._trigger("search",e)!==!1?this._search(t):void 0},_search:function(t){this.pending++,this._addClass("ui-autocomplete-loading"),this.cancelSearch=!1,this.source({term:t},this._response())},_response:function(){var e=++this.requestIndex;return t.proxy(function(t){e===this.requestIndex&&this.__response(t),this.pending--,this.pending||this._removeClass("ui-autocomplete-loading")},this)},__response:function(t){t&&(t=this._normalize(t)),this._trigger("response",null,{content:t}),!this.options.disabled&&t&&t.length&&!this.cancelSearch?(this._suggest(t),this._trigger("open")):this._close()},close:function(t){this.cancelSearch=!0,this._close(t)},_close:function(t){this._off(this.document,"mousedown"),this.menu.element.is(":visible")&&(this.menu.element.hide(),this.menu.blur(),this.isNewMenu=!0,this._trigger("close",t))},_change:function(t){this.previous!==this._value()&&this._trigger("change",t,{item:this.selectedItem})},_normalize:function(e){return e.length&&e[0].label&&e[0].value?e:t.map(e,function(e){return"string"==typeof e?{label:e,value:e}:t.extend({},e,{label:e.label||e.value,value:e.value||e.label})})},_suggest:function(e){var i=this.menu.element.empty();this._renderMenu(i,e),this.isNewMenu=!0,this.menu.refresh(),i.show(),this._resizeMenu(),i.position(t.extend({of:this.element},this.options.position)),this.options.autoFocus&&this.menu.next(),this._on(this.document,{mousedown:"_closeOnClickOutside"})},_resizeMenu:function(){var t=this.menu.element;t.outerWidth(Math.max(t.width("").outerWidth()+1,this.element.outerWidth()))},_renderMenu:function(e,i){var s=this;t.each(i,function(t,i){s._renderItemData(e,i)})},_renderItemData:function(t,e){return this._renderItem(t,e).data("ui-autocomplete-item",e)},_renderItem:function(e,i){return t("<li>").append(t("<div>").text(i.label)).appendTo(e)},_move:function(t,e){return this.menu.element.is(":visible")?this.menu.isFirstItem()&&/^previous/.test(t)||this.menu.isLastItem()&&/^next/.test(t)?(this.isMultiLine||this._value(this.term),this.menu.blur(),void 0):(this.menu[t](e),void 0):(this.search(null,e),void 0)},widget:function(){return this.menu.element},_value:function(){return this.valueMethod.apply(this.element,arguments)},_keyEvent:function(t,e){(!this.isMultiLine||this.menu.element.is(":visible"))&&(this._move(t,e),e.preventDefault())},_isContentEditable:function(t){if(!t.length)return!1;var e=t.prop("contentEditable");return"inherit"===e?this._isContentEditable(t.parent()):"true"===e}}),t.extend(t.ui.autocomplete,{escapeRegex:function(t){return t.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")},filter:function(e,i){var s=RegExp(t.ui.autocomplete.escapeRegex(i),"i");return t.grep(e,function(t){return s.test(t.label||t.value||t)})}}),t.widget("ui.autocomplete",t.ui.autocomplete,{options:{messages:{noResults:"No search results.",results:function(t){return t+(t>1?" results are":" result is")+" available, use up and down arrow keys to navigate."}}},__response:function(e){var i;this._superApply(arguments),this.options.disabled||this.cancelSearch||(i=e&&e.length?this.options.messages.results(e.length):this.options.messages.noResults,this.liveRegion.children().hide(),t("<div>").text(i).appendTo(this.liveRegion))}}),t.ui.autocomplete});
\ No newline at end of file diff --git a/plugins/javadoc/src/main/resources/static_res/jquery/jquery-ui.structure.css b/plugins/javadoc/src/main/resources/static_res/jquery/jquery-ui.structure.css new file mode 100644 index 00000000..d8c81c2b --- /dev/null +++ b/plugins/javadoc/src/main/resources/static_res/jquery/jquery-ui.structure.css @@ -0,0 +1,156 @@ +/*! + * jQuery UI CSS Framework 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/category/theming/ + */ +/* Layout helpers +----------------------------------*/ +.ui-helper-hidden { + display: none; +} +.ui-helper-hidden-accessible { + border: 0; + clip: rect(0 0 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; +} +.ui-helper-reset { + margin: 0; + padding: 0; + border: 0; + outline: 0; + line-height: 1.3; + text-decoration: none; + font-size: 100%; + list-style: none; +} +.ui-helper-clearfix:before, +.ui-helper-clearfix:after { + content: ""; + display: table; + border-collapse: collapse; +} +.ui-helper-clearfix:after { + clear: both; +} +.ui-helper-zfix { + width: 100%; + height: 100%; + top: 0; + left: 0; + position: absolute; + opacity: 0; + filter:Alpha(Opacity=0); /* support: IE8 */ +} + +.ui-front { + z-index: 100; +} + + +/* Interaction Cues +----------------------------------*/ +.ui-state-disabled { + cursor: default !important; + pointer-events: none; +} + + +/* Icons +----------------------------------*/ +.ui-icon { + display: inline-block; + vertical-align: middle; + margin-top: -.25em; + position: relative; + text-indent: -99999px; + overflow: hidden; + background-repeat: no-repeat; +} + +.ui-widget-icon-block { + left: 50%; + margin-left: -8px; + display: block; +} + +/* Misc visuals +----------------------------------*/ + +/* Overlays */ +.ui-widget-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; +} +.ui-autocomplete { + position: absolute; + top: 0; + left: 0; + cursor: default; +} +.ui-menu { + list-style: none; + padding: 0; + margin: 0; + display: block; + outline: 0; +} +.ui-menu .ui-menu { + position: absolute; +} +.ui-menu .ui-menu-item { + margin: 0; + cursor: pointer; + /* support: IE10, see #8844 */ + list-style-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"); +} +.ui-menu .ui-menu-item-wrapper { + position: relative; + padding: 3px 1em 3px .4em; +} +.ui-menu .ui-menu-divider { + margin: 5px 0; + height: 0; + font-size: 0; + line-height: 0; + border-width: 1px 0 0 0; +} +.ui-menu .ui-state-focus, +.ui-menu .ui-state-active { + margin: -1px; +} + +/* icon support */ +.ui-menu-icons { + position: relative; +} +.ui-menu-icons .ui-menu-item-wrapper { + padding-left: 2em; +} + +/* left-aligned */ +.ui-menu .ui-icon { + position: absolute; + top: 0; + bottom: 0; + left: .2em; + margin: auto 0; +} + +/* right-aligned */ +.ui-menu .ui-menu-icon { + left: auto; + right: 0; +} diff --git a/plugins/javadoc/src/main/resources/static_res/jquery/jquery-ui.structure.min.css b/plugins/javadoc/src/main/resources/static_res/jquery/jquery-ui.structure.min.css new file mode 100644 index 00000000..e8808927 --- /dev/null +++ b/plugins/javadoc/src/main/resources/static_res/jquery/jquery-ui.structure.min.css @@ -0,0 +1,5 @@ +/*! jQuery UI - v1.12.1 - 2018-12-06 +* http://jqueryui.com +* Copyright jQuery Foundation and other contributors; Licensed MIT */ + +.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important;pointer-events:none}.ui-icon{display:inline-block;vertical-align:middle;margin-top:-.25em;position:relative;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-icon-block{left:50%;margin-left:-8px;display:block}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-autocomplete{position:absolute;top:0;left:0;cursor:default}.ui-menu{list-style:none;padding:0;margin:0;display:block;outline:0}.ui-menu .ui-menu{position:absolute}.ui-menu .ui-menu-item{margin:0;cursor:pointer;list-style-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")}.ui-menu .ui-menu-item-wrapper{position:relative;padding:3px 1em 3px .4em}.ui-menu .ui-menu-divider{margin:5px 0;height:0;font-size:0;line-height:0;border-width:1px 0 0 0}.ui-menu .ui-state-focus,.ui-menu .ui-state-active{margin:-1px}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item-wrapper{padding-left:2em}.ui-menu .ui-icon{position:absolute;top:0;bottom:0;left:.2em;margin:auto 0}.ui-menu .ui-menu-icon{left:auto;right:0}
\ No newline at end of file diff --git a/plugins/javadoc/src/main/resources/static_res/resources/glass.png b/plugins/javadoc/src/main/resources/static_res/resources/glass.png Binary files differnew file mode 100644 index 00000000..a7f591f4 --- /dev/null +++ b/plugins/javadoc/src/main/resources/static_res/resources/glass.png diff --git a/plugins/javadoc/src/main/resources/static_res/resources/x.png b/plugins/javadoc/src/main/resources/static_res/resources/x.png Binary files differnew file mode 100644 index 00000000..30548a75 --- /dev/null +++ b/plugins/javadoc/src/main/resources/static_res/resources/x.png diff --git a/plugins/javadoc/src/main/resources/static_res/search.js b/plugins/javadoc/src/main/resources/static_res/search.js new file mode 100644 index 00000000..1608e648 --- /dev/null +++ b/plugins/javadoc/src/main/resources/static_res/search.js @@ -0,0 +1,208 @@ +const constants = { + noResult: { + l: "No results found", + renderable: "No results found" + }, + labels: { + modules: 'Modules', + packages: 'Packages', + types: 'Types', + members: 'Members', + tags: 'SearchTags' + } +} + +//It is super important to have vars here since they are lifter outside the block +//ES6 syntax doesn't provide those feature and therefore will fail when one of those values wouldn't be initialized +//eg. when a request for a given package fails +if(typeof moduleSearchIndex === 'undefined'){ + var moduleSearchIndex; +} +if(typeof packageSearchIndex === 'undefined'){ + var packageSearchIndex; +} +if(typeof typeSearchIndex === 'undefined'){ + var typeSearchIndex; +} +if(typeof memberSearchIndex === 'undefined'){ + var memberSearchIndex; +} +if(typeof tagSearchIndex === 'undefined'){ + var tagSearchIndex; +} + +const clearElementValue = (element) => { + element.val('') +} + +$(function init() { + const search = $("#search") + const reset = $("#reset") + + clearElementValue(search) + reset.on('click', () => { + clearElementValue(search) + search.focus() + }) +}) + +const itemHasResults = (item) => { + return item.l !== constants.noResult +} + +$.widget("custom.catcomplete", $.ui.autocomplete, { + _create: function() { + this._super(); + }, + _renderMenu: function(ul, items) { + const menu = this; + let category + $.each(items, (index, item) => { + const shouldCategoryLabelBeRendered = itemHasResults(item) && item.category !== category + if (shouldCategoryLabelBeRendered) { + ul.append(`<li class="ui-autocomplete-category">${item.category}</li>`); + category = item.category; + } + + const li = menu._renderItemData(ul, item); + if (item.category) { + li.attr("aria-label", `${item.category} : ${item.l}`); + } else { + li.attr("aria-label", item.l); + } + li.attr("class", "resultItem"); + }); + }, + _renderItem: (ul, item) => { + const li = $("<li/>").appendTo(ul); + const div = $("<div/>").appendTo(li); + div.html(item.renderable); + return li; + } +}); + +const highlight = (match) => `<span class="resultHighlight">` + match + `</span>` +const escapeHtml = (str) => str.replace("&", "&").replace("<", "<").replace(">", ">") + +const labelForPackage = (element) => (element.m) ? (element.m + "/" + element.l) : element.l +const labelForNested = (element) => { + var label = "" + if(element.p) label += `${element.p}.` + if(element.l !== element.c && element.c) label += `${element.c}.` + return label + element.l +} +const nestedName = (e) => e.l.substring(e.l.lastIndexOf(".") + 1) + +const renderableFromLabel = (label, regex) => escapeHtml(label).replace(regex, highlight) + +$(() => { + $("#search").catcomplete({ + minLength: 1, + delay: 100, + source: function(request, response) { + const exactRegexp = $.ui.autocomplete.escapeRegex(request.term) + "$" + const exactMatcher = new RegExp("^" + exactRegexp, "i"); + const camelCaseRegexp = ($.ui.autocomplete.escapeRegex(request.term)).split(/(?=[A-Z])/).join("([a-z0-9_$]*?)"); + const camelCaseMatcher = new RegExp("^" + camelCaseRegexp); + const secondaryMatcher = new RegExp($.ui.autocomplete.escapeRegex(request.term), "i"); + + const processWithExactLabel = (dataset, category) => { + const exactOrCamelMatches = [] + const secondaryMatches = [] + + dataset.map(element => { + element.category = category + return element + }).forEach((element) => { + if(exactMatcher.test(element.l)){ + element.renderable = renderableFromLabel(element.l, exactMatcher) + exactOrCamelMatches.push(element) + } else if(camelCaseMatcher.test(element.l)){ + element.renderable = renderableFromLabel(element.l, camelCaseMatcher) + exactOrCamelMatches.push(element) + } else if(secondaryMatcher.test(element.l)){ + element.renderable = renderableFromLabel(element.l, secondaryMatcher) + secondaryMatches.push(element) + } + }) + + return [...exactOrCamelMatches, ...secondaryMatches] + } + + const processPackages = (dataset) => { + const exactOrCamelMatches = [] + const secondaryMatches = [] + + dataset.map(element => { + element.category = constants.labels.packages + return element + }).forEach((element) => { + const label = labelForPackage(element); + if(exactMatcher.test(element.l)){ + element.renderable = renderableFromLabel(element.l, exactMatcher) + exactOrCamelMatches.push(element) + } else if(camelCaseMatcher.test(label)){ + element.renderable = renderableFromLabel(label, camelCaseMatcher) + exactOrCamelMatches.push(element) + } else if(secondaryMatcher.test(label)){ + element.renderable = renderableFromLabel(label, secondaryMatcher) + secondaryMatches.push(element) + } + }) + + return [...exactOrCamelMatches, ...secondaryMatches] + } + + const processNested = (dataset, label) => { + const exactOrCamelMatches = [] + const secondaryMatches = [] + + dataset.map(element => { + element.category = label + return element + }).forEach((element) => { + const label = nestedName(element); + if(exactMatcher.test(label)) { + element.renderable = renderableFromLabel(labelForNested(element), new RegExp(exactRegexp, "i")) + exactOrCamelMatches.push(element) + } else if(camelCaseMatcher.test(label)){ + element.renderable = renderableFromLabel(labelForNested(element), new RegExp(camelCaseRegexp)) + exactOrCamelMatches.push(element) + } else if(secondaryMatcher.test(labelForNested(element))){ + element.renderable = renderableFromLabel(labelForNested(element), secondaryMatcher) + secondaryMatches.push(element) + } + }) + + return [...exactOrCamelMatches, ...secondaryMatches] + } + + const modules = moduleSearchIndex ? processWithExactLabel(moduleSearchIndex, constants.labels.modules) : [] + const packages = packageSearchIndex ? processPackages(packageSearchIndex) : [] + const types = typeSearchIndex ? processNested(typeSearchIndex, constants.labels.types) : [] + const members = memberSearchIndex ? processNested(memberSearchIndex, constants.labels.members) : [] + const tags = tagSearchIndex ? processWithExactLabel(tagSearchIndex, constants.labels.tags) : [] + + const result = [...modules, ...packages, ...types, ...members, ...tags] + return response(result); + }, + response: function(event, ui) { + if (!ui.content.length) { + ui.content.push(constants.noResult); + } else { + $("#search").empty(); + } + }, + autoFocus: true, + position: { + collision: "flip" + }, + select: function(event, ui) { + if (ui.item.l !== constants.noResult.l) { + window.location.href = pathtoroot + ui.item.url; + $("#search").focus(); + } + } + }); +}); + diff --git a/plugins/javadoc/src/main/resources/static_res/stylesheet.css b/plugins/javadoc/src/main/resources/static_res/stylesheet.css new file mode 100644 index 00000000..e46e2825 --- /dev/null +++ b/plugins/javadoc/src/main/resources/static_res/stylesheet.css @@ -0,0 +1,879 @@ +/* + * Javadoc style sheet + */ + +@import url('resources/fonts/dejavu.css'); + +/* + * Styles for individual HTML elements. + * + * These are styles that are specific to individual HTML elements. Changing them affects the style of a particular + * HTML element throughout the page. + */ + +body { + background-color:#ffffff; + color:#353833; + font-family:'DejaVu Sans', Arial, Helvetica, sans-serif; + font-size:14px; + margin:0; + padding:0; + height:100%; + width:100%; +} +iframe { + margin:0; + padding:0; + height:100%; + width:100%; + overflow-y:scroll; + border:none; +} +a:link, a:visited { + text-decoration:none; + color:#4A6782; +} +a[href]:hover, a[href]:focus { + text-decoration:none; + color:#bb7a2a; +} +a[name] { + color:#353833; +} +a[name]:before, a[name]:target, a[id]:before, a[id]:target { + content:""; + display:inline-block; + position:relative; + padding-top:129px; + margin-top:-129px; +} +pre { + font-family:'DejaVu Sans Mono', monospace; + font-size:14px; +} +h1 { + font-size:20px; +} +h2 { + font-size:18px; +} +h3 { + font-size:16px; + font-style:italic; +} +h4 { + font-size:13px; +} +h5 { + font-size:12px; +} +h6 { + font-size:11px; +} +ul { + list-style-type:disc; +} +code, tt { + font-family:'DejaVu Sans Mono', monospace; + font-size:14px; + padding-top:4px; + margin-top:8px; + line-height:1.4em; +} +dt code { + font-family:'DejaVu Sans Mono', monospace; + font-size:14px; + padding-top:4px; +} +table tr td dt code { + font-family:'DejaVu Sans Mono', monospace; + font-size:14px; + vertical-align:top; + padding-top:4px; +} +sup { + font-size:8px; +} +button { + font-family: 'DejaVu Sans', Arial, Helvetica, sans-serif; + font-size: 14px; +} +/* + * Styles for HTML generated by javadoc. + * + * These are style classes that are used by the standard doclet to generate HTML documentation. + */ + +/* + * Styles for document title and copyright. + */ +.clear { + clear:both; + height:0px; + overflow:hidden; +} +.aboutLanguage { + float:right; + padding:0px 21px; + font-size:11px; + z-index:200; + margin-top:-9px; +} +.legalCopy { + margin-left:.5em; +} +.bar a, .bar a:link, .bar a:visited, .bar a:active { + color:#FFFFFF; + text-decoration:none; +} +.bar a:hover, .bar a:focus { + color:#bb7a2a; +} +.tab { + background-color:#0066FF; + color:#ffffff; + padding:8px; + width:5em; + font-weight:bold; +} +/* + * Styles for navigation bar. + */ +.bar { + background-color:#4D7A97; + color:#FFFFFF; + padding:.8em .5em .4em .8em; + height:auto;/*height:1.8em;*/ + font-size:11px; + margin:0; +} +.navPadding { + padding-top: 107px; +} +.fixedNav { + position:fixed; + width:100%; + z-index:999; + background-color:#ffffff; +} +.topNav { + background-color:#4D7A97; + color:#FFFFFF; + float:left; + padding:0; + width:100%; + clear:right; + height:2.8em; + padding-top:10px; + overflow:hidden; + font-size:12px; +} +.bottomNav { + margin-top:10px; + background-color:#4D7A97; + color:#FFFFFF; + float:left; + padding:0; + width:100%; + clear:right; + height:2.8em; + padding-top:10px; + overflow:hidden; + font-size:12px; +} +.subNav { + background-color:#dee3e9; + float:left; + width:100%; + overflow:hidden; + font-size:12px; +} +.subNav div { + clear:left; + float:left; + padding:0 0 5px 6px; + text-transform:uppercase; +} +.subNav .navList { + padding-top:5px; +} +ul.navList, ul.subNavList { + float:left; + margin:0 25px 0 0; + padding:0; +} +ul.navList li{ + list-style:none; + float:left; + padding: 5px 6px; + text-transform:uppercase; +} +ul.navListSearch { + float:right; + margin:0 0 0 0; + padding:0; +} +ul.navListSearch li { + list-style:none; + float:right; + padding: 5px 6px; + text-transform:uppercase; +} +ul.navListSearch li label { + position:relative; + right:-16px; +} +ul.subNavList li { + list-style:none; + float:left; + padding-top:10px; +} +.topNav a:link, .topNav a:active, .topNav a:visited, .bottomNav a:link, .bottomNav a:active, .bottomNav a:visited { + color:#FFFFFF; + text-decoration:none; + text-transform:uppercase; +} +.topNav a:hover, .bottomNav a:hover { + text-decoration:none; + color:#bb7a2a; + text-transform:uppercase; +} +.navBarCell1Rev { + background-color:#F8981D; + color:#253441; + margin: auto 5px; +} +.skipNav { + position:absolute; + top:auto; + left:-9999px; + overflow:hidden; +} +/* + * Styles for page header and footer. + */ +.header, .footer { + clear:both; + margin:0 20px; + padding:5px 0 0 0; +} +.indexNav { + position:relative; + font-size:12px; + background-color:#dee3e9; +} +.indexNav ul { + margin-top:0; + padding:5px; +} +.indexNav ul li { + display:inline; + list-style-type:none; + padding-right:10px; + text-transform:uppercase; +} +.indexNav h1 { + font-size:13px; +} +.title { + color:#2c4557; + margin:10px 0; +} +.subTitle { + margin:5px 0 0 0; +} +.header ul { + margin:0 0 15px 0; + padding:0; +} +.footer ul { + margin:20px 0 5px 0; +} +.header ul li, .footer ul li { + list-style:none; + font-size:13px; +} +/* + * Styles for headings. + */ +div.details ul.blockList ul.blockList ul.blockList li.blockList h4, div.details ul.blockList ul.blockList ul.blockListLast li.blockList h4 { + background-color:#dee3e9; + border:1px solid #d0d9e0; + margin:0 0 6px -8px; + padding:7px 5px; +} +ul.blockList ul.blockList ul.blockList li.blockList h3 { + background-color:#dee3e9; + border:1px solid #d0d9e0; + margin:0 0 6px -8px; + padding:7px 5px; +} +ul.blockList ul.blockList li.blockList h3 { + padding:0; + margin:15px 0; +} +ul.blockList li.blockList h2 { + padding:0px 0 20px 0; +} +/* + * Styles for page layout containers. + */ +.contentContainer, .sourceContainer, .classUseContainer, .serializedFormContainer, .constantValuesContainer, +.allClassesContainer, .allPackagesContainer { + clear:both; + padding:10px 20px; + position:relative; +} +.indexContainer { + margin:10px; + position:relative; + font-size:12px; +} +.indexContainer h2 { + font-size:13px; + padding:0 0 3px 0; +} +.indexContainer ul { + margin:0; + padding:0; +} +.indexContainer ul li { + list-style:none; + padding-top:2px; +} +.contentContainer .description dl dt, .contentContainer .details dl dt, .serializedFormContainer dl dt { + font-size:12px; + font-weight:bold; + margin:10px 0 0 0; + color:#4E4E4E; +} +.contentContainer .description dl dd, .contentContainer .details dl dd, .serializedFormContainer dl dd { + margin:5px 0 10px 0px; + font-size:14px; + font-family:'DejaVu Serif', Georgia, "Times New Roman", Times, serif; +} +.serializedFormContainer dl.nameValue dt { + margin-left:1px; + font-size:1.1em; + display:inline; + font-weight:bold; +} +.serializedFormContainer dl.nameValue dd { + margin:0 0 0 1px; + font-size:1.1em; + display:inline; +} +/* + * Styles for lists. + */ +li.circle { + list-style:circle; +} +ul.horizontal li { + display:inline; + font-size:0.9em; +} +ul.inheritance { + margin:0; + padding:0; +} +ul.inheritance li { + display:inline; + list-style:none; +} +ul.inheritance li ul.inheritance { + margin-left:15px; + padding-left:15px; + padding-top:1px; +} +ul.blockList, ul.blockListLast { + margin:10px 0 10px 0; + padding:0; +} +ul.blockList li.blockList, ul.blockListLast li.blockList { + list-style:none; + margin-bottom:15px; + line-height:1.4; +} +ul.blockList ul.blockList li.blockList, ul.blockList ul.blockListLast li.blockList { + padding:0px 20px 5px 10px; + border:1px solid #ededed; + background-color:#f8f8f8; +} +ul.blockList ul.blockList ul.blockList li.blockList, ul.blockList ul.blockList ul.blockListLast li.blockList { + padding:0 0 5px 8px; + background-color:#ffffff; + border:none; +} +ul.blockList ul.blockList ul.blockList ul.blockList li.blockList { + margin-left:0; + padding-left:0; + padding-bottom:15px; + border:none; +} +ul.blockList ul.blockList ul.blockList ul.blockList li.blockListLast { + list-style:none; + border-bottom:none; + padding-bottom:0; +} +table tr td dl, table tr td dl dt, table tr td dl dd { + margin-top:0; + margin-bottom:1px; +} +/* + * Styles for tables. + */ +.overviewSummary table, .memberSummary table, .typeSummary table, .useSummary table, .constantsSummary table, .deprecatedSummary table, +.requiresSummary table, .packagesSummary table, .providesSummary table, .usesSummary table { + width:100%; + border-spacing:0; + border-left:1px solid #EEE; + border-right:1px solid #EEE; + border-bottom:1px solid #EEE; +} +.overviewSummary table, .memberSummary table, .requiresSummary table, .packagesSummary table, .providesSummary table, .usesSummary table { + padding:0px; +} +.overviewSummary caption, .memberSummary caption, .typeSummary caption, +.useSummary caption, .constantsSummary caption, .deprecatedSummary caption, +.requiresSummary caption, .packagesSummary caption, .providesSummary caption, .usesSummary caption { + position:relative; + text-align:left; + background-repeat:no-repeat; + color:#253441; + font-weight:bold; + clear:none; + overflow:hidden; + padding:0px; + padding-top:10px; + padding-left:1px; + margin:0px; + white-space:pre; +} +.constantsSummary caption a:link, .constantsSummary caption a:visited, +.useSummary caption a:link, .useSummary caption a:visited { + color:#1f389c; +} +.overviewSummary caption a:link, .memberSummary caption a:link, .typeSummary caption a:link, +.deprecatedSummary caption a:link, +.requiresSummary caption a:link, .packagesSummary caption a:link, .providesSummary caption a:link, +.usesSummary caption a:link, +.overviewSummary caption a:hover, .memberSummary caption a:hover, .typeSummary caption a:hover, +.useSummary caption a:hover, .constantsSummary caption a:hover, .deprecatedSummary caption a:hover, +.requiresSummary caption a:hover, .packagesSummary caption a:hover, .providesSummary caption a:hover, +.usesSummary caption a:hover, +.overviewSummary caption a:active, .memberSummary caption a:active, .typeSummary caption a:active, +.useSummary caption a:active, .constantsSummary caption a:active, .deprecatedSummary caption a:active, +.requiresSummary caption a:active, .packagesSummary caption a:active, .providesSummary caption a:active, +.usesSummary caption a:active, +.overviewSummary caption a:visited, .memberSummary caption a:visited, .typeSummary caption a:visited, +.deprecatedSummary caption a:visited, +.requiresSummary caption a:visited, .packagesSummary caption a:visited, .providesSummary caption a:visited, +.usesSummary caption a:visited { + color:#FFFFFF; +} +.overviewSummary caption span, .memberSummary caption span, .typeSummary caption span, +.useSummary caption span, .constantsSummary caption span, .deprecatedSummary caption span, +.requiresSummary caption span, .packagesSummary caption span, .providesSummary caption span, +.usesSummary caption span { + white-space:nowrap; + padding-top:5px; + padding-left:12px; + padding-right:12px; + padding-bottom:7px; + display:inline-block; + float:left; + background-color:#F8981D; + border: none; + height:16px; +} +.overviewSummary .tabEnd, .memberSummary .tabEnd, .typeSummary .tabEnd, +.useSummary .tabEnd, .constantsSummary .tabEnd, .deprecatedSummary .tabEnd, +.requiresSummary .tabEnd, .packagesSummary .tabEnd, .providesSummary .tabEnd, .usesSummary .tabEnd { + display:none; + width:5px; + position:relative; + float:left; + background-color:#F8981D; +} +.overviewSummary [role=tablist] button, .memberSummary [role=tablist] button, +.typeSummary [role=tablist] button, .packagesSummary [role=tablist] button { + border: none; + cursor: pointer; + padding: 5px 12px 7px 12px; + font-weight: bold; + margin-right: 3px; +} +.overviewSummary [role=tablist] .activeTableTab, .memberSummary [role=tablist] .activeTableTab, +.typeSummary [role=tablist] .activeTableTab, .packagesSummary [role=tablist] .activeTableTab { + background: #F8981D; + color: #253441; +} +.overviewSummary [role=tablist] .tableTab, .memberSummary [role=tablist] .tableTab, +.typeSummary [role=tablist] .tableTab, .packagesSummary [role=tablist] .tableTab { + background: #4D7A97; + color: #FFFFFF; +} +.rowColor th, .altColor th { + font-weight:normal; +} +.overviewSummary td, .memberSummary td, .typeSummary td, +.useSummary td, .constantsSummary td, .deprecatedSummary td, +.requiresSummary td, .packagesSummary td, .providesSummary td, .usesSummary td { + text-align:left; + padding:0px 0px 12px 10px; +} +th.colFirst, th.colSecond, th.colLast, th.colConstructorName, th.colDeprecatedItemName, .useSummary th, +.constantsSummary th, .packagesSummary th, td.colFirst, td.colSecond, td.colLast, .useSummary td, +.constantsSummary td { + vertical-align:top; + padding-right:0px; + padding-top:8px; + padding-bottom:3px; +} +th.colFirst, th.colSecond, th.colLast, th.colConstructorName, th.colDeprecatedItemName, .constantsSummary th, +.packagesSummary th { + background:#dee3e9; + text-align:left; + padding:8px 3px 3px 7px; +} +td.colFirst, th.colFirst { + font-size:13px; +} +td.colSecond, th.colSecond, td.colLast, th.colConstructorName, th.colDeprecatedItemName, th.colLast { + font-size:13px; +} +.constantsSummary th, .packagesSummary th { + font-size:13px; +} +.providesSummary th.colFirst, .providesSummary th.colLast, .providesSummary td.colFirst, +.providesSummary td.colLast { + white-space:normal; + font-size:13px; +} +.overviewSummary td.colFirst, .overviewSummary th.colFirst, +.requiresSummary td.colFirst, .requiresSummary th.colFirst, +.packagesSummary td.colFirst, .packagesSummary td.colSecond, .packagesSummary th.colFirst, .packagesSummary th, +.usesSummary td.colFirst, .usesSummary th.colFirst, +.providesSummary td.colFirst, .providesSummary th.colFirst, +.memberSummary td.colFirst, .memberSummary th.colFirst, +.memberSummary td.colSecond, .memberSummary th.colSecond, .memberSummary th.colConstructorName, +.typeSummary td.colFirst, .typeSummary th.colFirst { + vertical-align:top; +} +.packagesSummary th.colLast, .packagesSummary td.colLast { + white-space:normal; +} +td.colFirst a:link, td.colFirst a:visited, +td.colSecond a:link, td.colSecond a:visited, +th.colFirst a:link, th.colFirst a:visited, +th.colSecond a:link, th.colSecond a:visited, +th.colConstructorName a:link, th.colConstructorName a:visited, +th.colDeprecatedItemName a:link, th.colDeprecatedItemName a:visited, +.constantValuesContainer td a:link, .constantValuesContainer td a:visited, +.allClassesContainer td a:link, .allClassesContainer td a:visited, +.allPackagesContainer td a:link, .allPackagesContainer td a:visited { + font-weight:bold; +} +.tableSubHeadingColor { + background-color:#EEEEFF; +} +.altColor, .altColor th { + background-color:#FFFFFF; +} +.rowColor, .rowColor th { + background-color:#EEEEEF; +} +/* + * Styles for contents. + */ +.description pre { + margin-top:0; +} +.deprecatedContent { + margin:0; + padding:10px 0; +} +.docSummary { + padding:0; +} +ul.blockList ul.blockList ul.blockList li.blockList h3 { + font-style:normal; +} +div.block { + font-size:14px; + font-family:'DejaVu Serif', Georgia, "Times New Roman", Times, serif; +} +td.colLast div { + padding-top:0px; +} +td.colLast a { + padding-bottom:3px; +} +/* + * Styles for formatting effect. + */ +.sourceLineNo { + color:green; + padding:0 30px 0 0; +} +h1.hidden { + visibility:hidden; + overflow:hidden; + font-size:10px; +} +.block { + display:block; + margin:3px 10px 2px 0px; + color:#474747; +} +.deprecatedLabel, .descfrmTypeLabel, .implementationLabel, .memberNameLabel, .memberNameLink, +.moduleLabelInPackage, .moduleLabelInType, .overrideSpecifyLabel, .packageLabelInType, +.packageHierarchyLabel, .paramLabel, .returnLabel, .seeLabel, .simpleTagLabel, +.throwsLabel, .typeNameLabel, .typeNameLink, .searchTagLink { + font-weight:bold; +} +.deprecationComment, .emphasizedPhrase, .interfaceName { + font-style:italic; +} +.deprecationBlock { + font-size:14px; + font-family:'DejaVu Serif', Georgia, "Times New Roman", Times, serif; + border-style:solid; + border-width:thin; + border-radius:10px; + padding:10px; + margin-bottom:10px; + margin-right:10px; + display:inline-block; +} +div.block div.deprecationComment, div.block div.block span.emphasizedPhrase, +div.block div.block span.interfaceName { + font-style:normal; +} +div.contentContainer ul.blockList li.blockList h2 { + padding-bottom:0px; +} +/* + * Styles for IFRAME. + */ +.mainContainer { + margin:0 auto; + padding:0; + height:100%; + width:100%; + position:fixed; + top:0; + left:0; +} +.leftContainer { + height:100%; + position:fixed; + width:320px; +} +.leftTop { + position:relative; + float:left; + width:315px; + top:0; + left:0; + height:30%; + border-right:6px solid #ccc; + border-bottom:6px solid #ccc; +} +.leftBottom { + position:relative; + float:left; + width:315px; + bottom:0; + left:0; + height:70%; + border-right:6px solid #ccc; + border-top:1px solid #000; +} +.rightContainer { + position:absolute; + left:320px; + top:0; + bottom:0; + height:100%; + right:0; + border-left:1px solid #000; +} +.rightIframe { + margin:0; + padding:0; + height:100%; + right:30px; + width:100%; + overflow:visible; + margin-bottom:30px; +} +/* + * Styles specific to HTML5 elements. + */ +main, nav, header, footer, section { + display:block; +} +/* + * Styles for javadoc search. + */ +.ui-autocomplete-category { + font-weight:bold; + font-size:15px; + padding:7px 0 7px 3px; + background-color:#4D7A97; + color:#FFFFFF; +} +.resultItem { + font-size:13px; +} +.ui-autocomplete { + max-height:85%; + max-width:65%; + overflow-y:scroll; + overflow-x:scroll; + white-space:nowrap; + box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23); +} +ul.ui-autocomplete { + position:fixed; + z-index:999999; +} +ul.ui-autocomplete li { + float:left; + clear:both; + width:100%; +} +.resultHighlight { + font-weight:bold; +} +#search { + background-image:url('resources/glass.png'); + background-size:13px; + background-repeat:no-repeat; + background-position:2px 3px; + padding-left:20px; + position:relative; + right:-18px; + width:400px; +} +#reset { + background-color: rgb(255,255,255); + background-image:url('resources/x.png'); + background-position:center; + background-repeat:no-repeat; + background-size:12px; + border:0 none; + width:16px; + height:17px; + position:relative; + left:-4px; + top:-4px; + font-size:0px; +} +.watermark { + color:#545454; +} +.searchTagDescResult { + font-style:italic; + font-size:11px; +} +.searchTagHolderResult { + font-style:italic; + font-size:12px; +} +.searchTagResult:before, .searchTagResult:target { + color:red; +} +.moduleGraph span { + display:none; + position:absolute; +} +.moduleGraph:hover span { + display:block; + margin: -100px 0 0 100px; + z-index: 1; +} +.methodSignature { + white-space:normal; +} + +/* + * Styles for user-provided tables. + * + * borderless: + * No borders, vertical margins, styled caption. + * This style is provided for use with existing doc comments. + * In general, borderless tables should not be used for layout purposes. + * + * plain: + * Plain borders around table and cells, vertical margins, styled caption. + * Best for small tables or for complex tables for tables with cells that span + * rows and columns, when the "striped" style does not work well. + * + * striped: + * Borders around the table and vertical borders between cells, striped rows, + * vertical margins, styled caption. + * Best for tables that have a header row, and a body containing a series of simple rows. + */ + +table.borderless, +table.plain, +table.striped { + margin-top: 10px; + margin-bottom: 10px; +} +table.borderless > caption, +table.plain > caption, +table.striped > caption { + font-weight: bold; + font-size: smaller; +} +table.borderless th, table.borderless td, +table.plain th, table.plain td, +table.striped th, table.striped td { + padding: 2px 5px; +} +table.borderless, +table.borderless > thead > tr > th, table.borderless > tbody > tr > th, table.borderless > tr > th, +table.borderless > thead > tr > td, table.borderless > tbody > tr > td, table.borderless > tr > td { + border: none; +} +table.borderless > thead > tr, table.borderless > tbody > tr, table.borderless > tr { + background-color: transparent; +} +table.plain { + border-collapse: collapse; + border: 1px solid black; +} +table.plain > thead > tr, table.plain > tbody tr, table.plain > tr { + background-color: transparent; +} +table.plain > thead > tr > th, table.plain > tbody > tr > th, table.plain > tr > th, +table.plain > thead > tr > td, table.plain > tbody > tr > td, table.plain > tr > td { + border: 1px solid black; +} +table.striped { + border-collapse: collapse; + border: 1px solid black; +} +table.striped > thead { + background-color: #E3E3E3; +} +table.striped > thead > tr > th, table.striped > thead > tr > td { + border: 1px solid black; +} +table.striped > tbody > tr:nth-child(even) { + background-color: #EEE +} +table.striped > tbody > tr:nth-child(odd) { + background-color: #FFF +} +table.striped > tbody > tr > th, table.striped > tbody > tr > td { + border-left: 1px solid black; + border-right: 1px solid black; +} +table.striped > tbody > tr > th { + font-weight: normal; +} diff --git a/plugins/javadoc/src/main/resources/views/class.korte b/plugins/javadoc/src/main/resources/views/class.korte new file mode 100644 index 00000000..1ddf6796 --- /dev/null +++ b/plugins/javadoc/src/main/resources/views/class.korte @@ -0,0 +1,297 @@ +{% extends "components/base.korte" %} +{% block content %} + +<main role="main"> + <div class="header"> + <div class="subTitle"><span class="packageLabelInType">Package</span> <a href="package-summary.html">{{ package }}</a></div> + <h2 title="{{ kind|capitalize }} {{ name }}" class="title">{{ kind|capitalize }} {{ name }}</h2> + </div> + <div class="contentContainer"> + <!-- <ul class="inheritance"> + <li>java.lang.Object</li> + <li> + <ul class="inheritance"> + <li>adaptation.Adaptation</li> + </ul> + </li> + </ul> TODO inheritance tree --> + <div class="description"> + <ul class="blockList"> + <li class="blockList"> + {% if implementedInterfaces.size != 0 %} + <dl> + <dt>All Implemented Interfaces:</dt> + <dd> + {% for name in implementedInterfaces %} + <code>{{ name }}</code> + {% if !loop.last %} + , + {% endif %} + {% endfor %} + </dd> + </dl> + {% endif %} + <hr> + <pre> +{% if signature.annotations != null %}{{ signature.annotations|raw }} {% endif %} +{{ signature.modifiers }} <span class="typeNameLabel">{{ signature.signatureWithoutModifiers|raw }}</span> +{% if signature.supertypes != null %}{{signature.supertypes|raw}} {% endif %} + </pre> + <div class="block">{{ classlikeDocumentation|raw }}</div> + </li> + </ul> + </div> + <div class="summary"> + <ul class="blockList"> + <li class="blockList"> + <!-- ======== NESTED CLASS SUMMARY ======== --> + {% if classlikes.size != 0 %} + <section role="region"> + <ul class="blockList"> + <li class="blockList"><a id="nested.class.summary"> + <!-- --> + </a> + <h3>Nested Class Summary</h3> + <div class="memberSummary"> + <table> + <caption><span>Nested Classes</span><span class="tabEnd"> </span></caption> + <tr> + <th class="colFirst" scope="col">Modifier and Type</th> + <th class="colSecond" scope="col">Class</th> + <th class="colLast" scope="col">Description</th> + </tr> + {% for classlike in classlikes %} + <tr class="{{ rowColor(loop.index0) }}"> + <td class="colFirst"><code>{{ classlike.modifiers }}</code></td> + <th class="colSecond" scope="row"><code>{{ classlike.signature }}</span></code> + </th> + <td class="colLast">{{ classlike.description }}</td> + </tr> + {% endfor %} + </table> + </div> + </li> + </ul> + </section> + {% endif %} + <!-- =========== FIELD SUMMARY =========== --> + {% if properties.size != 0 %} + <section role="region"> + <ul class="blockList"> + <li class="blockList"><a id="field.summary"> + <!-- --> + </a> + <h3>Field Summary</h3> + <div class="memberSummary"> + <table> + <caption><span>Fields</span><span class="tabEnd"> </span></caption> + <tr> + <th class="colFirst" scope="col">Modifier and Type</th> + <th class="colSecond" scope="col">Field</th> + <th class="colLast" scope="col">Description</th> + </tr> + {% for property in properties %} + <tr class="{{ rowColor(loop.index0) }}"> + <td class="colFirst"><code>{{ property.modifiers|raw }}</code></td> + <th class="colSecond" scope="row"><code>{{ property.signature|raw }}</code></th> + <td class="colLast">{{ description|raw }}</td> + </tr> + {% endfor %} + </table> + </div> + </li> + </ul> + </section> + {% endif %} + <!-- ======== CONSTRUCTOR SUMMARY ======== --> + {% if constructors.size != 0 %} + <section role="region"> + <ul class="blockList"> + <li class="blockList"><a id="constructor.summary"> + <!-- --> + </a> + <h3>Constructor Summary</h3> + <div class="memberSummary"> + <table> + <caption><span>Constructors</span><span class="tabEnd"> </span></caption> + <tbody> + <tr> + <th class="colFirst" scope="col">Constructor</th> + <th class="colLast" scope="col">Description</th> + </tr> + + {% for constructor in constructors %} + <tr class="{{ rowColor(loop.index0) }}"> + <th class="colConstructorName" scope="row"><code><span class="memberNameLink"><a + href="#{{ constructor.anchorLink }}">{{ constructor.name }}</a></span>({{ constructor.inlineParameters|raw }})</code></th> + <td class="colLast">{{ constructor.brief|raw }}</td> + </tr> + {% endfor %} + + </tbody> + </table> + </div> + </li> + </ul> + </section> + {% endif %} + <!-- =========== ENUM CONSTANT SUMMARY =========== --> + {% if entries.size != 0 %} + <section role="region"> + <ul class="blockList"> + <li class="blockList"><a id="enum.constant.summary"> + <!-- --> + </a> + <h3>Enum Constant Summary</h3> + <table class="memberSummary"> + <caption><span>Enum Constants</span><span class="tabEnd"> </span></caption> + <tr> + <th class="colFirst" scope="col">Enum Constant</th> + <th class="colLast" scope="col">Description</th> + </tr> + {% for entry in entries %} + <tr class="{{ rowColor(loop.index0) }}"> + <th class="colFirst" scope="row"><code><span class="memberNameLink"><a + href="TODO">{{ entry.signature.signatureWithoutModifiers|raw }}</a></span></code></th> + <td class="colLast">{{ entry.brief|raw }}</td> + </tr> + {% endfor %} + </table> + </li> + </ul> + </section> + {% endif %} + <!-- ========== METHOD SUMMARY =========== --> + {% if methods.own.size !=0 || methods.inherited.size != 0 %} + <section role="region"> + <ul class="blockList"> + <li class="blockList"><a id="method.summary"> + <!-- --> + </a> + <h3>Method Summary</h3> + <div class="memberSummary"> + <div role="tablist" aria-orientation="horizontal"> + <button role="tab" aria-selected="true" aria-controls="memberSummary_tabpanel" tabindex="0" onkeydown="switchTab(event)" id="t0" class="activeTableTab">All Methods</button> + <!-- TODO: Instance and Concrete Methods #1118 --> + </div> + <div id="memberSummary_tabpanel" role="tabpanel"> + <table aria-labelledby="t0"> + <tr> + <th class="colFirst" scope="col">Modifier and Type</th> + <th class="colSecond" scope="col">Method</th> + <th class="colLast" scope="col">Description</th> + </tr> + {% for method in methods.own %} + <tr id="i{{ loop.index0 }}" class="{{ rowColor(loop.index0) }}"> + <td class="colFirst"><code>{{ method.signature.modifiers|raw }}</code> + </td> + <th class="colSecond" scope="row"><code>{{ method.signature.signatureWithoutModifiers|raw }} </code> + </th> + <td class="colLast">{{ method.brief|raw }}</td> + </tr> + {% endfor %} + </tbody> + </table> + </div> + <ul class="blockList"> + {% for method in methods.inherited %} + <li class="blockList"><a id="methods.inherited.from.class.{{ method.inheritedFrom}}"> + <!-- --> + </a> + <h3>Methods inherited from class {{ method.inheritedFrom}}</h3> + <code>{{ method.names }}</code></li> + {% endfor %} + <li class="blockList"><a id="methods.inherited.from.class.java.lang.Object"> + <!-- --> + </a> + <h3>Methods inherited from class java.lang.Object</h3> + <code>clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, + wait, wait, wait</code></li> + </ul> + </li> + </ul> + </section> + {% endif %} + </li> + </ul> + </div> + <div class="details"> + <ul class="blockList"> + <li class="blockList"> + <!-- ========= CONSTRUCTOR DETAIL ======== --> + {% if constructors.size != 0 %} + <section role="region"> + <ul class="blockList"> + <li class="blockList"><a id="constructor.detail"> + <!-- --> + </a> + <h3>Constructor Detail</h3> + {% for constructor in constructors %} + <a name="{{ constructor.anchorLink }}"></a> + <ul class="blockList"> + <li class="blockList"> + <h4>{{ constructor.name }}</h4> + <pre>{{ constructor.name }}({{ constructor.inlineParameters|raw }})</pre> + <div class="block">{{ constructor.brief|raw}}</div> + {% if constructor.parameters.size != 0 && hasAnyDescription(constructor.parameters) %} + <dl> + <dt><span class="paramLabel">Parameters:</span></dt> + {% for parameter in constructor.parameters %} + {% if parameter.description != "" %} + <dd><code>{{ parameter.name }}</code> - {{ parameter.description|raw }}</dd> + {% endif %} + {% endfor %} + </dl> + {% endif %} + </li> + </ul> + {% endfor %} + </li> + </ul> + </section> + {% endif %} + <!-- ============ METHOD DETAIL ========== --> + {% if methods.own.size != 0 %} + <section role="region"> + <ul class="blockList"> + <li class="blockList"><a id="method.detail"> + <!-- --> + </a> + <h3>Method Detail</h3> + {% for method in methods.own %} + <a id="{{ method.anchorLink }}"> + <!-- --> + </a> + <ul class={% if loop.last %} + "blockListLast" + {% else %} + "blockList" + {% endif %}> + <li class="blockList"> + <h4>{{ method.name }}</h4> + <pre class="methodSignature">{{ method.signature.annotations|raw }} {{ method.signature.modifiers|raw }} {{ method.signature.signatureWithoutModifiers|raw}}</pre> + <div class="block">{{ method.brief|raw }}</div> + {% if method.parameters.size != 0 && hasAnyDescription(method.parameters) %} + <dl> + <dt><span class="paramLabel">Parameters:</span></dt> + {% for parameter in method.parameters %} + {% if parameter.description != "" %} + <dd><code>{{ parameter.name }}</code> - {{ parameter.description|raw }}</dd> + {% endif %} + {% endfor %} + </dl> + {% endif %} + <!-- TODO missing return annotation --> + </li> + </ul> + {% endfor %} + </li> + </ul> + </section> + {% endif %} + </li> + </ul> + </div> + </div> +</main> +{% end %}
\ No newline at end of file diff --git a/plugins/javadoc/src/main/resources/views/components/base.korte b/plugins/javadoc/src/main/resources/views/components/base.korte new file mode 100644 index 00000000..786f007c --- /dev/null +++ b/plugins/javadoc/src/main/resources/views/components/base.korte @@ -0,0 +1,26 @@ +<!DOCTYPE HTML> +<!-- NewPage --> +<html lang="en"> +{%- include "components/head.korte" -%} +<body> +<script type="text/javascript"><!-- +try { + if (location.href.indexOf('is-external=true') == -1) { + parent.document.title = "adaptation (terrain-generator 0.0.1 API)"; + } +} catch (err) { +} +//--> +var pathtoroot = "{{ pathToRoot }}"; +</script> +<noscript> + <div>JavaScript is disabled on your browser.</div> +</noscript> +{%- include "components/topNavbar.korte" -%} +{%- block content %} {% endblock -%} +{%- include "components/bottomNavbar.korte" -%} +<ul class="ui-autocomplete ui-front ui-menu ui-widget ui-widget-content" id="ui-id-1" tabindex="0" + style="display: none;"></ul> +<span role="status" aria-live="assertive" aria-relevant="additions" class="ui-helper-hidden-accessible"></span> +</body> +</html>
\ No newline at end of file diff --git a/plugins/javadoc/src/main/resources/views/components/bottomNavbar.korte b/plugins/javadoc/src/main/resources/views/components/bottomNavbar.korte new file mode 100644 index 00000000..789e3c54 --- /dev/null +++ b/plugins/javadoc/src/main/resources/views/components/bottomNavbar.korte @@ -0,0 +1,20 @@ +<footer role="contentinfo"> + <nav role="navigation"> + <!-- ======= START OF BOTTOM NAVBAR ====== --> + <div class="bottomNav"><a id="navbar.bottom"> + <!-- --> + </a> + <div class="skipNav"><a href="#skip.navbar.bottom" title="Skip navigation links">Skip navigation links</a> + </div> + <a id="navbar.bottom.firstrow"> + <!-- --> + </a> + {% include "components/navList.korte" -%} + </div> + <a id="skip.navbar.bottom"> + <!-- --> + </a></div> + {% set type="bottom" %}{% include "components/subNav.korte" -%} + <!-- ======== END OF BOTTOM NAVBAR ======= --> + </nav> +</footer>
\ No newline at end of file diff --git a/plugins/javadoc/src/main/resources/views/components/head.korte b/plugins/javadoc/src/main/resources/views/components/head.korte new file mode 100644 index 00000000..a3c74f12 --- /dev/null +++ b/plugins/javadoc/src/main/resources/views/components/head.korte @@ -0,0 +1,17 @@ +<head> + <title>{{ title }} ({{ docName }} {{ version }})</title> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> + <meta name="dc.created" content="2020-03-25"> + <link rel="stylesheet" type="text/css" href="{{ pathToRoot }}stylesheet.css" title="Style"> + <link rel="stylesheet" type="text/css" href="{{ pathToRoot }}jquery/jquery-ui.css" title="Style"> + <script type="text/javascript" src="{{ pathToRoot }}jquery/jquery-3.3.1.js"></script> + <script type="text/javascript" src="{{ pathToRoot }}jquery/jquery-migrate-3.0.1.js"></script> + <script type="text/javascript" src="{{ pathToRoot }}jquery/jquery-ui.js"></script> + + <script type="text/javascript" src="{{ pathToRoot }}search.js"></script> + <script async type="text/javascript" src="{{ pathToRoot }}module-search-index.js"></script> + <script async type="text/javascript" src="{{ pathToRoot }}package-search-index.js"></script> + <script async type="text/javascript" src="{{ pathToRoot }}type-search-index.js"></script> + <script async type="text/javascript" src="{{ pathToRoot }}member-search-index.js"></script> + <script async type="text/javascript" src="{{ pathToRoot }}tag-search-index.js"></script> +</head>
\ No newline at end of file diff --git a/plugins/javadoc/src/main/resources/views/components/indexPage.korte b/plugins/javadoc/src/main/resources/views/components/indexPage.korte new file mode 100644 index 00000000..d22b89ea --- /dev/null +++ b/plugins/javadoc/src/main/resources/views/components/indexPage.korte @@ -0,0 +1,30 @@ +<main role="main"> + <div class="header"> + <h1 {{ h1Title(kind)|raw }}class="title">{{ title }} {{ version }} API</h1> + </div> + <div class="header"> + <div class="subtitle"> + <div class="block">{{ subtitle|raw }}</div> + </div> + <p>See: <a href="#overview_description">Description</a></p> + </div> + <div class="contentContainer"> + {% if lists %} + <ul class="blockList"> + {% for item in lists%} + <li class="blockList"> + {% set list = item.list %} + {% set colTitle = item.colTitle %} + {% set tabTitle = item.tabTitle %} + {% set isTypeSummary = "true" %} + {% include "components/indexTable.korte" %} + </li> + {% endfor %} + </ul> + {% else %} + <div class="overviewSummary"> + {% include "components/indexTable.korte" %} + </div> + {% endif %} + </div> +</main>
\ No newline at end of file diff --git a/plugins/javadoc/src/main/resources/views/components/indexTable.korte b/plugins/javadoc/src/main/resources/views/components/indexTable.korte new file mode 100644 index 00000000..21c94b7c --- /dev/null +++ b/plugins/javadoc/src/main/resources/views/components/indexTable.korte @@ -0,0 +1,18 @@ +{% if isTypeSummary %} + <div class="typeSummary"> +{% endif %} +<table> +<caption><span>{{ tabTitle }}</span><span class="tabEnd"> </span></caption> +<tr> +<th class="colFirst" scope="col">{{ colTitle }}</th> +<th class="colLast" scope="col">Description</th> +</tr> +<tbody> +{% for item in list %} + <tr class="{{ rowColor(loop.index0) }}">{{ createTabRow(item, contextRoot)|raw }}</tr> +{% end -%} +</tbody> +</table> +{% if isTypeSummary %} + </div> +{% endif %}
\ No newline at end of file diff --git a/plugins/javadoc/src/main/resources/views/components/navList.korte b/plugins/javadoc/src/main/resources/views/components/navList.korte new file mode 100644 index 00000000..1bee80be --- /dev/null +++ b/plugins/javadoc/src/main/resources/views/components/navList.korte @@ -0,0 +1,25 @@ +<ul class="navList" title="Navigation"> + {%- if kind == "main" %} + <li class="navBarCell1Rev">Overview</li> + {% else %} + <li><a href="{{ pathToRoot }}index.html">Overview</a></li> + {% endif -%} + {%- if kind == "package" %} + <li class="navBarCell1Rev">Package</li> + {% else %} + <li><a href="package-summary.html">Package</a></li> + {% endif -%} + {%- if kind == "class" %} + <li class="navBarCell1Rev">Class</li> + {% else %} + <li>Class</li> + {% endif -%} + {%- if kind == "main" %} + <li><a href="overview-tree.html">Tree</a></li> + {% else %} + <li><a href="package-tree.html">Tree</a></li> + {% end %} + <li><a href="{{ pathToRoot }}deprecated-list.html">Deprecated</a></li> + <li><a href="{{ pathToRoot }}index-all.html">Index</a></li> + <li><a href="{{ pathToRoot }}help-doc.html">Help</a></li> +</ul>
\ No newline at end of file diff --git a/plugins/javadoc/src/main/resources/views/components/pageStart.korte b/plugins/javadoc/src/main/resources/views/components/pageStart.korte new file mode 100644 index 00000000..56cf243e --- /dev/null +++ b/plugins/javadoc/src/main/resources/views/components/pageStart.korte @@ -0,0 +1,37 @@ +<!DOCTYPE HTML> +<!-- NewPage --> +<html lang="en"> +<head> +<title>$documentTitle ({{ title }} {{ version }} API)</title> +<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> +<meta name="dc.created" content="{{ curDate() }}"> +<link rel="stylesheet" type="text/css" href="{{ pathToRoot }}stylesheet.css" title="Style"> +<link rel="stylesheet" type="text/css" href="{{ pathToRoot }}jquery/jquery-ui.css" title="Style"> +<script type="text/javascript" src="{{ pathToRoot }}script.js"></script> +<script type="text/javascript" src="{{ pathToRoot }}jquery/jszip/dist/jszip.min.js"></script> +<script type="text/javascript" src="{{ pathToRoot }}jquery/jszip-utils/dist/jszip-utils.min.js"></script> +<!--[if IE]> +<script type="text/javascript" src="{{ pathToRoot }}jquery/jszip-utils/dist/jszip-utils-ie.min.js"></script> +<![endif]--> +<script type="text/javascript" src="{{ pathToRoot }}jquery/jquery-{{ jQueryVersion() }}.js"></script> +<script type="text/javascript" src="{{ pathToRoot }}jquery/jquery-migrate-{{ jQueryMigrateVersion() }}.js"></script> +<script type="text/javascript" src="{{ pathToRoot }}jquery/jquery-ui.js"></script> +</head> +<body> +<script type="text/javascript"><!-- + try { + if (location.href.indexOf('is-external=true') == -1) { + parent.document.title="{{ documentTitle }} ({{ title }} {{ version }} API)"; + } + } + catch(err) { + } +//--> +var pathtoroot = "{{ pathToRoot }}"; +loadScripts(document, 'script');</script> +<noscript> +<div>JavaScript is disabled on your browser.</div> +</noscript> +<header role="banner"> +<nav role="navigation"> +<div class="fixedNav">
\ No newline at end of file diff --git a/plugins/javadoc/src/main/resources/views/components/subNav.korte b/plugins/javadoc/src/main/resources/views/components/subNav.korte new file mode 100644 index 00000000..aa0905cf --- /dev/null +++ b/plugins/javadoc/src/main/resources/views/components/subNav.korte @@ -0,0 +1,30 @@ +<div class="subNav"> +<ul class="navList" id="allclasses_navbar_top" style="display: block;"> +<li><a href="{{ pathToRoot }}allclasses.html">All Classes</a></li> +</ul> +{% if type != "bottom" %} +<ul class="navListSearch"> +<li><label for="search">SEARCH:</label> +<input type="text" id="search" value="search" class="ui-autocomplete-input" autocomplete="off" placeholder="Search"> +<input type="reset" id="reset" value="reset"> +</li> +</ul> +{% end -%} +<div> +<script type="text/javascript"><!-- + allClassesLink = document.getElementById("allclasses_navbar_top"); + if(window==top) { + allClassesLink.style.display = "block"; + } + else { + allClassesLink.style.display = "none"; + } + //--> +</script> +<noscript> +<div>JavaScript is disabled on your browser.</div> +</noscript> +</div> +<a id="skip.navbar.top"> +<!-- --> +</a></div>
\ No newline at end of file diff --git a/plugins/javadoc/src/main/resources/views/components/topNavbar.korte b/plugins/javadoc/src/main/resources/views/components/topNavbar.korte new file mode 100644 index 00000000..59b84558 --- /dev/null +++ b/plugins/javadoc/src/main/resources/views/components/topNavbar.korte @@ -0,0 +1,24 @@ +<header role="banner"> + <nav role="navigation"> + <div class="fixedNav"> + <!-- ========= START OF TOP NAVBAR ======= --> + <div class="topNav"><a id="navbar.top"> + <!-- --> + </a> + <div class="skipNav"><a href="#skip.navbar.top" title="Skip navigation links">Skip navigation links</a> + </div> + <a id="navbar.top.firstrow"> + <!-- --> + </a> + {% include "components/navList.korte" -%} + </div> + {% include "components/subNav.korte" -%} + <!-- ========= END OF TOP NAVBAR ========= --> + </div> + <div class="navPadding"> </div> + <script type="text/javascript"><!-- + $('.navPadding').css('padding-top', $('.fixedNav').css("height")); + //--> + </script> + </nav> +</header>
\ No newline at end of file diff --git a/plugins/javadoc/src/main/resources/views/listPage.korte b/plugins/javadoc/src/main/resources/views/listPage.korte new file mode 100644 index 00000000..985f1d4d --- /dev/null +++ b/plugins/javadoc/src/main/resources/views/listPage.korte @@ -0,0 +1,13 @@ +<html lang="en"> +{% include "components/head.korte" -%} +<body> +<h1 class="bar">{{ title }}</h1> +<main role="main" class="indexContainer"> + <ul> + {% for item in list %} + <li>{{ createListRow(item, contextRoot)|raw }}</li> + {% end -%} + </ul> +</main> +</body> +</html>
\ No newline at end of file diff --git a/plugins/javadoc/src/main/resources/views/tabPage.korte b/plugins/javadoc/src/main/resources/views/tabPage.korte new file mode 100644 index 00000000..c69d2c97 --- /dev/null +++ b/plugins/javadoc/src/main/resources/views/tabPage.korte @@ -0,0 +1,4 @@ +{% extends "components/base.korte" %} +{% block content %} +{% include "components/indexPage.korte" %} +{% end %} diff --git a/plugins/javadoc/src/main/resources/views/treePage.korte b/plugins/javadoc/src/main/resources/views/treePage.korte new file mode 100644 index 00000000..73a23896 --- /dev/null +++ b/plugins/javadoc/src/main/resources/views/treePage.korte @@ -0,0 +1,48 @@ +{% extends "components/base.korte" %} +{% block content %} +<main role="main"> + <div class="header"> + {% if kind == "main" %} + <h1 class="title">Hierarchy For All Packages</h1> + {% else %} + <h1 class="title">Hierarchy For Package {{ title }}</h1> + {% end -%} + <span class="packageHierarchyLabel">Package Hierarchies:</span> + <ul class="horizontal"> + {% if kind == "main" %} + {{ createPackageHierarchy(list)|raw }} + {% else %} + <li><a href="{{ pathToRoot }}package-tree.html">All Packages</a></li> + {% end -%} + </ul> + </div> + <div class="contentContainer"> + <section role="region"> + <h2 title="Class Hierarchy">Class Hierarchy</h2> + <ul> + {{ renderInheritanceGraph(classGraph)|raw }} + </ul> + </section> + <section role="region"> + <h2 title="Interface Hierarchy">Interface Hierarchy</h2> + <ul> + {{ renderInheritanceGraph(interfaceGraph)|raw }} + </ul> + </section> + <section role="region"> + <h2 title="Enum Hierarchy">Enum Hierarchy</h2> + <ul> + <li class="circle">java.lang.Object + <ul> + <li class="circle">java.lang.Enum<E> (implements java.lang.Comparable<T>, java.io.Serializable) + <ul> + <li class="circle">model.<a href="model/VertexType.html" title="enum in model"><span class="typeNameLink">VertexType</span></a></li> + </ul> + </li> + </ul> + </li> + </ul> + </section> + </div> +</main> +{% end %} diff --git a/plugins/javadoc/src/test/kotlin/javadoc/AbstractJavadocTemplateMapTest.kt b/plugins/javadoc/src/test/kotlin/javadoc/AbstractJavadocTemplateMapTest.kt new file mode 100644 index 00000000..f4f35ef0 --- /dev/null +++ b/plugins/javadoc/src/test/kotlin/javadoc/AbstractJavadocTemplateMapTest.kt @@ -0,0 +1,122 @@ +package javadoc + +import javadoc.pages.JavadocPageNode +import javadoc.pages.preprocessors +import javadoc.renderer.JavadocContentToTemplateMapTranslator +import org.jetbrains.dokka.DokkaConfigurationImpl +import org.jetbrains.dokka.javadoc.JavadocPlugin +import org.jetbrains.dokka.model.withDescendants +import org.jetbrains.dokka.pages.RootPageNode +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.DokkaPlugin +import org.jetbrains.dokka.plugability.plugin +import org.jetbrains.dokka.plugability.querySingle +import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest + +internal abstract class AbstractJavadocTemplateMapTest : AbstractCoreTest() { + protected var config: DokkaConfigurationImpl = dokkaConfiguration { + format = "javadoc" + sourceSets { + sourceSet { + sourceRoots = listOf("src") + analysisPlatform = "jvm" + } + } + } + + data class Result( + val rootPageNode: RootPageNode, + val context: DokkaContext + ) { + + val translator: JavadocContentToTemplateMapTranslator by lazy { + val locationProvider = context.plugin<JavadocPlugin>() + .querySingle { locationProviderFactory } + .getLocationProvider(rootPageNode) + + JavadocContentToTemplateMapTranslator(locationProvider, context) + } + + val JavadocPageNode.templateMap: Map<String, Any?> get() = translator.templateMapForPageNode(this) + + inline fun <reified T : JavadocPageNode> allPagesOfType(): List<T> { + return rootPageNode.withDescendants().filterIsInstance<T>().toList() + } + + inline fun <reified T : JavadocPageNode> firstPageOfType(): T { + return rootPageNode.withDescendants().filterIsInstance<T>().first() + } + + inline fun <reified T : JavadocPageNode> firstPageOfTypeOrNull(): T? { + return rootPageNode.withDescendants().filterIsInstance<T>().firstOrNull() + } + + inline fun <reified T : JavadocPageNode> singlePageOfType(): T { + return rootPageNode.withDescendants().filterIsInstance<T>().single() + } + } + + fun testTemplateMapInline( + query: String, + configuration: DokkaConfigurationImpl = config, + pluginsOverride: List<DokkaPlugin> = emptyList(), + assertions: Result.() -> Unit + ) { + testInline(query, configuration, pluginOverrides = pluginsOverride) { + renderingStage = { rootPageNode, dokkaContext -> + val transformedRootPageNode = preprocessors.fold(rootPageNode) { acc, pageTransformer -> + pageTransformer(acc) + } + + Result(transformedRootPageNode, dokkaContext).assertions() + } + } + } + + fun dualTestTemplateMapInline( + kotlin: String? = null, + java: String? = null, + configuration: DokkaConfigurationImpl = config, + pluginsOverride: List<DokkaPlugin> = emptyList(), + assertions: Result.() -> Unit + ) { + val kotlinException = kotlin?.let { + runCatching { + testTemplateMapInline( + query = kotlin, + configuration = configuration, + pluginsOverride = pluginsOverride, + assertions = assertions + ) + }.exceptionOrNull() + } + + val javaException = java?.let { + runCatching { + testTemplateMapInline( + query = java, + configuration = configuration, + pluginsOverride = pluginsOverride, + assertions = assertions + ) + }.exceptionOrNull() + } + + if (kotlinException != null && javaException != null) { + throw AssertionError( + "Kotlin and Java Code failed assertions\n" + + "Kotlin: ${kotlinException.message}\n" + + "Java : ${javaException.message}", + kotlinException + ) + } + + if (kotlinException != null) { + throw AssertionError("Kotlin Code failed assertions", kotlinException) + } + + if (javaException != null) { + throw AssertionError("Java Code failed assertions", javaException) + } + } +} diff --git a/plugins/javadoc/src/test/kotlin/javadoc/JavadocAllClassesTemplateMapTest.kt b/plugins/javadoc/src/test/kotlin/javadoc/JavadocAllClassesTemplateMapTest.kt new file mode 100644 index 00000000..314ba0a3 --- /dev/null +++ b/plugins/javadoc/src/test/kotlin/javadoc/JavadocAllClassesTemplateMapTest.kt @@ -0,0 +1,49 @@ +package javadoc + +import javadoc.pages.AllClassesPage +import javadoc.pages.LinkJavadocListEntry +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.pages.ContentKind +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import testApi.utils.assertIsInstance + +internal class JavadocAllClassesTemplateMapTest : AbstractJavadocTemplateMapTest() { + @Test + fun `two classes from different packages`() { + dualTestTemplateMapInline( + """ + /src/source0.kt + package package0 + /** + * Documentation for ClassA + */ + class ClassA + + /src/source1.kt + package package1 + /** + * Documentation for ClassB + */ + class ClassB + """ + ) { + val map = singlePageOfType<AllClassesPage>().templateMap + assertEquals("main", map["kind"]) + assertEquals("All Classes", map["title"]) + + val list = assertIsInstance<List<*>>(map["list"]) + assertEquals(2, list.size, "Expected two classes") + + val classA = assertIsInstance<LinkJavadocListEntry>(list[0]) + assertEquals("ClassA", classA.name) + assertEquals(DRI("package0", "ClassA"), classA.dri.single()) + assertEquals(ContentKind.Classlikes, classA.kind) + + val classB = assertIsInstance<LinkJavadocListEntry>(list[1]) + assertEquals("ClassB", classB.name) + assertEquals(DRI("package1", "ClassB"), classB.dri.single()) + assertEquals(ContentKind.Classlikes, classB.kind) + } + } +} diff --git a/plugins/javadoc/src/test/kotlin/javadoc/JavadocClasslikeTemplateMapTest.kt b/plugins/javadoc/src/test/kotlin/javadoc/JavadocClasslikeTemplateMapTest.kt new file mode 100644 index 00000000..2dea1abe --- /dev/null +++ b/plugins/javadoc/src/test/kotlin/javadoc/JavadocClasslikeTemplateMapTest.kt @@ -0,0 +1,331 @@ +package javadoc + +import javadoc.pages.JavadocClasslikePageNode +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import testApi.utils.assertIsInstance + +internal class JavadocClasslikeTemplateMapTest : AbstractJavadocTemplateMapTest() { + + @Test + fun `empty class`() { + dualTestTemplateMapInline( + kotlin = + """ + /src/source0.kt + package com.test.package0 + /** + * Documentation for TestClass + */ + class TestClass + """, + java = + """ + /src/com/test/package0/TestClass.java + package com.test.package0; + /** + * Documentation for TestClass + */ + public final class TestClass {} + """ + ) { + val map = singlePageOfType<JavadocClasslikePageNode>().templateMap + assertEquals("TestClass", map["name"]) + assertEquals("TestClass", map["title"]) + assertEquals("com.test.package0", map["packageName"]) + assertEquals("Documentation for TestClass", map["classlikeDocumentation"]) + assertEquals("Documentation for TestClass", map["subtitle"]) + assertEquals("public final class <a href=TestClass.html>TestClass</a>", map.signatureWithModifiers()) + } + } + + @Test + fun `single function`() { + dualTestTemplateMapInline( + kotlin = + """ + /src/source0.kt + package com.test.package0 + /** + * Documentation for TestClass + */ + class TestClass { + /** + * Documentation for testFunction + */ + fun testFunction(): String = "" + } + """, + java = + """ + /src/com/test/package0/TestClass.java + package com.test.package0 + /** + * Documentation for TestClass + */ + public final class TestClass { + /** + * Documentation for testFunction + */ + public final String testFunction() { + return ""; + } + } + """ + ) { + val map = singlePageOfType<JavadocClasslikePageNode>().templateMap + + assertEquals("TestClass", map["name"]) + assertEquals("TestClass", map["title"]) + assertEquals("com.test.package0", map["packageName"]) + assertEquals("Documentation for TestClass", map["classlikeDocumentation"]) + assertEquals("Documentation for TestClass", map["subtitle"]) + assertEquals("public final class", map.modifiers()) + assertEquals("<a href=TestClass.html>TestClass</a>", map.signatureWithoutModifiers()) + + val methods = assertIsInstance<Map<Any, Any?>>(map["methods"]) + val ownMethods = assertIsInstance<List<*>>(methods["own"]) + assertEquals(1, ownMethods.size, "Expected only one method") + val method = assertIsInstance<Map<String, Any?>>(ownMethods.single()) + assertEquals("Documentation for testFunction", method["brief"]) + assertEquals("testFunction", method["name"]) + assertEquals( + 0, assertIsInstance<List<*>>(method["parameters"]).size, + "Expected no parameters" + ) + assertEquals("final <a href=.html>String</a>", method.modifiers()) + assertEquals("<a href=TestClass.html#testFunction()>testFunction</a>()", method.signatureWithoutModifiers()) + } + } + + @Test + fun `class with annotation`(){ + dualTestTemplateMapInline( + kotlin = + """ + /src/source0.kt + package com.test.package0 + @MustBeDocumented + annotation class Author(val name: String) + + @Author( + name = "Benjamin Franklin" + ) + class TestClass {` + + @Author( + name = "Franklin D. Roosevelt" + ) + fun testFunction(): String = "" + } + """, + java = + """ + /src/com/test/package0/Author.java + package com.test.package0 + import java.lang.annotation.Documented; + + @Documented + public @interface Author { + String name(); + } + /src/com/test/package0/TestClass.java + package com.test.package0 + + @Author( + name = "Benjamin Franklin" + ) + public final class TestClass { + + @Author( + name = "Franklin D. Roosevelt" + ) + public final String testFunction() { + return ""; + } + } + """ + ){ + val map = allPagesOfType<JavadocClasslikePageNode>().first { it.name == "TestClass" }.templateMap + assertEquals("TestClass", map["name"]) + val signature = assertIsInstance<Map<String, Any?>>(map["signature"]) + assertEquals("@<a href=Author.html>Author</a>(name = \"Benjamin Franklin\")", signature["annotations"]) + + val methods = assertIsInstance<Map<Any, Any?>>(map["methods"]) + val ownMethods = assertIsInstance<List<*>>(methods["own"]) + val method = assertIsInstance<Map<String, Any?>>(ownMethods.single()) + val methodSignature = assertIsInstance<Map<String, Any?>>(method["signature"]) + assertEquals("@<a href=Author.html>Author</a>(name = \"Franklin D. Roosevelt\")", methodSignature["annotations"]) + } + } + + @Test + fun `simple enum`(){ + dualTestTemplateMapInline( + kotlin = + """ + /src/source0.kt + package com.test.package0 + enum class ClockDays { + /** + * Sample docs for first + */ + FIRST, + /** + * Sample docs for second + */ + SECOND + } + """, + java = + """ + /src/com/test/package0/TestClass.java + package com.test.package0; + enum ClockDays { + /** + * Sample docs for first + */ + FIRST, + /** + * Sample docs for second + */ + SECOND + } + """ + ){ + val map = singlePageOfType<JavadocClasslikePageNode>().templateMap + assertEquals("ClockDays", map["name"]) + assertEquals("enum", map["kind"]) + val entries = assertIsInstance<List<Map<String, Any?>>>(map["entries"]) + assertEquals(2, entries.size) + + val (first, second) = entries + assertEquals("Sample docs for first", first["brief"]) + assertEquals("Sample docs for second", second["brief"]) + + assertEquals("<a href=ClockDays.html#FIRST>FIRST</a>", first.signatureWithoutModifiers()) + assertEquals("<a href=ClockDays.html#SECOND>SECOND</a>", second.signatureWithoutModifiers()) + } + } + + @Test + fun `documented function parameters`(){ + dualTestTemplateMapInline( + kotlin = + """ + /src/source0.kt + package com.test.package0 + class TestClass { + /** + * Simple parameters list to check out + * @param simple simple String parameter + * @param parameters simple Integer parameter + * @param list simple Boolean parameter + * @return just a String + */ + fun testFunction(simple: String?, parameters: Int?, list: Boolean?): String { + return "" + } + } + """, + java = + """ + /src/com/test/package0/TestClass.java + package com.test.package0; + public final class TestClass { + /** + * Simple parameters list to check out + * @param simple simple String parameter + * @param parameters simple Integer parameter + * @param list simple Boolean parameter + * @return just a String + */ + public final String testFunction(String simple, Integer parameters, Boolean list) { + return ""; + } + } + """ + ) { + val map = singlePageOfType<JavadocClasslikePageNode>().templateMap + assertEquals("TestClass", map["name"]) + + val methods = assertIsInstance<Map<String, Any?>>(map["methods"]) + val testFunction = assertIsInstance<List<Map<String, Any?>>>(methods["own"]).single() + assertEquals("Simple parameters list to check out", testFunction["brief"]) + + val (first, second, third) = assertIsInstance<List<Map<String, Any?>>>(testFunction["parameters"]) + assertParameterNode( + node = first, + expectedName = "simple", + expectedType = "<a href=.html>String</a>", + expectedDescription = "simple String parameter" + ) + assertParameterNode( + node = second, + expectedName = "parameters", + expectedType = "<a href=.html>Integer</a>", + expectedDescription = "simple Integer parameter" + ) + assertParameterNode( + node = third, + expectedName = "list", + expectedType = "<a href=.html>Boolean</a>", + expectedDescription = "simple Boolean parameter" + ) + } + } + + @Test + fun `with generic parameters`(){ + dualTestTemplateMapInline( + kotlin = + """ + /src/source0.kt + package com.test.package0 + import java.io.Serializable + + class Generic<T : Serializable?> { + fun <D : T> sampleFunction(): D = TODO() + } + """, + java = + """ + /src/com/test/package0/Generic.java + package com.test.package0; + import java.io.Serializable; + + public final class Generic<T extends Serializable> { + public final <D extends T> D sampleFunction(){ + return null; + } + } + """ + ) { + val map = singlePageOfType<JavadocClasslikePageNode>().templateMap + assertEquals("Generic", map["name"]) + + assertEquals( + "public final class <a href=Generic.html>Generic</a><T extends <a href=.html>Serializable</a>>", + map.signatureWithModifiers() + ) + val methods = assertIsInstance<Map<Any, Any?>>(map["methods"]) + val ownMethods = assertIsInstance<List<*>>(methods["own"]).first() + val sampleFunction = assertIsInstance<Map<String, Any?>>(ownMethods) + + assertEquals("final <D extends <a href=Generic.html>T</a>> <a href=Generic.html#sampleFunction()>D</a> <a href=Generic.html#sampleFunction()>sampleFunction</a>()", sampleFunction.signatureWithModifiers()) + } + } + + private fun assertParameterNode(node: Map<String, Any?>, expectedName: String, expectedType: String, expectedDescription: String){ + assertEquals(expectedName, node["name"]) + assertEquals(expectedType, node["type"]) + assertEquals(expectedDescription, node["description"]) + } + + private fun Map<String, Any?>.signatureWithModifiers(): String = "${modifiers()} ${signatureWithoutModifiers()}" + + private fun Map<String, Any?>.signatureWithoutModifiers(): String = (get("signature") as Map<String, Any?>)["signatureWithoutModifiers"] as String + + private fun Map<String, Any?>.modifiers(): String = (get("signature") as Map<String, Any?>)["modifiers"] as String + +} diff --git a/plugins/javadoc/src/test/kotlin/javadoc/JavadocModuleTemplateMapTest.kt b/plugins/javadoc/src/test/kotlin/javadoc/JavadocModuleTemplateMapTest.kt new file mode 100644 index 00000000..b8bf41f2 --- /dev/null +++ b/plugins/javadoc/src/test/kotlin/javadoc/JavadocModuleTemplateMapTest.kt @@ -0,0 +1,78 @@ +package javadoc + +import javadoc.pages.JavadocModulePageNode +import javadoc.pages.RowJavadocListEntry +import org.jetbrains.dokka.links.DRI +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test +import testApi.utils.assertIsInstance + +internal class JavadocModuleTemplateMapTest : AbstractJavadocTemplateMapTest() { + + @Test + fun singleEmptyClass() { + dualTestTemplateMapInline( + kotlin = + """ + /src/source.kt + package com.test.package + class Test + """, + java = + """ + /src/com/test/package/Source.java + package com.test.package; + public class Test { } + """ + ) { + val moduleTemplateMap = singlePageOfType<JavadocModulePageNode>().templateMap + assertEquals("main", moduleTemplateMap["kind"]) + assertEquals("root", moduleTemplateMap["title"]) + assertEquals("", moduleTemplateMap["subtitle"]) + assertEquals("Packages", moduleTemplateMap["tabTitle"]) + assertEquals("Package", moduleTemplateMap["colTitle"]) + assertEquals("", moduleTemplateMap["pathToRoot"]) + + val list = moduleTemplateMap["list"] as List<*> + assertEquals(1, list.size, "Expected only one entry in 'list'") + val rowListEntry = assertIsInstance<RowJavadocListEntry>(list.first()) + + assertEquals("com.test", rowListEntry.link.name) + assertEquals(DRI("com.test"), rowListEntry.link.dri.single()) + } + } + + @Test + fun multiplePackages() { + dualTestTemplateMapInline( + kotlin = + """ + /src/source0.kt + package com.test.package0 + class Test0 + + /src/source1.kt + package com.test.package1 + class Test1 + """, + java = + """ + /src/com/test/package0/Test0.java + package com.test.package0; + public class Test0 {} + + /src/com/test/package1/Test1.java + package com.test.package1; + public class Test1 {} + """ + ) { + val moduleTemplateMap = singlePageOfType<JavadocModulePageNode>().templateMap + val list = assertIsInstance<List<*>>(moduleTemplateMap["list"]) + assertEquals(2, list.size, "Expected two entries in 'list'") + assertEquals("com.test.package0", assertIsInstance<RowJavadocListEntry>(list[0]).link.name) + assertEquals("com.test.package1", assertIsInstance<RowJavadocListEntry>(list[1]).link.name) + assertEquals(DRI("com.test.package0"), assertIsInstance<RowJavadocListEntry>(list[0]).link.dri.single()) + assertEquals(DRI("com.test.package1"), assertIsInstance<RowJavadocListEntry>(list[1]).link.dri.single()) + } + } +} diff --git a/plugins/javadoc/src/test/kotlin/javadoc/JavadocPackageTemplateMapTest.kt b/plugins/javadoc/src/test/kotlin/javadoc/JavadocPackageTemplateMapTest.kt new file mode 100644 index 00000000..e0ef030e --- /dev/null +++ b/plugins/javadoc/src/test/kotlin/javadoc/JavadocPackageTemplateMapTest.kt @@ -0,0 +1,123 @@ +package javadoc + +import javadoc.pages.JavadocContentKind +import javadoc.pages.JavadocPackagePageNode +import javadoc.pages.RowJavadocListEntry +import org.jetbrains.dokka.links.DRI +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import testApi.utils.assertIsInstance + +internal class JavadocPackageTemplateMapTest : AbstractJavadocTemplateMapTest() { + + @Test + fun `single class`() { + dualTestTemplateMapInline( + kotlin = + """ + /src/source.kt + package com.test.package0 + class Test + """, + java = + """ + /src/com/test/package0/Test.java + package com.test.package0; + public class Test {} + """ + ) { + val map = singlePageOfType<JavadocPackagePageNode>().templateMap + assertEquals("Class Summary", ((map["lists"] as List<*>).first() as Map<String, *>)["tabTitle"]) + assertEquals("Class", ((map["lists"] as List<*>).first() as Map<String, *>)["colTitle"]) + assertEquals("com.test.package0", map["title"]) + assertEquals("", map["subtitle"]) + assertEquals("package", map["kind"]) + + val list = assertIsInstance<List<*>>(((map["lists"] as List<*>).first() as Map<String, *>)["list"]) + val entry = assertIsInstance<RowJavadocListEntry>(list.single()) + assertEquals("Test", entry.link.name) + assertEquals(JavadocContentKind.Class, entry.link.kind) + assertEquals(DRI("com.test.package0", "Test"), entry.link.dri.single()) + } + } + + @Test + fun `multiple packages`() { + dualTestTemplateMapInline( + kotlin = + """ + /src/source0.kt + package com.test.package0 + class Test0 + + /src/source1.kt + package com.test.package1 + class Test1 + """, + java = + """ + /src/com/test/package0/Test0.java + package com.test.package0; + public class Test0 {} + + /src/com/test/package1/Test1.java + package com.test.package1; + public class Test1 {} + """ + ) { + val packagePages = allPagesOfType<JavadocPackagePageNode>() + packagePages.forEach { page -> + val map = page.templateMap + assertEquals("Class Summary", ((map["lists"] as List<*>).first() as Map<String, *>)["tabTitle"]) + assertEquals("Class", ((map["lists"] as List<*>).first() as Map<String, *>)["colTitle"]) + assertEquals("", map["subtitle"]) + assertEquals("package", map["kind"]) + } + + assertEquals(2, packagePages.size, "Expected two package pages") + } + } + + @Disabled("To be implemented / To be fixed") + @Test + fun `single class with package documentation`() { + dualTestTemplateMapInline( + kotlin = + """ + /src/packages.md + # Package com.test.package0 + ABC + + /src/source0.kt + package com.test.package0 + class Test + """, + + java = + """ + /src/com/test/package0/package-info.java + /** + * ABC + */ + package com.test.package0; + + /src/com/test/package0/Test.java + package com.test.package0; + public class Test{} + """, + configuration = config.copy( + sourceSets = config.sourceSets.map { sourceSet -> + sourceSet.copy( + includes = listOf("packages.md") + ) + } + ) + ) { + val packagePage = singlePageOfType<JavadocPackagePageNode>() + + val map = packagePage.templateMap + assertEquals("ABD", map["subtitle"].toString().trim()) + } + } +} diff --git a/plugins/javadoc/src/test/kotlin/javadoc/JavadocTest.kt b/plugins/javadoc/src/test/kotlin/javadoc/JavadocTest.kt new file mode 100644 index 00000000..31a33ad5 --- /dev/null +++ b/plugins/javadoc/src/test/kotlin/javadoc/JavadocTest.kt @@ -0,0 +1,69 @@ +package javadoc + +import org.jetbrains.dokka.javadoc.JavadocPlugin +import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest +import org.junit.jupiter.api.Test + +class JavadocTest : AbstractCoreTest() { + + @Test + fun test() { + val config = dokkaConfiguration { + format = "javadoc" + sourceSets { + sourceSet { + sourceRoots = listOf("jvmSrc/") + analysisPlatform = "jvm" + } + } + } + + /* + |/jvmSrc/javadoc/test/Test2.kt + |package javadoc.test + |class Test2() + */ + + testInline(""" + |/jvmSrc/javadoc/Test.kt + |/** + | test + |**/ + |package javadoc + |class Test() : List<Int> + |class Test2() : List<Int> + |/jvmSrc/javadoc/TestJ.java + |package javadoc + |abstract class Test3 extends List<Int> {} + """.trimIndent(), + config, + cleanupOutput = false, + pluginOverrides = listOf(JavadocPlugin()) + ) { + pagesTransformationStage = { + it + } + } + } + +// @Test +// fun test() { +// val config = dokkaConfiguration { +// format = "javadoc" +// passes { +// pass { +// sourceRoots = listOf("main/") +// analysisPlatform = "jvm" +// targets = listOf("jvm") +// } +// } +// } +// testFromData(config, +// cleanupOutput = false, +// pluginOverrides = listOf(JavadocPlugin())) { +// pagesTransformationStage = { +// it +// } +// } +// } +} diff --git a/plugins/javadoc/src/test/kotlin/javadoc/location/JavadocLocationTest.kt b/plugins/javadoc/src/test/kotlin/javadoc/location/JavadocLocationTest.kt new file mode 100644 index 00000000..235f68c0 --- /dev/null +++ b/plugins/javadoc/src/test/kotlin/javadoc/location/JavadocLocationTest.kt @@ -0,0 +1,128 @@ +package javadoc.location + +import javadoc.pages.JavadocClasslikePageNode +import javadoc.pages.JavadocPackagePageNode +import javadoc.renderer.JavadocContentToHtmlTranslator +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.ExternalDocumentationLinkImpl +import org.jetbrains.dokka.javadoc.JavadocPlugin +import org.jetbrains.dokka.model.firstChildOfType +import org.jetbrains.dokka.pages.RootPageNode +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.plugin +import org.jetbrains.dokka.plugability.querySingle +import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.Assertions.assertEquals + +class JavadocTest : AbstractCoreTest() { + + private fun locationTestInline(testHandler: (RootPageNode, DokkaContext) -> Unit) { + fun externalLink(link: String) = DokkaConfiguration.ExternalDocumentationLink + .Builder(link) + .build() as ExternalDocumentationLinkImpl + + val config = dokkaConfiguration { + format = "javadoc" + sourceSets { + sourceSet { + sourceRoots = listOf("jvmSrc/") + externalDocumentationLinks = listOf( + externalLink("https://docs.oracle.com/javase/8/docs/api/"), + externalLink("https://kotlinlang.org/api/latest/jvm/stdlib/") + ) + analysisPlatform = "jvm" + } + } + } + testInline( + """ + |/jvmSrc/javadoc/Test.kt + |package javadoc + |import java.io.Serializable + |class Test<A>() : Serializable, Cloneable { + | fun test() {} + | fun test2(s: String) {} + | fun <T> test3(a: A, t: T) {} + |} + """.trimIndent(), + config, + cleanupOutput = false, + pluginOverrides = listOf(JavadocPlugin()) + ) { renderingStage = testHandler } + } + + @Test + fun `resolved signature with external links`() { + + locationTestInline { rootPageNode, dokkaContext -> + val transformer = htmlTranslator(rootPageNode, dokkaContext) + val testClass = rootPageNode.firstChildOfType<JavadocPackagePageNode>() + .firstChildOfType<JavadocClasslikePageNode>() + assertEquals( + " implements <a href=https://docs.oracle.com/javase/8/docs/api/java/io/Serializable.html>Serializable</a>, <a href=https://docs.oracle.com/javase/8/docs/api/java/lang/Cloneable.html>Cloneable</a>", + transformer.htmlForContentNode(testClass.signature.supertypes!!, null) + ) + } + } + + @Test + fun `resolved signature to no argument function`() { + + locationTestInline { rootPageNode, dokkaContext -> + val transformer = htmlTranslator(rootPageNode, dokkaContext) + val testClassNode = rootPageNode.firstChildOfType<JavadocPackagePageNode>() + .firstChildOfType<JavadocClasslikePageNode> { it.name == "Test" } + val testFunctionNode = testClassNode.methods.first { it.name == "test" } + assertEquals( + """<a href=Test.html#test()>test</a>()""", + transformer.htmlForContentNode( + testFunctionNode.signature.signatureWithoutModifiers, + testClassNode + ) + ) + } + } + + @Test + fun `resolved signature to one argument function`() { + + locationTestInline { rootPageNode, dokkaContext -> + val transformer = htmlTranslator(rootPageNode, dokkaContext) + val testClassNode = rootPageNode.firstChildOfType<JavadocPackagePageNode>() + .firstChildOfType<JavadocClasslikePageNode> { it.name == "Test" } + val testFunctionNode = testClassNode.methods.first { it.name == "test2" } + assertEquals( + """<a href=Test.html#test2(String)>test2</a>(<a href=https://docs.oracle.com/javase/8/docs/api/java/lang/String.html>String</a>Â s)""", + transformer.htmlForContentNode( + testFunctionNode.signature.signatureWithoutModifiers, + testClassNode + ) + ) + } + } + + @Test + fun `resolved signature to generic function`() { + + locationTestInline { rootPageNode, dokkaContext -> + val transformer = htmlTranslator(rootPageNode, dokkaContext) + val testClassNode = rootPageNode.firstChildOfType<JavadocPackagePageNode>() + .firstChildOfType<JavadocClasslikePageNode> { it.name == "Test" } + val testFunctionNode = testClassNode.methods.first { it.name == "test3" } + assertEquals( + """<a href=Test.html#test3(A,T)>test3</a>(<a href=Test.html>A</a>Â a, <a href=Test.html#test3(A,T)>T</a>Â t)""", + transformer.htmlForContentNode( + testFunctionNode.signature.signatureWithoutModifiers, + testClassNode + ) + ) + } + } + + private fun htmlTranslator(rootPageNode: RootPageNode, dokkaContext: DokkaContext) = JavadocContentToHtmlTranslator( + dokkaContext.plugin<JavadocPlugin>().querySingle { locationProviderFactory } + .getLocationProvider(rootPageNode), + dokkaContext + ) +} diff --git a/plugins/javadoc/src/test/kotlin/javadoc/search/JavadocIndexSearchTest.kt b/plugins/javadoc/src/test/kotlin/javadoc/search/JavadocIndexSearchTest.kt new file mode 100644 index 00000000..6abed98d --- /dev/null +++ b/plugins/javadoc/src/test/kotlin/javadoc/search/JavadocIndexSearchTest.kt @@ -0,0 +1,59 @@ +package javadoc.search + +import javadoc.AbstractJavadocTemplateMapTest +import org.junit.jupiter.api.Test +import utils.TestOutputWriterPlugin +import org.junit.jupiter.api.Assertions.* + +internal class JavadocIndexSearchTest : AbstractJavadocTemplateMapTest() { + @Test + fun `javadoc index search tag`(){ + val writerPlugin = TestOutputWriterPlugin() + dualTestTemplateMapInline( + java = """ + /src/ClassA.java + package package0 + /** + * Documentation for ClassA + * Defines the implementation of the system Java compiler and its command line equivalent, {@index javac}, as well as javah. + */ + class ClassA { + + } + """, + pluginsOverride = listOf(writerPlugin) + ) { + val contents = writerPlugin.writer.contents + val expectedSearchTagJson = """var tagSearchIndex = [{"p":"package0","c":"ClassA","l":"javac","url":"package0/ClassA.html#javac"}]""" + assertEquals(expectedSearchTagJson, contents["tag-search-index.js"]) + } + } + + @Test + fun `javadoc type with member search`(){ + val writerPlugin = TestOutputWriterPlugin() + dualTestTemplateMapInline( + java = """ + /src/ClassA.java + package package0 + class ClassA { + public String propertyOfClassA = "Sample"; + + public void sampleFunction(){ + + } + } + """, + pluginsOverride = listOf(writerPlugin) + ) { + val contents = writerPlugin.writer.contents + val expectedPackageJson = """var packageSearchIndex = [{"l":"package0","url":"package0/package-summary.html"}, {"l":"All packages","url":"index.html"}]""" + val expectedClassesJson = """var typeSearchIndex = [{"p":"package0","l":"ClassA","url":"package0/ClassA.html"}, {"l":"All classes","url":"allclasses.html"}]""" + val expectedMembersJson = """var memberSearchIndex = [{"p":"package0","c":"ClassA","l":"sampleFunction()","url":"package0/ClassA.html#sampleFunction()"}, {"p":"package0","c":"ClassA","l":"propertyOfClassA","url":"package0/ClassA.html#propertyOfClassA"}]""" + + assertEquals(expectedPackageJson, contents["package-search-index.js"]) + assertEquals(expectedClassesJson, contents["type-search-index.js"]) + assertEquals(expectedMembersJson, contents["member-search-index.js"]) + } + } +}
\ No newline at end of file diff --git a/plugins/javadoc/src/test/resources/javadoc/test/main/java/adaptation/Adaptation.kt b/plugins/javadoc/src/test/resources/javadoc/test/main/java/adaptation/Adaptation.kt new file mode 100644 index 00000000..6ec3692a --- /dev/null +++ b/plugins/javadoc/src/test/resources/javadoc/test/main/java/adaptation/Adaptation.kt @@ -0,0 +1,50 @@ +package adaptation + +import app.MainApp +import model.InteriorNode +import model.ModelGraph +import org.javatuples.Pair +import transformation.Transformation + +class Adaptation { + private val log: org.apache.log4j.Logger = org.apache.log4j.Logger.getLogger(Adaptation::class.java.name) + fun transform(graph: ModelGraph, transformation: Transformation): Pair<ModelGraph, Boolean> { + return transform(graph, transformation, false) + } + + private fun transform( + graph: ModelGraph, + transformation: Transformation, + prevResult: Boolean + ): Pair<ModelGraph, Boolean> { + var model: ModelGraph = graph + val interiors: Collection<InteriorNode> = graph.getInteriors() + for (i in interiors) { + try { + if (transformation.isConditionCompleted(graph, i)) { + model = transformation.transformGraph(model, i) + log.info( + "Executing transformation: " + transformation.getClass().getSimpleName() + .toString() + " on interior" + i.getId() + ) + Thread.sleep(1000) + return transform(model, transformation, true) + } + } catch (e: Exception) { + log.error( + "Transformation " + transformation.getClass().getSimpleName() + .toString() + " returned an error: " + e.toString() + " for interior: " + i.toString() + ) + } + } + return Pair(model, prevResult) + } + + inner class AdaptationInternalClass internal constructor(var param: Int) { + + inner class AdaptationInternalInternalClass + + } + + class AdaptationInternalStaticClass internal constructor(var param: Int) +}
\ No newline at end of file diff --git a/plugins/jekyll/build.gradle.kts b/plugins/jekyll/build.gradle.kts new file mode 100644 index 00000000..64cf9800 --- /dev/null +++ b/plugins/jekyll/build.gradle.kts @@ -0,0 +1,10 @@ +import org.jetbrains.registerDokkaArtifactPublication + +dependencies { + implementation(project(":plugins:base")) + implementation(project(":plugins:gfm")) +} + +registerDokkaArtifactPublication("jekyllPlugin") { + artifactId = "jekyll-plugin" +} diff --git a/plugins/jekyll/src/main/kotlin/JekyllPlugin.kt b/plugins/jekyll/src/main/kotlin/JekyllPlugin.kt new file mode 100644 index 00000000..45afb072 --- /dev/null +++ b/plugins/jekyll/src/main/kotlin/JekyllPlugin.kt @@ -0,0 +1,57 @@ +package org.jetbrains.dokka.jekyll + +import org.jetbrains.dokka.CoreExtensions +import org.jetbrains.dokka.base.DokkaBase +import org.jetbrains.dokka.base.renderers.PackageListCreator +import org.jetbrains.dokka.base.renderers.RootCreator +import org.jetbrains.dokka.gfm.CommonmarkRenderer +import org.jetbrains.dokka.gfm.GfmPlugin +import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.DokkaPlugin +import org.jetbrains.dokka.plugability.plugin +import org.jetbrains.dokka.plugability.query +import org.jetbrains.dokka.transformers.pages.PageTransformer +import java.lang.StringBuilder + + +class JekyllPlugin : DokkaPlugin() { + + val jekyllPreprocessors by extensionPoint<PageTransformer>() + + val renderer by extending { + (CoreExtensions.renderer + providing { JekyllRenderer(it) } + override plugin<GfmPlugin>().renderer) + } + + val rootCreator by extending { + jekyllPreprocessors with RootCreator + } + + val packageListCreator by extending { + jekyllPreprocessors providing { + PackageListCreator( + it, + "jekyll", + "md" + ) + } order { after(rootCreator) } + } +} + +class JekyllRenderer( + context: DokkaContext +) : CommonmarkRenderer(context) { + + override val preprocessors = context.plugin<JekyllPlugin>().query { jekyllPreprocessors } + + override fun buildPage(page: ContentPage, content: (StringBuilder, ContentPage) -> Unit): String { + val builder = StringBuilder() + builder.append("---\n") + builder.append("title: ${page.name} -\n") + builder.append("---\n") + content(builder, page) + return builder.toString() + } +} diff --git a/plugins/jekyll/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin b/plugins/jekyll/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin new file mode 100644 index 00000000..92c75544 --- /dev/null +++ b/plugins/jekyll/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin @@ -0,0 +1 @@ +org.jetbrains.dokka.jekyll.JekyllPlugin diff --git a/plugins/kotlin-as-java/build.gradle.kts b/plugins/kotlin-as-java/build.gradle.kts new file mode 100644 index 00000000..eda6114f --- /dev/null +++ b/plugins/kotlin-as-java/build.gradle.kts @@ -0,0 +1,12 @@ +import org.jetbrains.registerDokkaArtifactPublication + +dependencies { + implementation(project(":plugins:base")) + testImplementation(project(":plugins:base")) + testImplementation(project(":plugins:base:test-utils")) + testImplementation(project(":test-tools")) +} + +registerDokkaArtifactPublication("kotlinAsJavaPlugin") { + artifactId = "kotlin-as-java-plugin" +} diff --git a/plugins/kotlin-as-java/src/main/kotlin/KotlinAsJavaPlugin.kt b/plugins/kotlin-as-java/src/main/kotlin/KotlinAsJavaPlugin.kt new file mode 100644 index 00000000..5f06852e --- /dev/null +++ b/plugins/kotlin-as-java/src/main/kotlin/KotlinAsJavaPlugin.kt @@ -0,0 +1,19 @@ +package org.jetbrains.dokka.kotlinAsJava + +import org.jetbrains.dokka.CoreExtensions +import org.jetbrains.dokka.base.DokkaBase +import org.jetbrains.dokka.kotlinAsJava.signatures.JavaSignatureProvider +import org.jetbrains.dokka.kotlinAsJava.transformers.KotlinAsJavaDocumentableTransformer +import org.jetbrains.dokka.plugability.DokkaPlugin + +class KotlinAsJavaPlugin : DokkaPlugin() { + val kotlinAsJavaDocumentableToPageTranslator by extending { + CoreExtensions.documentableTransformer with KotlinAsJavaDocumentableTransformer() + } + val javaSignatureProvider by extending { + val dokkaBasePlugin = plugin<DokkaBase>() + dokkaBasePlugin.signatureProvider providing { ctx -> + JavaSignatureProvider(ctx.single(dokkaBasePlugin.commentsToContentConverter), ctx.logger) + } override dokkaBasePlugin.kotlinSignatureProvider + } +}
\ No newline at end of file diff --git a/plugins/kotlin-as-java/src/main/kotlin/converters/KotlinToJavaConverter.kt b/plugins/kotlin-as-java/src/main/kotlin/converters/KotlinToJavaConverter.kt new file mode 100644 index 00000000..e66266a2 --- /dev/null +++ b/plugins/kotlin-as-java/src/main/kotlin/converters/KotlinToJavaConverter.kt @@ -0,0 +1,298 @@ +package org.jetbrains.dokka.kotlinAsJava.converters + +import org.jetbrains.dokka.links.* +import org.jetbrains.dokka.links.Callable +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.DAnnotation +import org.jetbrains.dokka.model.DEnum +import org.jetbrains.dokka.model.DFunction +import org.jetbrains.dokka.model.Nullable +import org.jetbrains.dokka.model.TypeConstructor +import org.jetbrains.dokka.model.properties.PropertyContainer +import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType +import java.lang.IllegalStateException + +private fun <T : WithExpectActual> List<T>.groupedByLocation() = + map { it.sources to it } + .groupBy({ (location, _) -> + location.let { + it.entries.first().value.path.split("/").last().split(".").first() + "Kt" + } // TODO: first() does not look reasonable + }) { it.second } + +internal fun DPackage.asJava(): DPackage { + @Suppress("UNCHECKED_CAST") + val syntheticClasses = ((properties + functions) as List<WithExpectActual>) + .groupedByLocation() + .map { (syntheticClassName, nodes) -> + DClass( + dri = dri.withClass(syntheticClassName), + name = syntheticClassName, + properties = nodes.filterIsInstance<DProperty>().map { it.asJava() }, + constructors = emptyList(), + functions = ( + nodes.filterIsInstance<DProperty>() + .flatMap { it.javaAccessors() } + + nodes.filterIsInstance<DFunction>() + .map { it.asJava(syntheticClassName) }), // TODO: methods are static and receiver is a param + classlikes = emptyList(), + sources = emptyMap(), + expectPresentInSet = null, + visibility = sourceSets.map { + it to JavaVisibility.Public + }.toMap(), + companion = null, + generics = emptyList(), + supertypes = emptyMap(), + documentation = emptyMap(), + modifier = sourceSets.map { it to JavaModifier.Final }.toMap(), + sourceSets = sourceSets, + extra = PropertyContainer.empty() + ) + } + + return copy( + functions = emptyList(), + properties = emptyList(), + classlikes = classlikes.map { it.asJava() } + syntheticClasses + ) +} + +internal fun DProperty.asJava(isTopLevel: Boolean = false, relocateToClass: String? = null) = + copy( + dri = if (relocateToClass.isNullOrBlank()) { + dri + } else { + dri.withClass(relocateToClass) + }, + modifier = javaModifierFromSetter(), + visibility = visibility.mapValues { it.value.propertyVisibilityAsJava() }, + type = type.asJava(), // TODO: check + setter = null, + getter = null, // Removing getters and setters as they will be available as functions + extra = if (isTopLevel) extra + + extra.mergeAdditionalModifiers( + sourceSets.map { + it to setOf(ExtraModifiers.JavaOnlyModifiers.Static) + }.toMap() + ) + else extra + ) + +internal fun DProperty.javaModifierFromSetter() = + modifier.mapValues { + when { + it.value is JavaModifier -> it.value + setter == null -> JavaModifier.Final + else -> JavaModifier.Empty + } + } + +internal fun DProperty.javaAccessors(isTopLevel: Boolean = false, relocateToClass: String? = null): List<DFunction> = + listOfNotNull( + getter?.copy( + dri = if (relocateToClass.isNullOrBlank()) { + dri + } else { + dri.withClass(relocateToClass) + }, + name = "get" + name.capitalize(), + modifier = javaModifierFromSetter(), + visibility = visibility.mapValues { JavaVisibility.Public }, + type = type.asJava(), // TODO: check + extra = if (isTopLevel) getter!!.extra + + getter!!.extra.mergeAdditionalModifiers( + sourceSets.map { + it to setOf(ExtraModifiers.JavaOnlyModifiers.Static) + }.toMap() + ) + else getter!!.extra + ), + setter?.copy( + dri = if (relocateToClass.isNullOrBlank()) { + dri + } else { + dri.withClass(relocateToClass) + }, + name = "set" + name.capitalize(), + modifier = javaModifierFromSetter(), + visibility = visibility.mapValues { JavaVisibility.Public }, + type = type.asJava(), // TODO: check + extra = if (isTopLevel) setter!!.extra + setter!!.extra.mergeAdditionalModifiers( + sourceSets.map { + it to setOf(ExtraModifiers.JavaOnlyModifiers.Static) + }.toMap() + ) + else setter!!.extra + ) + ) + + +internal fun DFunction.asJava(containingClassName: String): DFunction { + val newName = when { + isConstructor -> containingClassName + else -> name + } + return copy( +// dri = dri.copy(callable = dri.callable?.asJava()), + name = newName, + type = type.asJava(), + modifier = if (modifier.all { (_, v) -> v is KotlinModifier.Final } && isConstructor) + sourceSets.map { it to JavaModifier.Empty }.toMap() + else sourceSets.map { it to modifier.values.first() }.toMap(), + parameters = listOfNotNull(receiver?.asJava()) + parameters.map { it.asJava() }, + receiver = null + ) // TODO static if toplevel +} + +internal fun DClasslike.asJava(): DClasslike = when (this) { + is DClass -> asJava() + is DEnum -> asJava() + is DAnnotation -> asJava() + is DObject -> asJava() + is DInterface -> asJava() + else -> throw IllegalArgumentException("$this shouldn't be here") +} + +internal fun DClass.asJava(): DClass = copy( + constructors = constructors.map { it.asJava(name) }, + functions = (functions + properties.map { it.getter } + properties.map { it.setter }).filterNotNull().map { + it.asJava(name) + }, + properties = properties.map { it.asJava() }, + classlikes = classlikes.map { it.asJava() }, + generics = generics.map { it.asJava() }, + supertypes = supertypes.mapValues { it.value.map { it.asJava() } }, + modifier = if (modifier.all { (_, v) -> v is KotlinModifier.Empty }) sourceSets.map { it to JavaModifier.Final } + .toMap() + else sourceSets.map { it to modifier.values.first() }.toMap() +) + +private fun DTypeParameter.asJava(): DTypeParameter = copy( + dri = dri.possiblyAsJava(), + bounds = bounds.map { it.asJava() } +) + +private fun Bound.asJava(): Bound = when (this) { + is TypeConstructor -> copy( + dri = dri.possiblyAsJava() + ) + is Nullable -> copy( + inner = inner.asJava() + ) + else -> this +} + +internal fun DEnum.asJava(): DEnum = copy( + constructors = constructors.map { it.asJava(name) }, + functions = (functions + properties.map { it.getter } + properties.map { it.setter }).filterNotNull().map { + it.asJava(name) + }, + properties = properties.map { it.asJava() }, + classlikes = classlikes.map { it.asJava() }, + supertypes = supertypes.mapValues { it.value.map { it.asJava() } } +// , entries = entries.map { it.asJava() } +) + +internal fun DObject.asJava(): DObject = copy( + functions = (functions + properties.map { it.getter } + properties.map { it.setter }) + .filterNotNull() + .map { it.asJava(name.orEmpty()) }, + properties = properties.map { it.asJava() } + + DProperty( + name = "INSTANCE", + modifier = sourceSets.map { it to JavaModifier.Final }.toMap(), + dri = dri.copy(callable = Callable("INSTANCE", null, emptyList())), + documentation = emptyMap(), + sources = emptyMap(), + visibility = sourceSets.map { + it to JavaVisibility.Public + }.toMap(), + type = TypeConstructor(dri, emptyList()), + setter = null, + getter = null, + sourceSets = sourceSets, + receiver = null, + generics = emptyList(), + expectPresentInSet = expectPresentInSet, + extra = PropertyContainer.withAll(sourceSets.map { + mapOf(it to setOf(ExtraModifiers.JavaOnlyModifiers.Static)).toAdditionalModifiers() + }) + ), + classlikes = classlikes.map { it.asJava() }, + supertypes = supertypes.mapValues { it.value.map { it.asJava() } } +) + +internal fun DInterface.asJava(): DInterface = copy( + functions = (functions + properties.map { it.getter } + properties.map { it.setter }) + .filterNotNull() + .map { it.asJava(name) }, + properties = emptyList(), + classlikes = classlikes.map { it.asJava() }, // TODO: public static final class DefaultImpls with impls for methods + generics = generics.map { it.asJava() }, + supertypes = supertypes.mapValues { it.value.map { it.asJava() } } +) + +internal fun DAnnotation.asJava(): DAnnotation = copy( + properties = properties.map { it.asJava() }, + constructors = emptyList(), + classlikes = classlikes.map { it.asJava() } +) // TODO investigate if annotation class can have methods and properties not from constructor + +internal fun DParameter.asJava(): DParameter = copy( + type = type.asJava(), + name = if (name.isNullOrBlank()) "\$self" else name +) + +internal fun Visibility.propertyVisibilityAsJava(): Visibility = + if(this is JavaVisibility) this + else JavaVisibility.Private + +internal fun String.getAsPrimitive(): JvmPrimitiveType? = org.jetbrains.kotlin.builtins.PrimitiveType.values() + .find { it.typeFqName.asString() == this } + ?.let { JvmPrimitiveType.get(it) } + +private fun DRI.partialFqName() = packageName?.let { "$it." } + classNames +private fun DRI.possiblyAsJava() = this.partialFqName().mapToJava()?.toDRI(this) ?: this + +private fun String.mapToJava(): ClassId? = + JavaToKotlinClassMap.mapKotlinToJava(FqName(this).toUnsafe()) + +internal fun ClassId.toDRI(dri: DRI?): DRI = DRI( + packageName = packageFqName.asString(), + classNames = classNames(), + callable = dri?.callable,//?.asJava(), TODO: check this + extra = null, + target = PointingToDeclaration +) + +internal fun DriWithKind.asJava(): DriWithKind = + DriWithKind( + dri = dri.possiblyAsJava(), + kind = kind.asJava() + ) + +internal fun ClassKind.asJava(): ClassKind { + return when(this){ + is JavaClassKindTypes -> this + KotlinClassKindTypes.CLASS -> JavaClassKindTypes.CLASS + KotlinClassKindTypes.INTERFACE -> JavaClassKindTypes.INTERFACE + KotlinClassKindTypes.ENUM_CLASS -> JavaClassKindTypes.ENUM_CLASS + KotlinClassKindTypes.ENUM_ENTRY -> JavaClassKindTypes.ENUM_ENTRY + KotlinClassKindTypes.ANNOTATION_CLASS -> JavaClassKindTypes.ANNOTATION_CLASS + KotlinClassKindTypes.OBJECT -> JavaClassKindTypes.CLASS + else -> throw IllegalStateException("Non exchaustive match while trying to convert $this to Java") + } +} + +private fun PropertyContainer<out Documentable>.mergeAdditionalModifiers(second: SourceSetDependent<Set<ExtraModifiers>>) = + this[AdditionalModifiers]?.squash(AdditionalModifiers(second)) ?: AdditionalModifiers(second) + +private fun AdditionalModifiers.squash(second: AdditionalModifiers) = + AdditionalModifiers(content + second.content) + +internal fun ClassId.classNames(): String = + shortClassName.identifier + (outerClassId?.classNames()?.let { ".$it" } ?: "")
\ No newline at end of file diff --git a/plugins/kotlin-as-java/src/main/kotlin/signatures/JavaSignatureProvider.kt b/plugins/kotlin-as-java/src/main/kotlin/signatures/JavaSignatureProvider.kt new file mode 100644 index 00000000..158dda22 --- /dev/null +++ b/plugins/kotlin-as-java/src/main/kotlin/signatures/JavaSignatureProvider.kt @@ -0,0 +1,175 @@ +package org.jetbrains.dokka.kotlinAsJava.signatures + +import org.jetbrains.dokka.base.signatures.JvmSignatureUtils +import org.jetbrains.dokka.base.signatures.SignatureProvider +import org.jetbrains.dokka.base.transformers.pages.comments.CommentsToContentConverter +import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.links.sureClassNames +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.properties.WithExtraProperties +import org.jetbrains.dokka.pages.ContentKind +import org.jetbrains.dokka.pages.ContentNode +import org.jetbrains.dokka.pages.TextStyle +import org.jetbrains.dokka.utilities.DokkaLogger +import kotlin.text.Typography.nbsp + +class JavaSignatureProvider(ctcc: CommentsToContentConverter, logger: DokkaLogger) : SignatureProvider, + JvmSignatureUtils by JavaSignatureUtils { + private val contentBuilder = PageContentBuilder(ctcc, this, logger) + + private val ignoredVisibilities = setOf(JavaVisibility.Default) + + private val ignoredModifiers = + setOf(KotlinModifier.Open, JavaModifier.Empty, KotlinModifier.Empty, KotlinModifier.Sealed) + + override fun signature(documentable: Documentable): List<ContentNode> = when (documentable) { + is DFunction -> signature(documentable) + is DProperty -> signature(documentable) + is DClasslike -> signature(documentable) + is DEnumEntry -> signature(documentable) + is DTypeParameter -> signature(documentable) + else -> throw NotImplementedError( + "Cannot generate signature for ${documentable::class.qualifiedName} ${documentable.name}" + ) + } + + private fun signature(e: DEnumEntry) = + e.sourceSets.map { + contentBuilder.contentFor( + e, + ContentKind.Symbol, + setOf(TextStyle.Monospace) + e.stylesIfDeprecated(it), + sourceSets = setOf(it) + ) { + link(e.name, e.dri) + } + } + + private fun signature(c: DClasslike) = + c.sourceSets.map { + contentBuilder.contentFor( + c, + ContentKind.Symbol, + setOf(TextStyle.Monospace) + ((c as? WithExtraProperties<out Documentable>)?.stylesIfDeprecated(it) + ?: emptySet()), + sourceSets = setOf(it) + ) { + text(c.visibility[it]?.takeIf { it !in ignoredVisibilities }?.name?.plus(" ") ?: "") + + if (c is DClass) { + text(c.modifier[it]?.takeIf { it !in ignoredModifiers }?.name?.plus(" ") ?: "") + text(c.modifiers()[it]?.toSignatureString() ?: "") + } + + when (c) { + is DClass -> text("class ") + is DInterface -> text("interface ") + is DEnum -> text("enum ") + is DObject -> text("class ") + is DAnnotation -> text("@interface ") + } + link(c.name!!, c.dri) + if (c is WithGenerics) { + list(c.generics, prefix = "<", suffix = ">") { + +buildSignature(it) + } + } + if (c is WithSupertypes) { + c.supertypes.map { (p, dris) -> + val (classes, interfaces) = dris.partition { it.kind == JavaClassKindTypes.CLASS } + list(classes, prefix = " extends ", sourceSets = setOf(p)) { + link(it.dri.sureClassNames, it.dri, sourceSets = setOf(p)) + } + list(interfaces, prefix = " implements ", sourceSets = setOf(p)){ + link(it.dri.sureClassNames, it.dri, sourceSets = setOf(p)) + } + } + } + } + } + + private fun signature(p: DProperty) = + p.sourceSets.map { + contentBuilder.contentFor( + p, + ContentKind.Symbol, + setOf(TextStyle.Monospace, TextStyle.Block) + p.stylesIfDeprecated(it), + sourceSets = setOf(it) + ) { + annotationsBlock(p) + text(p.visibility[it]?.takeIf { it !in ignoredVisibilities }?.name?.plus(" ") ?: "") + text(p.modifier[it]?.name + " ") + text(p.modifiers()[it]?.toSignatureString() ?: "") + signatureForProjection(p.type) + text(nbsp.toString()) + link(p.name, p.dri) + } + } + + private fun signature(f: DFunction) = + f.sourceSets.map { + contentBuilder.contentFor( + f, + ContentKind.Symbol, + setOf(TextStyle.Monospace, TextStyle.Block) + f.stylesIfDeprecated(it), + sourceSets = setOf(it) + ) { + annotationsBlock(f) + text(f.modifier[it]?.takeIf { it !in ignoredModifiers }?.name?.plus(" ") ?: "") + text(f.modifiers()[it]?.toSignatureString() ?: "") + val returnType = f.type + signatureForProjection(returnType) + text(nbsp.toString()) + link(f.name, f.dri) + list(f.generics, prefix = "<", suffix = ">") { + +buildSignature(it) + } + text("(") + list(f.parameters) { + annotationsInline(it) + text(it.modifiers()[it]?.toSignatureString() ?: "") + signatureForProjection(it.type) + text(nbsp.toString()) + link(it.name!!, it.dri) + } + text(")") + } + } + + private fun signature(t: DTypeParameter) = + t.sourceSets.map { + contentBuilder.contentFor(t, styles = t.stylesIfDeprecated(it), sourceSets = setOf(it)) { + text(t.name.substringAfterLast(".")) + list(t.bounds, prefix = " extends ") { + signatureForProjection(it) + } + } + + } + + private fun PageContentBuilder.DocumentableContentBuilder.signatureForProjection(p: Projection): Unit = when (p) { + is OtherParameter -> link(p.name, p.declarationDRI) + + is TypeConstructor -> group(styles = emptySet()) { + link(p.dri.classNames.orEmpty(), p.dri) + list(p.projections, prefix = "<", suffix = ">") { + signatureForProjection(it) + } + } + + is Variance -> group(styles = emptySet()) { + text(p.kind.toString() + " ") // TODO: "super" && "extends" + signatureForProjection(p.inner) + } + + is Star -> text("?") + + is Nullable -> signatureForProjection(p.inner) + + is JavaObject, is Dynamic -> link("Object", DRI("java.lang", "Object")) + is Void -> text("void") + is PrimitiveJavaType -> text(p.name) + is UnresolvedBound -> text(p.name) + } +}
\ No newline at end of file diff --git a/plugins/kotlin-as-java/src/main/kotlin/signatures/JavaSignatureUtils.kt b/plugins/kotlin-as-java/src/main/kotlin/signatures/JavaSignatureUtils.kt new file mode 100644 index 00000000..ecb97617 --- /dev/null +++ b/plugins/kotlin-as-java/src/main/kotlin/signatures/JavaSignatureUtils.kt @@ -0,0 +1,35 @@ +package org.jetbrains.dokka.kotlinAsJava.signatures + +import org.jetbrains.dokka.base.signatures.All +import org.jetbrains.dokka.base.signatures.JvmSignatureUtils +import org.jetbrains.dokka.base.translators.documentables.PageContentBuilder +import org.jetbrains.dokka.links.DRI +import org.jetbrains.dokka.model.* +import org.jetbrains.dokka.model.properties.WithExtraProperties + +object JavaSignatureUtils : JvmSignatureUtils { + + private val ignoredAnnotations = setOf( + Annotations.Annotation(DRI("kotlin.jvm", "Transient"), emptyMap()), + Annotations.Annotation(DRI("kotlin.jvm", "Volatile"), emptyMap()), + Annotations.Annotation(DRI("kotlin.jvm", "Transitive"), emptyMap()), + Annotations.Annotation(DRI("kotlin.jvm", "Strictfp"), emptyMap()), + Annotations.Annotation(DRI("kotlin.jvm", "JvmStatic"), emptyMap()) + ) + + private val strategy = All + private val listBrackets = Pair('{', '}') + private val classExtension = ".class" + + override fun PageContentBuilder.DocumentableContentBuilder.annotationsBlock(d: Documentable) = + annotationsBlockWithIgnored(d, ignoredAnnotations, strategy, listBrackets, classExtension) + + override fun PageContentBuilder.DocumentableContentBuilder.annotationsInline(d: Documentable) = + annotationsInlineWithIgnored(d, ignoredAnnotations, strategy, listBrackets, classExtension) + + override fun <T : Documentable> WithExtraProperties<T>.modifiers() = + extra[AdditionalModifiers]?.content?.entries?.map { + it.key to it.value.filterIsInstance<ExtraModifiers.JavaOnlyModifiers>().toSet() + }?.toMap() ?: emptyMap() + +}
\ No newline at end of file diff --git a/plugins/kotlin-as-java/src/main/kotlin/transformers/KotlinAsJavaDocumentableTransformer.kt b/plugins/kotlin-as-java/src/main/kotlin/transformers/KotlinAsJavaDocumentableTransformer.kt new file mode 100644 index 00000000..8b07670f --- /dev/null +++ b/plugins/kotlin-as-java/src/main/kotlin/transformers/KotlinAsJavaDocumentableTransformer.kt @@ -0,0 +1,11 @@ +package org.jetbrains.dokka.kotlinAsJava.transformers + +import org.jetbrains.dokka.kotlinAsJava.converters.asJava +import org.jetbrains.dokka.model.DModule +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.transformers.documentation.DocumentableTransformer + +class KotlinAsJavaDocumentableTransformer : DocumentableTransformer { + override fun invoke(original: DModule, context: DokkaContext): DModule = + original.copy(packages = original.packages.map { it.asJava() }) +} diff --git a/plugins/kotlin-as-java/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin b/plugins/kotlin-as-java/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin new file mode 100644 index 00000000..8ff3df82 --- /dev/null +++ b/plugins/kotlin-as-java/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin @@ -0,0 +1 @@ +org.jetbrains.dokka.kotlinAsJava.KotlinAsJavaPlugin diff --git a/plugins/kotlin-as-java/src/test/kotlin/KotlinAsJavaPluginTest.kt b/plugins/kotlin-as-java/src/test/kotlin/KotlinAsJavaPluginTest.kt new file mode 100644 index 00000000..af66a48e --- /dev/null +++ b/plugins/kotlin-as-java/src/test/kotlin/KotlinAsJavaPluginTest.kt @@ -0,0 +1,298 @@ +package kotlinAsJavaPlugin + +import org.jetbrains.dokka.model.dfs +import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest +import org.jetbrains.kotlin.utils.addToStdlib.cast +import org.junit.jupiter.api.Test +import matchers.content.* + +class KotlinAsJavaPluginTest : AbstractCoreTest() { + + @Test + fun topLevelTest() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/") + } + } + } + testInline( + """ + |/src/main/kotlin/kotlinAsJavaPlugin/Test.kt + |package kotlinAsJavaPlugin + | + |object TestObj {} + | + |fun testFL(l: List<String>) = l + |fun testF() {} + |fun testF2(i: Int) = i + |fun testF3(to: TestObj) = to + |fun <T : Char> testF4(t: T) = listOf(t) + |val testV = 1 + """, + configuration, + cleanupOutput = true + ) { + pagesGenerationStage = { root -> + val content = (root.children.single().children.first { it.name == "TestKt" } as ContentPage).content + + val children = content.mainContents.first().cast<ContentGroup>() + .children.filterIsInstance<ContentTable>() + .filter { it.children.isNotEmpty() } + + children.assertCount(2) + } + } + } + + @Test + fun topLevelWithClassTest() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/") + } + } + } + testInline( + """ + |/src/main/kotlin/kotlinAsJavaPlugin/Test.kt + |package kotlinAsJavaPlugin + | + |class Test { + | fun testFC() {} + | val testVC = 1 + |} + | + |fun testF(i: Int) = i + |val testV = 1 + """, + configuration, + cleanupOutput = true + ) { + pagesGenerationStage = { root -> + val contentList = root.children + .flatMap { it.children<ContentPage>() } + .map { it.content } + + val children = contentList.flatMap { content -> + content.mainContents.first().cast<ContentGroup>().children + .filterIsInstance<ContentTable>() + .filter { it.children.isNotEmpty() } + }.filterNot { it.toString().contains("<init>") } + + children.assertCount(4) + } + } + } + + @Test + fun kotlinAndJavaTest() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/") + } + } + } + testInline( + """ + |/src/main/kotlin/kotlinAsJavaPlugin/Test.kt + |package kotlinAsJavaPlugin + | + |fun testF(i: Int) = i + |/src/main/kotlin/kotlinAsJavaPlugin/TestJ.java + |package kotlinAsJavaPlugin + | + |class TestJ { + | int testF(int i) { return i; } + |} + """, + configuration, + cleanupOutput = true + ) { + pagesGenerationStage = { root -> + val classes = root.children.first().children.associateBy { it.name } + classes.values.assertCount(2, "Class count: ") + + classes["TestKt"].let { + it?.children.orEmpty().assertCount(1, "(Kotlin) TestKt members: ") + it!!.children.first() + .let { assert(it.name == "testF") { "(Kotlin) Expected method name: testF, got: ${it.name}" } } + } + + classes["TestJ"].let { + it?.children.orEmpty().assertCount(1, "(Java) TestJ members: ") + it!!.children.first() + .let { assert(it.name == "testF") { "(Java) Expected method name: testF, got: ${it.name}" } } + } + } + } + } + + @Test + fun `public kotlin properties should have a getter with same visibilities`(){ + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/") + } + } + } + testInline( + """ + |/src/main/kotlin/kotlinAsJavaPlugin/Test.kt + |package kotlinAsJavaPlugin + | + |class Test { + | public val publicProperty: String = "" + |} + """, + configuration, + cleanupOutput = true + ) { + pagesTransformationStage = { rootPageNode -> + val propertyGetter = rootPageNode.dfs { it is MemberPageNode && it.name == "getPublicProperty" } as? MemberPageNode + assert(propertyGetter != null) + propertyGetter!!.content.assertNode { + group { + header(1) { + +"getPublicProperty" + } + } + divergentGroup { + divergentInstance { + divergent { + group { + +"final" + group { + link { + +" String" + } + } + link { + +"getPublicProperty" + } + +"()" + } + } + } + } + } + } + } + } + + @Test + fun `java properties should keep its modifiers`(){ + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/") + } + } + } + testInline( + """ + |/src/main/kotlin/kotlinAsJavaPlugin/TestJ.java + |package kotlinAsJavaPlugin + | + |class TestJ { + | public Int publicProperty = 1; + |} + """, + configuration, + cleanupOutput = true + ) { + pagesGenerationStage = { root -> + val testClass = root.dfs { it.name == "TestJ" } as? ClasslikePageNode + assert(testClass != null) + (testClass!!.content as ContentGroup).children.last().assertNode { + group { + header(2){ + +"Properties" + } + table { + group { + link { + +"publicProperty" + } + platformHinted { + group { + +"public Int" + link { + +"publicProperty" + } + } + } + } + } + } + } + } + } + } + + @Test + fun `koltin interfaces and classes should be split to extends and implements`(){ + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/") + } + } + } + testInline( + """ + |/src/main/kotlin/kotlinAsJavaPlugin/Test.kt + |package kotlinAsJavaPlugin + | + | open class A { } + | interface B + | class C : A(), B + """, + configuration, + cleanupOutput = true + ) { + pagesGenerationStage = { root -> + val testClass = root.dfs { it.name == "C" } as? ClasslikePageNode + assert(testClass != null) + testClass!!.content.assertNode { + group { + header(expectedLevel = 1) { + +"C" + } + platformHinted { + group { + +"public final class" + link { + +"C" + } + +" extends " + link { + +"A" + } + +" implements " + link { + +"B" + } + } + } + } + skipAllNotMatching() + } + } + } + } + + private fun <T> Collection<T>.assertCount(n: Int, prefix: String = "") = + assert(count() == n) { "${prefix}Expected $n, got ${count()}" } + +} + +private val ContentNode.mainContents: List<ContentNode> + get() = (this as ContentGroup).children + .filterIsInstance<ContentGroup>() + .single { it.dci.kind == ContentKind.Main }.children diff --git a/plugins/mathjax/build.gradle.kts b/plugins/mathjax/build.gradle.kts new file mode 100644 index 00000000..e570de46 --- /dev/null +++ b/plugins/mathjax/build.gradle.kts @@ -0,0 +1,14 @@ +import org.jetbrains.registerDokkaArtifactPublication + +dependencies { + testImplementation("org.jsoup:jsoup:1.12.1") + testImplementation(project(":plugins:base")) + testImplementation(project(":plugins:base:test-utils")) + testImplementation(project(":test-tools")) + testImplementation(kotlin("test-junit")) + testImplementation(project(":kotlin-analysis")) +} + +registerDokkaArtifactPublication("mathjaxPlugin") { + artifactId = "mathjax-plugin" +} diff --git a/plugins/mathjax/src/main/kotlin/MathjaxPlugin.kt b/plugins/mathjax/src/main/kotlin/MathjaxPlugin.kt new file mode 100644 index 00000000..63699585 --- /dev/null +++ b/plugins/mathjax/src/main/kotlin/MathjaxPlugin.kt @@ -0,0 +1,32 @@ +package org.jetbrains.dokka.mathjax + + +import org.jetbrains.dokka.CoreExtensions +import org.jetbrains.dokka.model.doc.CustomTagWrapper +import org.jetbrains.dokka.pages.ContentPage +import org.jetbrains.dokka.pages.RootPageNode +import org.jetbrains.dokka.plugability.DokkaPlugin +import org.jetbrains.dokka.transformers.pages.PageTransformer + +class MathjaxPlugin : DokkaPlugin() { + val transformer by extending { + CoreExtensions.pageTransformer with MathjaxTransformer + } +} + +private const val ANNOTATION = "usesMathJax" +internal const val LIB_PATH = "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.6/MathJax.js?config=TeX-AMS_SVG&latest" + +object MathjaxTransformer : PageTransformer { + override fun invoke(input: RootPageNode) = input.transformContentPagesTree { + it.modified( + embeddedResources = it.embeddedResources + if (it.isNeedingMathjax) listOf(LIB_PATH) else emptyList() + ) + } + + private val ContentPage.isNeedingMathjax + get() = documentable?.documentation?.values + ?.flatMap { it.children } + .orEmpty() + .any { (it as? CustomTagWrapper)?.name == ANNOTATION } +}
\ No newline at end of file diff --git a/plugins/mathjax/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin b/plugins/mathjax/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin new file mode 100644 index 00000000..9f9736f3 --- /dev/null +++ b/plugins/mathjax/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin @@ -0,0 +1 @@ +org.jetbrains.dokka.mathjax.MathjaxPlugin diff --git a/plugins/mathjax/src/test/kotlin/MathjaxPluginTest.kt b/plugins/mathjax/src/test/kotlin/MathjaxPluginTest.kt new file mode 100644 index 00000000..4a4ada23 --- /dev/null +++ b/plugins/mathjax/src/test/kotlin/MathjaxPluginTest.kt @@ -0,0 +1,85 @@ +package mathjaxTest + +import org.jetbrains.dokka.mathjax.LIB_PATH +import org.jetbrains.dokka.mathjax.MathjaxPlugin +import org.jetbrains.dokka.testApi.testRunner.AbstractCoreTest +import org.jsoup.Jsoup +import org.junit.jupiter.api.Test +import utils.TestOutputWriterPlugin + +class MathjaxPluginTest : AbstractCoreTest() { + @Test + fun noMathjaxTest() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin/test/Test.kt") + } + } + } + val source = + """ + |/src/main/kotlin/test/Test.kt + |package example + | /** + | * Just a regular kdoc + | */ + | fun test(): String = "" + """.trimIndent() + val writerPlugin = TestOutputWriterPlugin() + testInline( + source, + configuration, + pluginOverrides = listOf(writerPlugin, MathjaxPlugin()) + ) { + renderingStage = { + _, _ -> Jsoup + .parse(writerPlugin.writer.contents["root/example/test.html"]) + .head() + .select("link, script") + .let { + assert(!it.`is`("[href=$LIB_PATH], [src=$LIB_PATH]")) + } + } + } + } + + @Test + fun usingMathjaxTest() { + val configuration = dokkaConfiguration { + sourceSets { + sourceSet { + sourceRoots = listOf("src/main/kotlin/test/Test.kt") + } + } + } + val source = + """ + |/src/main/kotlin/test/Test.kt + |package example + | /** + | * @usesMathJax + | * + | * \(\alpha_{out} = \alpha_{dst}\) + | * \(C_{out} = C_{dst}\) + | */ + | fun test(): String = "" + """.trimIndent() + val writerPlugin = TestOutputWriterPlugin() + testInline( + source, + configuration, + pluginOverrides = listOf(writerPlugin, MathjaxPlugin()) + ) { + renderingStage = { + _, _ -> Jsoup + .parse(writerPlugin.writer.contents["root/example/test.html"]) + .head() + .select("link, script") + .let { + assert(it.`is`("[href=$LIB_PATH], [src=$LIB_PATH]")) + } + } + } + } +} diff --git a/plugins/xml/build.gradle.kts b/plugins/xml/build.gradle.kts new file mode 100644 index 00000000..5d3984bc --- /dev/null +++ b/plugins/xml/build.gradle.kts @@ -0,0 +1,3 @@ +import org.jetbrains.configurePublication + +configurePublication("xml-plugin")
\ No newline at end of file diff --git a/plugins/xml/src/main/kotlin/XmlPlugin.kt b/plugins/xml/src/main/kotlin/XmlPlugin.kt new file mode 100644 index 00000000..e19b76b3 --- /dev/null +++ b/plugins/xml/src/main/kotlin/XmlPlugin.kt @@ -0,0 +1,63 @@ +package org.jetbrains.dokka.xml + +import org.jetbrains.dokka.CoreExtensions +import org.jetbrains.dokka.model.Documentable +import org.jetbrains.dokka.model.dfs +import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.DokkaPlugin +import org.jetbrains.dokka.plugability.single +import org.jetbrains.dokka.transformers.descriptors.XMLMega +import org.jetbrains.dokka.transformers.pages.PageNodeTransformer + +class XmlPlugin : DokkaPlugin() { + val transformer by extending { + CoreExtensions.pageTransformer providing ::XmlTransformer + } +} + +class XmlTransformer(private val dokkaContext: DokkaContext) : PageNodeTransformer { + private val commentsToContentConverter by lazy { dokkaContext.single(CoreExtensions.commentsToContentConverter) } + + enum class XMLKind : Kind { + Main, XmlList + } + + override fun invoke(input: RootPageNode): RootPageNode = + input.transformPageNodeTree { if (it is ModulePageNode) transformModule(it) else it } + + private fun transformModule(module: ModulePageNode) = module.transformContentPagesTree { node -> + if (node !is ClasslikePageNode) node + else { + val refs = + node.documentable?.extra?.filterIsInstance<XMLMega>()?.filter { it.key == "@attr ref" } + .orEmpty() + val elementsToAdd = mutableListOf<Documentable>() + + refs.forEach { ref -> + module.documentable?.dfs { it.dri == ref.dri }?.let { elementsToAdd.add(it) } + } + val platformData = node.platforms().toSet() + val refTable = DefaultPageContentBuilder.group( + node.dri, + platformData, + XMLKind.XmlList, + commentsToContentConverter, + dokkaContext.logger + ) { + block("XML Attributes", 2, XMLKind.XmlList, elementsToAdd, platformData) { element -> + link(element.dri, XMLKind.XmlList) { + text(element.name ?: "<unnamed>", XMLKind.Main) + } + text(element.briefDocTagString, XMLKind.XmlList) + } + } + + val content = node.content as ContentGroup + val children = (node.content as ContentGroup).children + node.modified(content = content.copy(children = children + refTable)) + } + } + + private fun ContentPage.platforms() = this.content.platforms.toList() +}
\ No newline at end of file diff --git a/plugins/xml/src/main/kotlin/utils.kt b/plugins/xml/src/main/kotlin/utils.kt new file mode 100644 index 00000000..2f5f1893 --- /dev/null +++ b/plugins/xml/src/main/kotlin/utils.kt @@ -0,0 +1 @@ +package org.jetbrains.dokka.xml
\ No newline at end of file diff --git a/plugins/xml/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin b/plugins/xml/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin new file mode 100644 index 00000000..ebc3e551 --- /dev/null +++ b/plugins/xml/src/main/resources/META-INF/services/org.jetbrains.dokka.plugability.DokkaPlugin @@ -0,0 +1 @@ +org.jetbrains.dokka.xml.XmlPlugin diff --git a/runners/ant/build.gradle b/runners/ant/build.gradle deleted file mode 100644 index 216420c6..00000000 --- a/runners/ant/build.gradle +++ /dev/null @@ -1,18 +0,0 @@ -apply plugin: 'kotlin' - -sourceCompatibility = 1.8 - -tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { - kotlinOptions { - freeCompilerArgs += "-Xjsr305=strict" - languageVersion = language_version - apiVersion = language_version - jvmTarget = "1.8" - } -} - -dependencies { - compile project(":core") - compileOnly group: 'org.apache.ant', name: 'ant', version: ant_version -} - diff --git a/runners/ant/src/main/kotlin/ant/dokka.kt b/runners/ant/src/main/kotlin/ant/dokka.kt deleted file mode 100644 index 3ecc7b94..00000000 --- a/runners/ant/src/main/kotlin/ant/dokka.kt +++ /dev/null @@ -1,192 +0,0 @@ -package org.jetbrains.dokka.ant - -import org.apache.tools.ant.BuildException -import org.apache.tools.ant.Project -import org.apache.tools.ant.Task -import org.apache.tools.ant.types.Path -import org.apache.tools.ant.types.Reference -import org.jetbrains.dokka.* -import org.jetbrains.dokka.DokkaConfiguration.ExternalDocumentationLink -import java.io.File - -class AntLogger(val task: Task): DokkaLogger { - override fun info(message: String) = task.log(message, Project.MSG_INFO) - override fun warn(message: String) = task.log(message, Project.MSG_WARN) - override fun error(message: String) = task.log(message, Project.MSG_ERR) -} - -class AntSourceLinkDefinition(var path: String? = null, var url: String? = null, var lineSuffix: String? = null) - -class AntSourceRoot(var path: String? = null) { - fun toSourceRoot(): SourceRootImpl? = path?.let { path -> - SourceRootImpl(path) - } -} - -class TextProperty(var value: String = "") - -class AntPassConfig(task: Task) : DokkaConfiguration.PassConfiguration { - override var moduleName: String = "" - override val classpath: List<String> - get() = buildClassPath.list().toList() - - override val sourceRoots: List<DokkaConfiguration.SourceRoot> - get() = sourcePath.list().map { SourceRootImpl(it) } + antSourceRoots.mapNotNull { it.toSourceRoot() } - - override val samples: List<String> - get() = samplesPath.list().toList() - override val includes: List<String> - get() = includesPath.list().toList() - override var includeNonPublic: Boolean = false - override var includeRootPackage: Boolean = true - override var reportUndocumented: Boolean = false - override var skipEmptyPackages: Boolean = true - override var skipDeprecated: Boolean = false - override var jdkVersion: Int = 6 - override val sourceLinks: List<DokkaConfiguration.SourceLinkDefinition> - get() = antSourceLinkDefinition.map { - val path = it.path!! - val url = it.url!! - SourceLinkDefinitionImpl(File(path).canonicalFile.absolutePath, url, it.lineSuffix) - } - override val perPackageOptions: MutableList<DokkaConfiguration.PackageOptions> = mutableListOf() - override val externalDocumentationLinks: List<ExternalDocumentationLink> - get() = buildExternalLinksBuilders.map { it.build() } + defaultExternalDocumentationLinks - - override var languageVersion: String? = null - override var apiVersion: String? = null - override var noStdlibLink: Boolean = false - override var noJdkLink: Boolean = false - override var suppressedFiles: MutableList<String> = mutableListOf() - override var collectInheritedExtensionsFromLibraries: Boolean = false - override var analysisPlatform: Platform = Platform.DEFAULT - override var targets: List<String> = listOf() - get() = buildTargets.filter { it.value != "" } - .map { it.value } - - override var sinceKotlin: String? = null - - private val samplesPath: Path by lazy { Path(task.project) } - private val includesPath: Path by lazy { Path(task.project) } - private val buildClassPath: Path by lazy { Path(task.project) } - val sourcePath: Path by lazy { Path(task.project) } - val antSourceRoots: MutableList<AntSourceRoot> = mutableListOf() - - private val buildTargets: MutableList<TextProperty> = mutableListOf() - private val buildExternalLinksBuilders: MutableList<ExternalDocumentationLink.Builder> = mutableListOf() - val antSourceLinkDefinition: MutableList<AntSourceLinkDefinition> = mutableListOf() - - private val defaultExternalDocumentationLinks: List<DokkaConfiguration.ExternalDocumentationLink> - get() { - val links = mutableListOf<DokkaConfiguration.ExternalDocumentationLink>() - if (!noJdkLink) - links += DokkaConfiguration.ExternalDocumentationLink.Builder("https://docs.oracle.com/javase/$jdkVersion/docs/api/").build() - - if (!noStdlibLink) - links += DokkaConfiguration.ExternalDocumentationLink.Builder("https://kotlinlang.org/api/latest/jvm/stdlib/").build() - return links - } - - - fun setSamples(ref: Path) { - samplesPath.append(ref) - } - - fun setSamplesRef(ref: Reference) { - samplesPath.createPath().refid = ref - } - - fun setInclude(ref: Path) { - includesPath.append(ref) - } - - fun setClasspath(classpath: Path) { - buildClassPath.append(classpath) - } - - fun createPackageOptions(): AntPackageOptions = AntPackageOptions().apply { perPackageOptions.add(this) } - - fun createSourceRoot(): AntSourceRoot = AntSourceRoot().apply { antSourceRoots.add(this) } - - fun createTarget(): TextProperty = TextProperty().apply { - buildTargets.add(this) - } - - fun setClasspathRef(ref: Reference) { - buildClassPath.createPath().refid = ref - } - - fun setSrc(src: Path) { - sourcePath.append(src) - } - - fun setSrcRef(ref: Reference) { - sourcePath.createPath().refid = ref - } - - fun createSourceLink(): AntSourceLinkDefinition { - val def = AntSourceLinkDefinition() - antSourceLinkDefinition.add(def) - return def - } - - fun createExternalDocumentationLink() = - ExternalDocumentationLink.Builder().apply { buildExternalLinksBuilders.add(this) } - -} - -class AntPackageOptions( - override var prefix: String = "", - override var includeNonPublic: Boolean = false, - override var reportUndocumented: Boolean = true, - override var skipDeprecated: Boolean = false, - override var suppress: Boolean = false) : DokkaConfiguration.PackageOptions - -class DokkaAntTask: Task(), DokkaConfiguration { - - override var format: String = "html" - override var generateIndexPages: Boolean = false - override var outputDir: String = "" - override var impliedPlatforms: List<String> = listOf() - get() = buildImpliedPlatforms.map { it.value }.toList() - private val buildImpliedPlatforms: MutableList<TextProperty> = mutableListOf() - - override var cacheRoot: String? = null - override val passesConfigurations: MutableList<AntPassConfig> = mutableListOf() - - fun createPassConfig() = AntPassConfig(this).apply { passesConfigurations.add(this) } - fun createImpliedPlatform(): TextProperty = TextProperty().apply { buildImpliedPlatforms.add(this) } - - - override fun execute() { - for (passConfig in passesConfigurations) { - if (passConfig.sourcePath.list().isEmpty() && passConfig.antSourceRoots.isEmpty()) { - throw BuildException("At least one source path needs to be specified") - } - - if (passConfig.moduleName == "") { - throw BuildException("Module name needs to be specified and not empty") - } - - for (sourceLink in passConfig.antSourceLinkDefinition) { - if (sourceLink.path == null) { - throw BuildException("'path' attribute of a <sourceLink> element is required") - } - if (sourceLink.path!!.contains("\\")) { - throw BuildException("'dir' attribute of a <sourceLink> - incorrect value, only Unix based path allowed") - } - - if (sourceLink.url == null) { - throw BuildException("'url' attribute of a <sourceLink> element is required") - } - } - } - - if (outputDir == "") { - throw BuildException("Output directory needs to be specified and not empty") - } - - val generator = DokkaGenerator(this, AntLogger(this)) - generator.generate() - } -}
\ No newline at end of file diff --git a/runners/ant/src/main/resources/dokka-antlib.xml b/runners/ant/src/main/resources/dokka-antlib.xml deleted file mode 100644 index 9c3373d5..00000000 --- a/runners/ant/src/main/resources/dokka-antlib.xml +++ /dev/null @@ -1,3 +0,0 @@ -<antlib> - <taskdef name="dokka" classname="org.jetbrains.dokka.ant.DokkaAntTask"/> -</antlib> diff --git a/runners/build.gradle b/runners/build.gradle deleted file mode 100644 index 23d232d2..00000000 --- a/runners/build.gradle +++ /dev/null @@ -1,7 +0,0 @@ -subprojects { - buildscript { - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } - } -}
\ No newline at end of file diff --git a/runners/build.gradle.kts b/runners/build.gradle.kts new file mode 100644 index 00000000..57081149 --- /dev/null +++ b/runners/build.gradle.kts @@ -0,0 +1,6 @@ +subprojects { + apply { + plugin("maven-publish") + plugin("com.jfrog.bintray") + } +}
\ No newline at end of file diff --git a/runners/cli/build.gradle b/runners/cli/build.gradle deleted file mode 100644 index 24db0b1e..00000000 --- a/runners/cli/build.gradle +++ /dev/null @@ -1,16 +0,0 @@ -apply plugin: 'kotlin' - -sourceCompatibility = 1.8 - -tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { - kotlinOptions { - languageVersion = language_version - apiVersion = language_version - jvmTarget = "1.8" - } -} - -dependencies { - implementation "org.jetbrains.kotlinx:kotlinx-cli-jvm:0.1.0-dev-3" - implementation project(":core") -} diff --git a/runners/cli/build.gradle.kts b/runners/cli/build.gradle.kts new file mode 100644 index 00000000..4ad34192 --- /dev/null +++ b/runners/cli/build.gradle.kts @@ -0,0 +1,34 @@ +import org.jetbrains.DokkaPublicationBuilder.Component.Shadow +import org.jetbrains.registerDokkaArtifactPublication + +plugins { + id("com.github.johnrengelman.shadow") + id("com.jfrog.bintray") +} + +repositories { + maven(url = "https://dl.bintray.com/kotlin/kotlinx") +} + +dependencies { + implementation("org.jetbrains.kotlinx:kotlinx-cli-jvm:0.2.1") + implementation(project(":core")) + implementation(kotlin("stdlib")) +} + +tasks { + shadowJar { + val dokka_version: String by project + archiveFileName.set("dokka-cli-$dokka_version.jar") + archiveClassifier.set("") + manifest { + attributes("Main-Class" to "org.jetbrains.dokka.MainKt") + } + } +} + +registerDokkaArtifactPublication("dokkaCli"){ + artifactId = "dokka-cli" + component = Shadow +} + diff --git a/runners/cli/src/main/kotlin/cli/DokkaArgumentsParser.kt b/runners/cli/src/main/kotlin/cli/DokkaArgumentsParser.kt deleted file mode 100644 index 5d795da7..00000000 --- a/runners/cli/src/main/kotlin/cli/DokkaArgumentsParser.kt +++ /dev/null @@ -1,185 +0,0 @@ -package org.jetbrains.dokka - -import kotlinx.cli.* -import kotlin.reflect.KProperty - -class ParseContext(val cli: CommandLineInterface = CommandLineInterface("dokka")) { - private val transformActions = mutableMapOf<KProperty<*>, (String) -> Unit>() - private val flagActions = mutableMapOf<KProperty<*>, () -> Unit>() - - fun registerFlagAction( - keys: List<String>, - help: String, - property: KProperty<*>, - invoke: () -> Unit - ) { - if (property !in flagActions.keys) { - cli.flagAction(keys, help) { - flagActions[property]!!() - } - } - flagActions[property] = invoke - - } - - fun registerSingleOption( - keys: List<String>, - help: String, - property: KProperty<*>, - invoke: (String) -> Unit - ) { - if (property !in transformActions.keys) { - cli.singleAction(keys, help) { - transformActions[property]!!(it) - } - } - transformActions[property] = invoke - } - - fun registerRepeatableOption( - keys: List<String>, - help: String, - property: KProperty<*>, - invoke: (String) -> Unit - ) { - if (property !in transformActions.keys) { - cli.repeatingAction(keys, help) { - transformActions[property]!!(it) - } - } - transformActions[property] = invoke - } - - fun parse(args: Array<String>) { - cli.parseArgs(*args) - } - -} - -fun CommandLineInterface.singleAction( - keys: List<String>, - help: String, - invoke: (String) -> Unit -) = registerAction( - object : FlagActionBase(keys, help) { - override fun invoke(arguments: ListIterator<String>) { - if (arguments.hasNext()) { - val msg = arguments.next() - invoke(msg) - } - } - - override fun invoke() { - error("should be never called") - } - } -) - -fun CommandLineInterface.repeatingAction( - keys: List<String>, - help: String, - invoke: (String) -> Unit -) = registerAction( - object : FlagActionBase(keys, help) { - override fun invoke(arguments: ListIterator<String>) { - while (arguments.hasNext()) { - val message = arguments.next() - - if (this@repeatingAction.getFlagAction(message) != null) { - arguments.previous() - break - } - invoke(message) - } - } - - override fun invoke() { - error("should be never called") - } - } - -) - - -class DokkaArgumentsParser(val args: Array<String>, val parseContext: ParseContext) { - class OptionDelegate<T>( - var value: T, - private val action: (delegate: OptionDelegate<T>, property: KProperty<*>) -> Unit - ) { - operator fun getValue(thisRef: Any?, property: KProperty<*>): T = value - operator fun provideDelegate(thisRef: Any, property: KProperty<*>): OptionDelegate<T> { - action(this, property) - return this - } - } - - fun <T> parseInto(dest: T): T { - // TODO: constructor: (DokkaArgumentsParser) -> T - parseContext.parse(args) - return dest - } - - fun <T> repeatableOption( - keys: List<String>, - help: String, - transform: (String) -> T - ) = OptionDelegate(mutableListOf<T>()) { delegate, property -> - parseContext.registerRepeatableOption(keys, help, property) { - delegate.value.add(transform(it)) - } - } - - fun <T : String?> repeatableOption( - keys: List<String>, - help: String - ) = repeatableOption(keys, help) { it as T } - - fun <T> repeatableFlag( - keys: List<String>, - help: String, - initElement: (ParseContext) -> T - ) = OptionDelegate(mutableListOf<T>()) { delegate, property -> - parseContext.registerFlagAction(keys, help, property) { - delegate.value.add(initElement(parseContext)) - } - } - - fun <T> singleFlag( - keys: List<String>, - help: String, - initElement: (ParseContext) -> T, - transform: () -> T - ) = OptionDelegate(initElement(parseContext)) { delegate, property -> - parseContext.registerFlagAction(keys, help, property) { - delegate.value = transform() - } - } - - fun singleFlag( - keys: List<String>, - help: String - ) = singleFlag(keys, help, { false }, { true }) - - fun <T : String?> stringOption( - keys: List<String>, - help: String, - defaultValue: T - ) = singleOption(keys, help, { it as T }, { defaultValue }) - - fun <T> singleOption( - keys: List<String>, - help: String, - transform: (String) -> T, - initElement: (ParseContext) -> T - ) = OptionDelegate(initElement(parseContext)) { delegate, property -> - parseContext.registerSingleOption(keys, help, property) { - val toAdd = transform(it) - delegate.value = toAdd - } - } -} - - -//`(-perPackage fqName [-include-non-public] [...other flags])*` (edited) -//`(-sourceLink dir url [-urlSuffix value])*` -//`(-extLink url [packageListUrl])*`
\ No newline at end of file diff --git a/runners/cli/src/main/kotlin/cli/main.kt b/runners/cli/src/main/kotlin/cli/main.kt index 55601b21..989a45a1 100644 --- a/runners/cli/src/main/kotlin/cli/main.kt +++ b/runners/cli/src/main/kotlin/cli/main.kt @@ -1,278 +1,350 @@ package org.jetbrains.dokka +import kotlinx.cli.* import org.jetbrains.dokka.DokkaConfiguration.ExternalDocumentationLink -import org.jetbrains.dokka.Utilities.defaultLinks -import java.io.File +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet.* +import org.jetbrains.dokka.utilities.DokkaConsoleLogger +import org.jetbrains.dokka.utilities.cast +import java.io.* import java.net.MalformedURLException import java.net.URL -import java.net.URLClassLoader - -open class GlobalArguments(parser: DokkaArgumentsParser) : DokkaConfiguration { - override val outputDir: String by parser.stringOption( - listOf("-output"), - "Output directory path", - "") - - override val format: String by parser.stringOption( - listOf("-format"), - "Output format (text, html, markdown, jekyll, kotlin-website)", - "") - - override val generateIndexPages: Boolean by parser.singleFlag( - listOf("-generateIndexPages"), - "Generate index page" - ) +import java.nio.file.Files +import java.nio.file.Paths - override val cacheRoot: String? by parser.stringOption( - listOf("-cacheRoot"), - "Path to cache folder, or 'default' to use ~/.cache/dokka, if not provided caching is disabled", - null) +class GlobalArguments(args: Array<String>) : DokkaConfiguration { - override val impliedPlatforms: List<String> = emptyList() + val parser = ArgParser("globalArguments", prefixStyle = ArgParser.OptionPrefixStyle.JVM) - override val passesConfigurations: List<Arguments> by parser.repeatableFlag( - listOf("-pass"), - "Single dokka pass" - ) { - Arguments(parser) - } -} + val json: String? by parser.argument(ArgType.String, description = "Json file name").optional() -class Arguments(val parser: DokkaArgumentsParser) : DokkaConfiguration.PassConfiguration { - override val moduleName: String by parser.stringOption( - listOf("-module"), - "Name of the documentation module", - "") + override val outputDir by parser.option(ArgType.String, description = "Output directory path") + .default(DokkaDefaults.outputDir) - override val classpath: List<String> by parser.repeatableOption( - listOf("-classpath"), - "Classpath for symbol resolution" + override val cacheRoot by parser.option( + ArgType.String, + description = "Path to cache folder, or 'default' to use ~/.cache/dokka, if not provided caching is disabled" ) - override val sourceRoots: List<DokkaConfiguration.SourceRoot> by parser.repeatableOption( - listOf("-src"), - "Source file or directory (allows many paths separated by the system path separator)" - ) { SourceRootImpl(it) } - - override val samples: List<String> by parser.repeatableOption( - listOf("-sample"), - "Source root for samples" + override val sourceSets by parser.option( + ArgTypeArgument, + description = "Single dokka source set", + fullName = "sourceSet" + ).multiple() + + override val pluginsConfiguration by parser.option( + ArgTypePlugin, + description = "Configuration for plugins in format fqPluginName=json^^fqPluginName=json..." + ).default(emptyMap()) + + override val pluginsClasspath by parser.option( + ArgTypeFile, + description = "List of jars with dokka plugins (allows many paths separated by the semicolon `;`)" + ).delimiter(";") + + + override val offlineMode by parser.option( + ArgType.Boolean, + "Offline mode (do not download package lists from the Internet)" + ).default(DokkaDefaults.offlineMode) + + override val failOnWarning by parser.option( + ArgType.Boolean, + "Throw an exception if the generation exited with warnings" + ).default(DokkaDefaults.failOnWarning) + + val globalPackageOptions by parser.option( + ArgType.String, + description = "List of package source sets in format \"prefix,-deprecated,-privateApi,+warnUndocumented,+suppress;...\" " + ).delimiter(";") + + val globalLinks by parser.option( + ArgType.String, + description = "External documentation links in format url^packageListUrl^^url2..." + ).delimiter("^^") + + val globalSrcLink by parser.option( + ArgType.String, + description = "Mapping between a source directory and a Web site for browsing the code (allows many paths separated by the semicolon `;`)" + ).delimiter(";") + + val helpSourceSet by parser.option( + ArgTypeHelpSourceSet, + description = "Prints help for single -sourceSet" ) - override val includes: List<String> by parser.repeatableOption( - listOf("-include"), - "Markdown files to load (allows many paths separated by the system path separator)" - ) - - override val includeNonPublic: Boolean by parser.singleFlag( - listOf("-includeNonPublic"), - "Include non public") - - override val includeRootPackage: Boolean by parser.singleFlag( - listOf("-includeRootPackage"), - "Include root package") + override val modules: List<DokkaConfiguration.DokkaModuleDescription> = emptyList() - override val reportUndocumented: Boolean by parser.singleFlag( - listOf("-reportUndocumented"), - "Report undocumented members") + init { + parser.parse(args) - override val skipEmptyPackages: Boolean by parser.singleFlag( - listOf("-skipEmptyPackages"), - "Do not create index pages for empty packages") - - override val skipDeprecated: Boolean by parser.singleFlag( - listOf("-skipDeprecated"), - "Do not output deprecated members") - - override val jdkVersion: Int by parser.singleOption( - listOf("-jdkVersion"), - "Version of JDK to use for linking to JDK JavaDoc", - { it.toInt() }, - { 6 } - ) - - override val languageVersion: String? by parser.stringOption( - listOf("-languageVersion"), - "Language Version to pass to Kotlin Analysis", - null) - - override val apiVersion: String? by parser.stringOption( - listOf("-apiVersion"), - "Kotlin Api Version to pass to Kotlin Analysis", - null - ) + sourceSets.all { + it.perPackageOptions.cast<MutableList<DokkaConfiguration.PackageOptions>>() + .addAll(parsePerPackageOptions(globalPackageOptions)) + } - override val noStdlibLink: Boolean by parser.singleFlag( - listOf("-noStdlibLink"), - "Disable documentation link to stdlib") + sourceSets.all { + it.externalDocumentationLinks.cast<MutableList<ExternalDocumentationLink>>().addAll(parseLinks(globalLinks)) + } - override val noJdkLink: Boolean by parser.singleFlag( - listOf("-noJdkLink"), - "Disable documentation link to JDK") + globalSrcLink.forEach { + if (it.isNotEmpty() && it.contains("=")) + sourceSets.all { sourceSet -> + sourceSet.sourceLinks.cast<MutableList<SourceLinkDefinitionImpl>>() + .add(SourceLinkDefinitionImpl.parseSourceLinkDefinition(it)) + } + else { + DokkaConsoleLogger.warn("Invalid -srcLink syntax. Expected: <path>=<url>[#lineSuffix]. No source links will be generated.") + } + } - override val suppressedFiles: List<String> by parser.repeatableOption( - listOf("-suppressedFile"), - "" - ) + sourceSets.forEach { + it.externalDocumentationLinks.cast<MutableList<ExternalDocumentationLink>>().addAll(defaultLinks(it)) + it.externalDocumentationLinks.cast<MutableList<ExternalDocumentationLink>>().replaceAll { link -> + ExternalDocumentationLink.Builder(link.url, link.packageListUrl).build() + } + } + } +} - override val sinceKotlin: String? by parser.stringOption( - listOf("-sinceKotlin"), - "Kotlin Api version to use as base version, if none specified", - null - ) +private fun parseSourceSet(args: Array<String>): DokkaConfiguration.DokkaSourceSet { - override val collectInheritedExtensionsFromLibraries: Boolean by parser.singleFlag( - listOf("-collectInheritedExtensionsFromLibraries"), - "Search for applicable extensions in libraries") + val parser = ArgParser("sourceSet", prefixStyle = ArgParser.OptionPrefixStyle.JVM) - override val analysisPlatform: Platform by parser.singleOption( - listOf("-analysisPlatform"), - "Platform for analysis", - { Platform.fromString(it) }, - { Platform.DEFAULT } - ) + val moduleName by parser.option( + ArgType.String, + description = "Name of the documentation module", + fullName = "moduleName" + ).required() - override val targets: List<String> by parser.repeatableOption( - listOf("-target"), - "Generation targets" + val moduleDisplayName by parser.option( + ArgType.String, + description = "Name of the documentation module" ) - override val perPackageOptions: MutableList<DokkaConfiguration.PackageOptions> by parser.singleOption( - listOf("-packageOptions"), - "List of package passConfiguration in format \"prefix,-deprecated,-privateApi,+warnUndocumented,+suppress;...\" ", - { parsePerPackageOptions(it).toMutableList() }, - { mutableListOf() } + val sourceSetName by parser.option( + ArgType.String, + description = "Name of the source set" + ).default("main") + + val displayName by parser.option( + ArgType.String, + description = "Displayed name of the source set" + ).default("JVM") + + val classpath by parser.option( + ArgType.String, + description = "Classpath for symbol resolution (allows many paths separated by the semicolon `;`)" + ).delimiter(";") + + val sourceRoots by parser.option( + ArgType.String, + description = "Source file or directory (allows many paths separated by the semicolon `;`)", + fullName = "src" + ).delimiter(";") + + val dependentSourceSets by parser.option( + ArgType.String, + description = "Names of dependent source sets in format \"moduleName/sourceSetName\" (allows many paths separated by the semicolon `;`)" + ).delimiter(";") + + val samples by parser.option( + ArgType.String, + description = "Source root for samples (allows many paths separated by the semicolon `;`)" + ).delimiter(";") + + val includes by parser.option( + ArgType.String, + description = "Markdown files to load (allows many paths separated by the semicolon `;`)" + ).delimiter(";") + + val includeNonPublic: Boolean by parser.option(ArgType.Boolean, description = "Include non public") + .default(DokkaDefaults.includeNonPublic) + + val includeRootPackage by parser.option(ArgType.Boolean, description = "Include root package") + .default(DokkaDefaults.includeRootPackage) + + val reportUndocumented by parser.option(ArgType.Boolean, description = "Report undocumented members") + .default(DokkaDefaults.reportUndocumented) + + val skipEmptyPackages by parser.option( + ArgType.Boolean, + description = "Do not create index pages for empty packages" + ).default(DokkaDefaults.skipEmptyPackages) + + val skipDeprecated by parser.option(ArgType.Boolean, description = "Do not output deprecated members") + .default(DokkaDefaults.skipDeprecated) + + val jdkVersion by parser.option( + ArgType.Int, + description = "Version of JDK to use for linking to JDK JavaDoc" + ).default(DokkaDefaults.jdkVersion) + + val languageVersion by parser.option( + ArgType.String, + description = "Language Version to pass to Kotlin analysis" ) - override val externalDocumentationLinks: MutableList<DokkaConfiguration.ExternalDocumentationLink> by parser.singleOption( - listOf("-links"), - "External documentation links in format url^packageListUrl^^url2...", - { MainKt.parseLinks(it).toMutableList() }, - { mutableListOf() } + val apiVersion by parser.option( + ArgType.String, + description = "Kotlin Api Version to pass to Kotlin analysis" ) - override val sourceLinks: MutableList<DokkaConfiguration.SourceLinkDefinition> by parser.repeatableOption( - listOf("-srcLink"), - "Mapping between a source directory and a Web site for browsing the code" - ) { - if (it.isNotEmpty() && it.contains("=")) - SourceLinkDefinitionImpl.parseSourceLinkDefinition(it) - else { - throw IllegalArgumentException("Warning: Invalid -srcLink syntax. Expected: <path>=<url>[#lineSuffix]. No source links will be generated.") - } + val noStdlibLink by parser.option(ArgType.Boolean, description = "Disable documentation link to stdlib") + .default(DokkaDefaults.noStdlibLink) + + val noJdkLink by parser.option(ArgType.Boolean, description = "Disable documentation link to JDK") + .default(DokkaDefaults.noJdkLink) + + val suppressedFiles by parser.option( + ArgType.String, + description = "Paths to files to be suppressed (allows many paths separated by the semicolon `;`)" + ).delimiter(";") + + val analysisPlatform: Platform by parser.option( + ArgTypePlatform, + description = "Platform for analysis" + ).default(DokkaDefaults.analysisPlatform) + + val perPackageOptions by parser.option( + ArgType.String, + description = "List of package source set configuration in format \"prefix,-deprecated,-privateApi,+warnUndocumented,+suppress;...\" " + ).delimiter(";") + + val externalDocumentationLinks by parser.option( + ArgType.String, + description = "External documentation links in format url^packageListUrl^^url2..." + ).delimiter("^^") + + val sourceLinks by parser.option( + ArgTypeSourceLinkDefinition, + description = "Mapping between a source directory and a Web site for browsing the code (allows many paths separated by the semicolon `;`)", + fullName = "srcLink" + ).delimiter(";") + + parser.parse(args) + + return object : DokkaConfiguration.DokkaSourceSet { + override val moduleDisplayName = moduleDisplayName ?: moduleName + override val displayName = displayName + override val sourceSetID = DokkaSourceSetID(moduleName, sourceSetName) + override val classpath = classpath + override val sourceRoots = sourceRoots.map { SourceRootImpl(it.toAbsolutePath()) } + override val dependentSourceSets: Set<DokkaSourceSetID> = dependentSourceSets + .map { dependentSourceSetName -> dependentSourceSetName.split('/').let { DokkaSourceSetID(it[0], it[1]) } } + .toSet() + override val samples = samples.map { it.toAbsolutePath() } + override val includes = includes.map { it.toAbsolutePath() } + override val includeNonPublic = includeNonPublic + override val includeRootPackage = includeRootPackage + override val reportUndocumented = reportUndocumented + override val skipEmptyPackages = skipEmptyPackages + override val skipDeprecated = skipDeprecated + override val jdkVersion = jdkVersion + override val sourceLinks = sourceLinks + override val analysisPlatform = analysisPlatform + override val perPackageOptions = parsePerPackageOptions(perPackageOptions) + override val externalDocumentationLinks = parseLinks(externalDocumentationLinks) + override val languageVersion = languageVersion + override val apiVersion = apiVersion + override val noStdlibLink = noStdlibLink + override val noJdkLink = noJdkLink + override val suppressedFiles = suppressedFiles } } -object MainKt { - fun parseLinks(links: String): List<ExternalDocumentationLink> { - val (parsedLinks, parsedOfflineLinks) = links.split("^^") - .map { it.split("^").map { it.trim() }.filter { it.isNotBlank() } } - .filter { it.isNotEmpty() } - .partition { it.size == 1 } - - return parsedLinks.map { (root) -> ExternalDocumentationLink.Builder(root).build() } + - parsedOfflineLinks.map { (root, packageList) -> - val rootUrl = URL(root) - val packageListUrl = - try { - URL(packageList) - } catch (ex: MalformedURLException) { - File(packageList).toURI().toURL() - } - ExternalDocumentationLink.Builder(rootUrl, packageListUrl).build() - } - } +object ArgTypeFile : ArgType<File>(true) { + override fun convert(value: kotlin.String, name: kotlin.String): File = File(value) + override val description: kotlin.String + get() = "{ String that points to file path }" +} - @JvmStatic - fun entry(configuration: DokkaConfiguration) { - val generator = DokkaGenerator(configuration, DokkaConsoleLogger) - generator.generate() - DokkaConsoleLogger.report() - } +object ArgTypePlatform : ArgType<Platform>(true) { + override fun convert(value: kotlin.String, name: kotlin.String): Platform = Platform.fromString(value) + override val description: kotlin.String + get() = "{ String thar represents paltform }" +} - fun findToolsJar(): File { - val javaHome = System.getProperty("java.home") - val default = File(javaHome, "../lib/tools.jar") - val mac = File(javaHome, "../Classes/classes.jar") - when { - default.exists() -> return default - mac.exists() -> return mac - else -> { - throw Exception("tools.jar not found, please check it, also you can provide it manually, using -cp") +object ArgTypePlugin : ArgType<Map<String, String>>(true) { + override fun convert(value: kotlin.String, name: kotlin.String): Map<kotlin.String, kotlin.String> = + value.split("^^").map { + it.split("=").let { + it[0] to it[1] } - } - } + }.toMap() - fun createClassLoaderWithTools(): ClassLoader { - val toolsJar = findToolsJar().canonicalFile.toURI().toURL() - val originalUrls = (javaClass.classLoader as? URLClassLoader)?.urLs - val dokkaJar = javaClass.protectionDomain.codeSource.location - val urls = if (originalUrls != null) arrayOf(toolsJar, *originalUrls) else arrayOf(toolsJar, dokkaJar) - return URLClassLoader(urls, ClassLoader.getSystemClassLoader().parent) - } + override val description: kotlin.String + get() = "{ String fqName=json, remember to escape `\"` inside json }" +} - fun startWithToolsJar(configuration: DokkaConfiguration) { - try { - javaClass.classLoader.loadClass("com.sun.tools.doclets.formats.html.HtmlDoclet") - entry(configuration) - } catch (e: ClassNotFoundException) { - val classLoader = createClassLoaderWithTools() - classLoader.loadClass("org.jetbrains.dokka.MainKt") - .methods.find { it.name == "entry" }!! - .invoke(null, configuration) +object ArgTypeSourceLinkDefinition : ArgType<DokkaConfiguration.SourceLinkDefinition>(true) { + override fun convert(value: kotlin.String, name: kotlin.String): DokkaConfiguration.SourceLinkDefinition = + if (value.isNotEmpty() && value.contains("=")) + SourceLinkDefinitionImpl.parseSourceLinkDefinition(value) + else { + throw IllegalArgumentException("Warning: Invalid -srcLink syntax. Expected: <path>=<url>[#lineSuffix]. No source links will be generated.") } - } - fun createConfiguration(args: Array<String>): GlobalArguments { - val parseContext = ParseContext() - val parser = DokkaArgumentsParser(args, parseContext) - val configuration = GlobalArguments(parser) + override val description: kotlin.String + get() = "{ String that represent source links }" +} - parseContext.cli.singleAction( - listOf("-globalPackageOptions"), - "List of package passConfiguration in format \"prefix,-deprecated,-privateApi,+warnUndocumented,+suppress;...\" " - ) { link -> - configuration.passesConfigurations.all { it.perPackageOptions.addAll(parsePerPackageOptions(link)) } - } +object ArgTypeArgument : ArgType<DokkaConfiguration.DokkaSourceSet>(true) { + override fun convert(value: kotlin.String, name: kotlin.String): DokkaConfiguration.DokkaSourceSet = + parseSourceSet(value.split(" ").filter { it.isNotBlank() }.toTypedArray()) - parseContext.cli.singleAction( - listOf("-globalLinks"), - "External documentation links in format url^packageListUrl^^url2..." - ) { link -> - configuration.passesConfigurations.all { it.externalDocumentationLinks.addAll(parseLinks(link)) } - } + override val description: kotlin.String + get() = "" +} - parseContext.cli.repeatingAction( - listOf("-globalSrcLink"), - "Mapping between a source directory and a Web site for browsing the code" - ) { - val newSourceLinks = if (it.isNotEmpty() && it.contains("=")) - listOf(SourceLinkDefinitionImpl.parseSourceLinkDefinition(it)) - else { - if (it.isNotEmpty()) { - println("Warning: Invalid -srcLink syntax. Expected: <path>=<url>[#lineSuffix]. No source links will be generated.") - } - listOf() - } +// Workaround for printing nested parsers help +object ArgTypeHelpSourceSet : ArgType<Any>(false) { + override fun convert(value: kotlin.String, name: kotlin.String): Any = Any().also { parseSourceSet(arrayOf("-h")) } - configuration.passesConfigurations.all { it.sourceLinks.addAll(newSourceLinks) } - } + override val description: kotlin.String + get() = "" +} - parser.parseInto(configuration) - return configuration +fun defaultLinks(config: DokkaConfiguration.DokkaSourceSet): MutableList<ExternalDocumentationLink> = + mutableListOf<ExternalDocumentationLink>().apply { + if (!config.noJdkLink) + this += DokkaConfiguration.ExternalDocumentationLink + .Builder("https://docs.oracle.com/javase/${config.jdkVersion}/docs/api/") + .build() + + if (!config.noStdlibLink) + this += ExternalDocumentationLink + .Builder("https://kotlinlang.org/api/latest/jvm/stdlib/") + .build() } - fun GlobalArguments.addDefaultLinks() = passesConfigurations.forEach { it.externalDocumentationLinks += it.defaultLinks() } - - @JvmStatic - fun main(args: Array<String>) { - val configuration = createConfiguration(args).apply { addDefaultLinks() } - if (configuration.format.toLowerCase() == "javadoc") - startWithToolsJar(configuration) - else - entry(configuration) - } +private fun String.toAbsolutePath() = Paths.get(this).toAbsolutePath().toString() + +fun parseLinks(links: List<String>): List<ExternalDocumentationLink> { + val (parsedLinks, parsedOfflineLinks) = links + .map { it.split("^").map { it.trim() }.filter { it.isNotBlank() } } + .filter { it.isNotEmpty() } + .partition { it.size == 1 } + + return parsedLinks.map { (root) -> ExternalDocumentationLink.Builder(root).build() } + + parsedOfflineLinks.map { (root, packageList) -> + val rootUrl = URL(root) + val packageListUrl = + try { + URL(packageList) + } catch (ex: MalformedURLException) { + File(packageList).toURI().toURL() + } + ExternalDocumentationLink.Builder(rootUrl, packageListUrl).build() + } } - - +fun main(args: Array<String>) { + val globalArguments = GlobalArguments(args) + val configuration = if (globalArguments.json != null) + DokkaConfigurationImpl( + Paths.get(checkNotNull(globalArguments.json)).toFile().readText() + ) + else + globalArguments + DokkaGenerator(configuration, DokkaConsoleLogger).generate() +} diff --git a/runners/fatjar/build.gradle b/runners/fatjar/build.gradle deleted file mode 100644 index 4ce0416c..00000000 --- a/runners/fatjar/build.gradle +++ /dev/null @@ -1,50 +0,0 @@ -import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer -import org.jetbrains.PluginXmlTransformer - -apply plugin: 'java' -apply plugin: 'com.github.johnrengelman.shadow' - -dependencies { - compile project(":runners:cli") - compile project(":runners:ant") -} - -jar { - manifest { - attributes 'Main-Class': 'org.jetbrains.dokka.MainKt' - } -} - -shadowJar { - baseName = 'dokka-fatjar' - classifier = '' - - configurations { - exclude compileOnly - } - - transform(ServiceFileTransformer) - transform(PluginXmlTransformer) - - exclude 'colorScheme/**' - exclude 'fileTemplates/**' - exclude 'inspectionDescriptions/**' - exclude 'intentionDescriptions/**' - - exclude 'src/**' - - relocate('kotlin.reflect.full', 'kotlin.reflect') -} - -apply plugin: 'maven-publish' - -publishing { - publications { - dokkaFatJar(MavenPublication) { publication -> - artifactId = 'dokka-fatjar' - project.shadow.component(publication) - } - } -} - -bintrayPublication(project, ["dokkaFatJar"])
\ No newline at end of file diff --git a/runners/gradle-integration-tests/android-licenses/android-sdk-license b/runners/gradle-integration-tests/android-licenses/android-sdk-license deleted file mode 100644 index c311cf48..00000000 --- a/runners/gradle-integration-tests/android-licenses/android-sdk-license +++ /dev/null @@ -1,2 +0,0 @@ - -d56f5187479451eabf01fb78af6dfcb131a6481e
\ No newline at end of file diff --git a/runners/gradle-integration-tests/android-licenses/android-sdk-preview-license b/runners/gradle-integration-tests/android-licenses/android-sdk-preview-license deleted file mode 100644 index da4552d2..00000000 --- a/runners/gradle-integration-tests/android-licenses/android-sdk-preview-license +++ /dev/null @@ -1,2 +0,0 @@ - -84831b9409646a918e30573bab4c9c91346d8abd
\ No newline at end of file diff --git a/runners/gradle-integration-tests/build.gradle b/runners/gradle-integration-tests/build.gradle deleted file mode 100644 index 2430d46b..00000000 --- a/runners/gradle-integration-tests/build.gradle +++ /dev/null @@ -1,59 +0,0 @@ -apply plugin: 'kotlin' - -sourceCompatibility = 1.8 - -tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { - kotlinOptions { - freeCompilerArgs += "-Xjsr305=strict" - languageVersion = language_version - apiVersion = language_version - jvmTarget = "1.8" - } -} - -configurations { - dokkaPlugin - dokkaFatJar - kotlinGradle -} - -dependencies { - - testCompileOnly group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib', version: kotlin_for_gradle_runtime_version - testCompile group: 'org.jetbrains.kotlin', name: 'kotlin-test-junit', version: kotlin_for_gradle_runtime_version - testCompile ideaRT() - - dokkaPlugin project(path: ':runners:gradle-plugin', configuration: 'shadow') - dokkaFatJar project(path: ":runners:fatjar", configuration: 'shadow') - - kotlinGradle "org.jetbrains.kotlin:kotlin-gradle-plugin" - - testCompile group: 'junit', name: 'junit', version: '4.12' - testCompile gradleTestKit() -} - - - -task createClasspathManifest { - def outputDir = file("$buildDir/$name") - - inputs.files(configurations.dokkaPlugin + configurations.dokkaFatJar) - outputs.dir outputDir - - doLast { - outputDir.mkdirs() - file("$outputDir/dokka-plugin-classpath.txt").text = configurations.dokkaPlugin.join("\n") - file("$outputDir/fatjar.txt").text = configurations.dokkaFatJar.join("\n") - file("$outputDir/kotlin-gradle.txt").text = configurations.kotlinGradle.join("\n") - } -} - - -createClasspathManifest.mustRunAfter project(":runners:fatjar").shadowJar -testClasses.dependsOn project(":runners:fatjar").shadowJar -testClasses.dependsOn createClasspathManifest - -test { - systemProperty "android.licenses.overwrite", project.findProperty("android.licenses.overwrite") ?: "" - inputs.dir(file('testData')) -}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AbstractAndroidAppTest.kt b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AbstractAndroidAppTest.kt deleted file mode 100644 index c3fe2ea9..00000000 --- a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AbstractAndroidAppTest.kt +++ /dev/null @@ -1,51 +0,0 @@ -package org.jetbrains.dokka.gradle - -import org.gradle.testkit.runner.TaskOutcome -import kotlin.test.assertEquals - -abstract class AbstractAndroidAppTest(val testDataRootPath: String) : AbstractDokkaAndroidGradleTest() { - - fun prepareTestData() { - val testDataRoot = testDataFolder.resolve(testDataRootPath) - val tmpRoot = testProjectDir.root.toPath() - - testDataRoot.resolve("app").copy(tmpRoot.resolve("app")) - testDataRoot.resolve("build.gradle").copy(tmpRoot.resolve("build.gradle")) - testDataRoot.resolve("settings.gradle").copy(tmpRoot.resolve("settings.gradle")) - - androidLocalProperties?.copy(tmpRoot.resolve("local.properties")) - } - - - data class AndroidPluginParams(val pluginVersion: String, val buildToolsVersion: String, val compileSdk: Int) { - fun asArguments(): List<String> = listOf( - "-Pabt_plugin_version=$pluginVersion", - "-Pabt_version=$buildToolsVersion", - "-Psdk_version=$compileSdk" - ) - } - - - protected fun doTest(gradleVersion: String, kotlinVersion: String, androidPluginParams: AndroidPluginParams) { - prepareTestData() - - val result = configure(gradleVersion, kotlinVersion, - arguments = arrayOf("dokka", "--stacktrace") + androidPluginParams.asArguments()) - .build() - - println(result.output) - - assertEquals(TaskOutcome.SUCCESS, result.task(":app:dokka")?.outcome) - - val docsOutput = "app/build/dokka" - - checkOutputStructure("$testDataRootPath/fileTree.txt", docsOutput) - - checkNoErrorClasses(docsOutput) - checkNoUnresolvedLinks(docsOutput) - - checkExternalLink(docsOutput, "<span class=\"identifier\">Activity</span>", - """<a href="https://developer.android.com/reference/android/app/Activity.html"><span class="identifier">Activity</span></a>""") - } - -}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaAndroidGradleTest.kt b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaAndroidGradleTest.kt deleted file mode 100644 index 334fc7c8..00000000 --- a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaAndroidGradleTest.kt +++ /dev/null @@ -1,45 +0,0 @@ -package org.jetbrains.dokka.gradle - -import org.junit.BeforeClass -import java.io.File - -abstract class AbstractDokkaAndroidGradleTest : AbstractDokkaGradleTest() { - - override val pluginClasspath: List<File> = pluginClasspathData.toFile().readLines().map { File(it) } - - companion object { - - @JvmStatic - @BeforeClass - fun acceptAndroidSdkLicenses() { - val sdkDir = androidLocalProperties?.toFile()?.let { - val lines = it.readLines().map { it.trim() } - val sdkDirLine = lines.firstOrNull { "sdk.dir" in it } - sdkDirLine?.substringAfter("=")?.trim() - } ?: System.getenv("ANDROID_HOME") - - if (sdkDir == null || sdkDir.isEmpty()) { - error("Android SDK home not set, " + - "try setting \$ANDROID_HOME " + - "or sdk.dir in runners/gradle-integration-tests/testData/android.local.properties") - } - val sdkDirFile = File(sdkDir) - if (!sdkDirFile.exists()) error("\$ANDROID_HOME and android.local.properties points to non-existing location") - val sdkLicensesDir = sdkDirFile.resolve("licenses") - - val acceptedLicenses = File("android-licenses") - acceptedLicenses.listFiles().forEach { licenseFile -> - val target = sdkLicensesDir.resolve(licenseFile.name) - if(!target.exists() || target.readText() != licenseFile.readText()) { - val overwrite = System.getProperty("android.licenses.overwrite", "false")!!.toBoolean() - if (!target.exists() || overwrite) { - licenseFile.copyTo(target, true) - println("Accepted ${licenseFile.name}, by copying $licenseFile to $target") - } - } - - } - } - - } -}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaGradleTest.kt b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaGradleTest.kt deleted file mode 100644 index 4814e707..00000000 --- a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaGradleTest.kt +++ /dev/null @@ -1,108 +0,0 @@ -package org.jetbrains.dokka.gradle - - -import com.intellij.rt.execution.junit.FileComparisonFailure -import org.gradle.testkit.runner.GradleRunner -import org.junit.Rule -import org.junit.rules.TemporaryFolder -import java.io.File -import java.nio.file.Files -import java.nio.file.Path -import java.nio.file.Paths - - -val testDataFolder: Path = Paths.get("testData") - -val pluginClasspathData: Path = Paths.get("build", "createClasspathManifest", "dokka-plugin-classpath.txt") - -val dokkaFatJarPathData: Path = pluginClasspathData.resolveSibling("fatjar.txt") - -val androidLocalProperties = testDataFolder.resolve("android.local.properties").let { if (Files.exists(it)) it else null } - -abstract class AbstractDokkaGradleTest { - @get:Rule val testProjectDir = TemporaryFolder() - - open val pluginClasspath: List<File> = pluginClasspathData.toFile().readLines().map { File(it) } - - fun checkOutputStructure(expected: String, actualSubpath: String) { - val expectedPath = testDataFolder.resolve(expected) - val actualPath = testProjectDir.root.toPath().resolve(actualSubpath).normalize() - - assertEqualsIgnoringSeparators(expectedPath.toFile(), buildString { - actualPath.toFile().writeStructure(this, File(actualPath.toFile(), ".")) - }) - } - - fun checkNoErrorClasses(actualSubpath: String, extension: String = "html", errorClassMarker: String = "ERROR CLASS") { - val actualPath = testProjectDir.root.toPath().resolve(actualSubpath).normalize() - var checked = 0 - Files.walk(actualPath).filter { Files.isRegularFile(it) && it.fileName.toString().endsWith(".$extension") }.forEach { - val text = it.toFile().readText() - - val noErrorClasses = text.replace(errorClassMarker, "?!") - - if (noErrorClasses != text) { - throw FileComparisonFailure("", noErrorClasses, text, null) - } - - checked++ - } - println("$checked files checked for error classes") - } - - fun checkNoUnresolvedLinks(actualSubpath: String, extension: String = "html", marker: Regex = "[\"']#[\"']".toRegex()) { - val actualPath = testProjectDir.root.toPath().resolve(actualSubpath).normalize() - var checked = 0 - Files.walk(actualPath).filter { Files.isRegularFile(it) && it.fileName.toString().endsWith(".$extension") }.forEach { - val text = it.toFile().readText() - - val noErrorClasses = text.replace(marker, "?!") - - if (noErrorClasses != text) { - throw FileComparisonFailure("", noErrorClasses, text, null) - } - - checked++ - } - println("$checked files checked for unresolved links") - } - - fun checkExternalLink(actualSubpath: String, linkBody: String, fullLink: String, extension: String = "html") { - val match = "!!match!!" - val notMatch = "!!not-match!!" - - val actualPath = testProjectDir.root.toPath().resolve(actualSubpath).normalize() - var checked = 0 - var totalEntries = 0 - Files.walk(actualPath).filter { Files.isRegularFile(it) && it.fileName.toString().endsWith(".$extension") }.forEach { - val text = it.toFile().readText() - - val textWithoutMatches = text.replace(fullLink, match) - - val textWithoutNonMatches = textWithoutMatches.replace(linkBody, notMatch) - - if (textWithoutNonMatches != textWithoutMatches) { - - val expected = textWithoutNonMatches.replace(notMatch, fullLink).replace(match, fullLink) - val actual = textWithoutMatches.replace(match, fullLink) - - throw FileComparisonFailure("", expected, actual, null) - } - if (text != textWithoutMatches) - totalEntries++ - - checked++ - } - println("$checked files checked for valid external links '$linkBody', found $totalEntries links") - } - - fun configure(gradleVersion: String = "3.5", kotlinVersion: String = "1.1.2", arguments: Array<String>): GradleRunner { - val fatjar = dokkaFatJarPathData.toFile().readText() - - return GradleRunner.create().withProjectDir(testProjectDir.root) - .withArguments("-Pdokka_fatjar=$fatjar", "-Ptest_kotlin_version=$kotlinVersion", *arguments) - .withPluginClasspath(pluginClasspath) - .withGradleVersion(gradleVersion) - .withDebug(true) - } -}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AndroidAppTest.kt b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AndroidAppTest.kt deleted file mode 100644 index bbb63909..00000000 --- a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AndroidAppTest.kt +++ /dev/null @@ -1,25 +0,0 @@ -package org.jetbrains.dokka.gradle - -import org.junit.Test - -class AndroidAppTest : AbstractAndroidAppTest("androidApp") { - @Test - fun `test kotlin 1_1_2-5 and gradle 4_0 and abt 3_0_0-alpha3`() { - doTest("4.0", "1.1.2-5", AndroidPluginParams("3.0.0-alpha3", "25.0.2", 25)) - } - - @Test - fun `test kotlin 1_1_2 and gradle 3_5 and abt 2_3_0`() { - doTest("3.5", "1.1.2", AndroidPluginParams("2.3.0", "25.0.0", 24)) - } - - @Test - fun `test kotlin 1_0_7 and gradle 2_14_1 and abt 2_2_3`() { - doTest("2.14.1", "1.0.7", AndroidPluginParams("2.2.3", "25.0.0", 24)) - } - - @Test - fun `test kotlin 1_2_20 and gradle 4_5 and abt 3_0_1`() { - doTest("4.5", "1.2.20", AndroidPluginParams("3.0.1", "27.0.0", 27)) - } -}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AndroidLibDependsOnJavaLibTest.kt b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AndroidLibDependsOnJavaLibTest.kt deleted file mode 100644 index 9bc52273..00000000 --- a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AndroidLibDependsOnJavaLibTest.kt +++ /dev/null @@ -1,48 +0,0 @@ -package org.jetbrains.dokka.gradle - -import org.gradle.testkit.runner.TaskOutcome -import org.junit.Test -import kotlin.test.assertEquals - -class AndroidLibDependsOnJavaLibTest: AbstractDokkaAndroidGradleTest() { - - private val testDataRootPath = "androidLibDependsOnJavaLib" - - private fun prepareTestData() { - val testDataRoot = testDataFolder.resolve(testDataRootPath) - val tmpRoot = testProjectDir.root.toPath() - - testDataRoot.copy(tmpRoot) - - androidLocalProperties?.copy(tmpRoot.resolve("local.properties")) - } - - - private fun doTest(gradleVersion: String, kotlinVersion: String, androidPluginParams: AbstractAndroidAppTest.AndroidPluginParams) { - prepareTestData() - - val result = configure(gradleVersion, kotlinVersion, - arguments = arrayOf("dokka", "--stacktrace") + androidPluginParams.asArguments()) - .build() - - println(result.output) - - assertEquals(TaskOutcome.SUCCESS, result.task(":lib:dokka")?.outcome) - - val docsOutput = "lib/build/dokka" - - checkOutputStructure("$testDataRootPath/fileTree.txt", docsOutput) - - checkNoErrorClasses(docsOutput) - checkNoUnresolvedLinks(docsOutput) - - checkExternalLink(docsOutput, "<span class=\"identifier\">LibClz</span>", - """<a href="https://example.com/example/jlib/LibClz.html"><span class="identifier">LibClz</span></a>""") - } - - - @Test - fun `test kotlin 1_2_20 and gradle 4_5 and abt 3_0_1`() { - doTest("4.5", "1.2.20", AbstractAndroidAppTest.AndroidPluginParams("3.0.1", "27.0.0", 27)) - } -}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AndroidMultiFlavourAppTest.kt b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AndroidMultiFlavourAppTest.kt deleted file mode 100644 index ef1b94d8..00000000 --- a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/AndroidMultiFlavourAppTest.kt +++ /dev/null @@ -1,60 +0,0 @@ -package org.jetbrains.dokka.gradle - -import org.gradle.testkit.runner.TaskOutcome -import org.jetbrains.dokka.gradle.AbstractAndroidAppTest.AndroidPluginParams -import org.junit.Test -import kotlin.test.assertEquals - -class AndroidMultiFlavourAppTest : AbstractDokkaAndroidGradleTest() { - - fun prepareTestData(testDataRootPath: String) { - val testDataRoot = testDataFolder.resolve(testDataRootPath) - val tmpRoot = testProjectDir.root.toPath() - - testDataRoot.resolve("app").copy(tmpRoot.resolve("app")) - testDataRoot.resolve("build.gradle").copy(tmpRoot.resolve("build.gradle")) - testDataRoot.resolve("settings.gradle").copy(tmpRoot.resolve("settings.gradle")) - - androidLocalProperties?.copy(tmpRoot.resolve("local.properties")) - } - - private fun doTest(gradleVersion: String, kotlinVersion: String, androidPluginParams: AndroidPluginParams) { - prepareTestData("androidMultiFlavourApp") - - val result = configure(gradleVersion, kotlinVersion, - arguments = arrayOf("dokka", "dokkaFullFlavourOnly", "--stacktrace") + androidPluginParams.asArguments()) - .build() - - println(result.output) - - assertEquals(TaskOutcome.SUCCESS, result.task(":app:dokka")?.outcome) - assertEquals(TaskOutcome.SUCCESS, result.task(":app:dokkaFullFlavourOnly")?.outcome) - - val docsOutput = "app/build/dokka" - - checkOutputStructure("androidMultiFlavourApp/fileTree.txt", docsOutput) - - checkNoErrorClasses(docsOutput) - checkNoUnresolvedLinks(docsOutput) - - checkExternalLink(docsOutput, "<span class=\"identifier\">Activity</span>", - """<a href="https://developer.android.com/reference/android/app/Activity.html"><span class="identifier">Activity</span></a>""") - } - - @Test fun `test kotlin 1_1_2-5 and gradle 4_0 and abt 3_0_0-alpha3`() { - doTest("4.0", "1.1.2-5", AndroidPluginParams("3.0.0-alpha3", "25.0.2", 25)) - } - - @Test fun `test kotlin 1_1_2 and gradle 3_5 and abt 2_3_0`() { - doTest("3.5", "1.1.2", AndroidPluginParams("2.3.0", "25.0.0", 24)) - } - - @Test fun `test kotlin 1_0_7 and gradle 2_14_1 and abt 2_2_3`() { - doTest("2.14.1", "1.0.7", AndroidPluginParams("2.2.3", "25.0.0", 24)) - } - - @Test fun `test kotlin 1_2_20 and gradle 4_5 and abt 3_0_1`() { - doTest("4.5", "1.2.20", AndroidPluginParams("3.0.1", "27.0.0", 27)) - } - -}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/BasicTest.kt b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/BasicTest.kt deleted file mode 100644 index 2e1a0d41..00000000 --- a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/BasicTest.kt +++ /dev/null @@ -1,51 +0,0 @@ -package org.jetbrains.dokka.gradle - -import org.gradle.testkit.runner.TaskOutcome -import org.junit.Test -import kotlin.test.assertEquals - -class BasicTest : AbstractDokkaGradleTest() { - - fun prepareTestData(testDataRootPath: String) { - val testDataRoot = testDataFolder.resolve(testDataRootPath) - val tmpRoot = testProjectDir.root.toPath() - - testDataRoot.resolve("src").copy(tmpRoot.resolve("src")) - testDataRoot.resolve("classDir").copy(tmpRoot.resolve("classDir")) - testDataRoot.resolve("build.gradle").copy(tmpRoot.resolve("build.gradle")) - testDataRoot.resolve("settings.gradle").copy(tmpRoot.resolve("settings.gradle")) - } - - private fun doTest(gradleVersion: String, kotlinVersion: String) { - - prepareTestData("basic") - - val result = configure(gradleVersion, kotlinVersion, arguments = arrayOf("dokka", "--stacktrace")).build() - - println(result.output) - - assertEquals(TaskOutcome.SUCCESS, result.task(":dokka")?.outcome) - - val docsOutput = "build/dokka" - - checkOutputStructure("basic/fileTree.txt", docsOutput) - - checkNoErrorClasses(docsOutput) - checkNoUnresolvedLinks(docsOutput) - - checkExternalLink(docsOutput, "<span class=\"identifier\">String</span>", - """<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a>""") - } - - @Test fun `test kotlin 1_0_7 and gradle 2_14_1`() { - doTest("2.14.1", "1.0.7") - } - - @Test fun `test kotlin 1_1_2 and gradle 4_0`() { - doTest("4.0", "1.1.2") - } - - @Test fun `test kotlin 1_2_20 and gradle 4_5`() { - doTest("4.5", "1.2.20") - } -}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/JavadocRSuppressionTest.kt b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/JavadocRSuppressionTest.kt deleted file mode 100644 index 3a4d08b8..00000000 --- a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/JavadocRSuppressionTest.kt +++ /dev/null @@ -1,24 +0,0 @@ -package org.jetbrains.dokka.gradle - -import org.junit.Test - -class JavadocRSuppressionTest : AbstractAndroidAppTest("androidAppJavadoc") { - @Test - fun `test kotlin 1_1_2-5 and gradle 4_0 and abt 3_0_0-alpha3`() { - doTest("4.0", "1.1.2-5", AndroidPluginParams("3.0.0-alpha3", "25.0.2", 25)) - } - - @Test - fun `test kotlin 1_1_2 and gradle 3_5 and abt 2_3_0`() { - doTest("3.5", "1.1.2", AndroidPluginParams("2.3.0", "25.0.0", 24)) - } - - @Test - fun `test kotlin 1_0_7 and gradle 2_14_1 and abt 2_2_3`() { - doTest("2.14.1", "1.0.7", AndroidPluginParams("2.2.3", "25.0.0", 24)) - } - - @Test fun `test kotlin 1_2_20 and gradle 4_5 and abt 3_0_1`() { - doTest("4.5", "1.2.20", AndroidPluginParams("3.0.1", "27.0.0", 27)) - } -}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/MultiProjectSingleOutTest.kt b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/MultiProjectSingleOutTest.kt deleted file mode 100644 index 9458528c..00000000 --- a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/MultiProjectSingleOutTest.kt +++ /dev/null @@ -1,57 +0,0 @@ -package org.jetbrains.dokka.gradle - -import org.gradle.testkit.runner.TaskOutcome -import org.junit.Test -import kotlin.test.assertEquals - -class MultiProjectSingleOutTest : AbstractDokkaGradleTest() { - - fun prepareTestData(testDataRootPath: String) { - val testDataRoot = testDataFolder.resolve(testDataRootPath) - val tmpRoot = testProjectDir.root.toPath() - - testDataRoot.apply { - resolve("build.gradle").copy(tmpRoot.resolve("build.gradle")) - resolve("settings.gradle").copy(tmpRoot.resolve("settings.gradle")) - resolve("subA").copy(tmpRoot.resolve("subA")) - resolve("subB").copy(tmpRoot.resolve("subB")) - } - } - - private fun doTest(gradleVersion: String, kotlinVersion: String) { - - prepareTestData("multiProjectSingleOut") - - val result = configure(gradleVersion, kotlinVersion, arguments = arrayOf("dokka", "--stacktrace")).build() - - println(result.output) - - assertEquals(TaskOutcome.SUCCESS, result.task(":dokka")?.outcome) - - val docsOutput = "build/dokka" - - checkOutputStructure("multiProjectSingleOut/fileTree.txt", docsOutput) - - checkNoErrorClasses(docsOutput) - checkNoUnresolvedLinks(docsOutput) - - checkExternalLink(docsOutput, "<span class=\"identifier\">String</span>", - """<a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-string/index.html"><span class="identifier">String</span></a>""") - } - - @Test fun `test kotlin 1_1_2 and gradle 3_5`() { - doTest("3.5", "1.1.2") - } - - @Test fun `test kotlin 1_0_7 and gradle 2_14_1`() { - doTest("2.14.1", "1.0.7") - } - - @Test fun `test kotlin 1_1_2 and gradle 4_0`() { - doTest("4.0", "1.1.2") - } - - @Test fun `test kotlin 1_2_20 and gradle 4_5`() { - doTest("4.5", "1.2.20") - } -}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/MultiplatformProjectTest.kt b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/MultiplatformProjectTest.kt deleted file mode 100644 index 6ab9fd52..00000000 --- a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/MultiplatformProjectTest.kt +++ /dev/null @@ -1,54 +0,0 @@ -package org.jetbrains.dokka.gradle - -import org.gradle.testkit.runner.TaskOutcome -import org.junit.Test -import java.io.File -import kotlin.test.assertEquals - -class MultiplatformProjectTest : AbstractDokkaGradleTest() { - - fun prepareTestData(testDataRootPath: String) { - val testDataRoot = testDataFolder.resolve(testDataRootPath) - val tmpRoot = testProjectDir.root.toPath() - - testDataRoot.apply { - resolve("build.gradle").copy(tmpRoot.resolve("build.gradle")) - resolve("settings.gradle").copy(tmpRoot.resolve("settings.gradle")) - resolve("src").copy(tmpRoot.resolve("src")) - } - } - - private fun doTest(gradleVersion: String, kotlinVersion: String) { - val kotlinGradlePlugin = pluginClasspathData.resolveSibling("kotlin-gradle.txt").toFile().readLines().map { File(it) } - prepareTestData("multiplatformProject") - - // Remove withDebug(false) when https://github.com/gradle/gradle/issues/6862 is solved - val result = configure(gradleVersion, kotlinVersion, arguments = arrayOf("dokka", "--stacktrace")) - .withDebug(false) - .withPluginClasspath(pluginClasspath.union(kotlinGradlePlugin)) - .build() - - println(result.output) - - assertEquals(TaskOutcome.SUCCESS, result.task(":dokka")?.outcome) - - val docsOutput = "build/dokka" - - checkOutputStructure("multiplatformProject/fileTree.txt", docsOutput) - - checkNoErrorClasses(docsOutput) - checkNoUnresolvedLinks(docsOutput) - } - - @Test fun `test kotlin 1_3_30 and gradle 4_9`() { - doTest("4.9", "1.3.30") - } - - @Test fun `test kotlin 1_3_40 and gradle 4_10_3`() { - doTest("4.10.3", "1.3.40") - } - - @Test fun `test kotlin 1_3_40 and gradle 5_6_1`() { - doTest("5.6.1", "1.3.50") - } -}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/RebuildAfterSourceChangeTest.kt b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/RebuildAfterSourceChangeTest.kt deleted file mode 100644 index 8b2db560..00000000 --- a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/RebuildAfterSourceChangeTest.kt +++ /dev/null @@ -1,74 +0,0 @@ -package org.jetbrains.dokka.gradle - -import org.gradle.testkit.runner.TaskOutcome -import org.junit.Test -import java.nio.file.Path -import kotlin.test.assertEquals - -class RebuildAfterSourceChangeTest : AbstractDokkaGradleTest() { - - fun prepareTestData(testDataRootPath: String): Pair<Path, Path> { - val testDataRoot = testDataFolder.resolve(testDataRootPath) - val tmpRoot = testProjectDir.root.toPath() - - testDataRoot.resolve("src").copy(tmpRoot.resolve("src")) - testDataRoot.resolve("build.gradle").copy(tmpRoot.resolve("build.gradle")) - testDataRoot.resolve("settings.gradle").copy(tmpRoot.resolve("settings.gradle")) - - return testDataRoot to tmpRoot - } - - private fun doTest(gradleVersion: String, kotlinVersion: String) { - - val (testDataRoot, tmpRoot) = prepareTestData("sourcesChange") - val docsOutput = "build/dokka" - - configure(gradleVersion, kotlinVersion, arguments = arrayOf("dokka", "--stacktrace")).build().let { result -> - println(result.output) - - assertEquals(TaskOutcome.SUCCESS, result.task(":dokka")?.outcome) - } - - - configure(gradleVersion, kotlinVersion, arguments = arrayOf("dokka", "-i", "--stacktrace")).build().let { result -> - println(result.output) - - assertEquals(TaskOutcome.UP_TO_DATE, result.task(":dokka")?.outcome) - } - - checkOutputStructure("sourcesChange/fileTree.txt", docsOutput) - - testDataRoot.resolve("src1").copy(tmpRoot.resolve("src")) - - configure(gradleVersion, kotlinVersion, arguments = arrayOf("dokka", "--stacktrace")).build().let { result -> - println(result.output) - - assertEquals(TaskOutcome.SUCCESS, result.task(":dokka")?.outcome) - } - - - checkOutputStructure("sourcesChange/fileTree1.txt", docsOutput) - - } - - - @Test - fun `test kotlin 1_0_7 and gradle 2_14_1`() { - doTest("2.14.1", "1.0.7") - } - - @Test - fun `test kotlin 1_1_2 and gradle 3_5`() { - doTest("3.5", "1.1.2") - } - - @Test - fun `test kotlin 1_1_2 and gradle 4_0`() { - doTest("4.0", "1.1.2") - } - - @Test - fun `test kotlin 1_2_20 and gradle 4_5`() { - doTest("4.5", "1.2.20") - } -}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/TypeSafeConfigurationTest.kt b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/TypeSafeConfigurationTest.kt deleted file mode 100644 index 7b179e92..00000000 --- a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/TypeSafeConfigurationTest.kt +++ /dev/null @@ -1,36 +0,0 @@ -package org.jetbrains.dokka.gradle - -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.Parameterized - -@RunWith(Parameterized::class) -class TypeSafeConfigurationTest(private val testCase: TestCase) : AbstractDokkaGradleTest() { - - data class TestCase(val gradleVersion: String, val kotlinVersion: String) { - override fun toString(): String = "Gradle $gradleVersion and Kotlin $kotlinVersion" - } - - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun testCases() = listOf( - TestCase("4.0", "1.1.2"), - TestCase("4.5", "1.2.20"), - TestCase("4.10.1", "1.2.60") - ) - } - - @Test - fun test() { - - testDataFolder.resolve("typeSafeConfiguration").toFile() - .copyRecursively(testProjectDir.root) - - configure( - testCase.gradleVersion, - testCase.kotlinVersion, - arguments = arrayOf("help", "-s") - ).build() - } -} diff --git a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/Utils.kt b/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/Utils.kt deleted file mode 100644 index 6f17af22..00000000 --- a/runners/gradle-integration-tests/src/test/kotlin/org/jetbrains/dokka/gradle/Utils.kt +++ /dev/null @@ -1,56 +0,0 @@ -package org.jetbrains.dokka.gradle - -import com.intellij.rt.execution.junit.FileComparisonFailure -import java.io.File -import java.io.IOException -import java.nio.file.* -import java.nio.file.attribute.BasicFileAttributes - - -fun File.writeStructure(builder: StringBuilder, relativeTo: File = this, spaces: Int = 0) { - builder.append(" ".repeat(spaces)) - val out = if (this != relativeTo) this.relativeTo(relativeTo) else this - - builder.append(out) - if (this.isDirectory) { - builder.appendln("/") - this.listFiles().sortedBy { it.name }.forEach { it.writeStructure(builder, this, spaces + 4) } - } else { - builder.appendln() - } -} - -fun assertEqualsIgnoringSeparators(expectedFile: File, output: String) { - if (!expectedFile.exists()) expectedFile.createNewFile() - val expectedText = expectedFile.readText().replace("\r\n", "\n") - val actualText = output.replace("\r\n", "\n") - - if (expectedText != actualText) - throw FileComparisonFailure("", expectedText, actualText, expectedFile.canonicalPath) -} - -class CopyFileVisitor(private var sourcePath: Path?, private val targetPath: Path) : SimpleFileVisitor<Path>() { - - @Throws(IOException::class) - override fun preVisitDirectory(dir: Path, - attrs: BasicFileAttributes): FileVisitResult { - if (sourcePath == null) { - sourcePath = dir - } else { - Files.createDirectories(targetPath.resolve(sourcePath?.relativize(dir))) - } - return FileVisitResult.CONTINUE - } - - @Throws(IOException::class) - override fun visitFile(file: Path, - attrs: BasicFileAttributes): FileVisitResult { - Files.copy(file, targetPath.resolve(sourcePath?.relativize(file)), StandardCopyOption.REPLACE_EXISTING) - return FileVisitResult.CONTINUE - } -} - -fun Path.copy(to: Path) { - Files.walkFileTree(this, CopyFileVisitor(this, to)) -} - diff --git a/runners/gradle-integration-tests/testData/androidApp/app/build.gradle b/runners/gradle-integration-tests/testData/androidApp/app/build.gradle deleted file mode 100644 index 2420107c..00000000 --- a/runners/gradle-integration-tests/testData/androidApp/app/build.gradle +++ /dev/null @@ -1,45 +0,0 @@ -buildscript { - repositories { - jcenter() - mavenLocal() - } - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$test_kotlin_version" - } -} - -plugins { - id 'org.jetbrains.dokka' -} - - -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-android-extensions' - -android { - compileSdkVersion Integer.parseInt(sdk_version) - buildToolsVersion abt_version - - defaultConfig { - applicationId "org.example.kotlin.mixed" - minSdkVersion 14 - targetSdkVersion Integer.parseInt(sdk_version) - versionCode 1 - versionName "1.0" - } - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt') - } - } - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - } -} - -dependencies { - compile "org.jetbrains.kotlin:kotlin-stdlib:$test_kotlin_version" - dokkaRuntime files(dokka_fatjar) -}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/AndroidManifest.xml b/runners/gradle-integration-tests/testData/androidApp/app/src/main/AndroidManifest.xml deleted file mode 100644 index b4e1a892..00000000 --- a/runners/gradle-integration-tests/testData/androidApp/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,26 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="org.example.kotlin.mixed" > - - <application - android:allowBackup="true" - android:icon="@drawable/ic_launcher" - android:label="@string/app_name" - android:theme="@style/AppTheme" > - - <activity - android:name=".JavaActivity" - android:label="@string/title_activity_main_activity1" > - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - <category android:name="android.intent.category.LAUNCHER" /> - </intent-filter> - </activity> - - <activity - android:name=".KotlinActivity" - android:label="@string/title_activity_main_activity2" /> - - </application> - -</manifest> diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/java/org/example/kotlin/mixed/JavaActivity.java b/runners/gradle-integration-tests/testData/androidApp/app/src/main/java/org/example/kotlin/mixed/JavaActivity.java deleted file mode 100644 index 3668c594..00000000 --- a/runners/gradle-integration-tests/testData/androidApp/app/src/main/java/org/example/kotlin/mixed/JavaActivity.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.example.kotlin.mixed; - -import android.content.Intent; -import android.os.Bundle; -import android.app.Activity; -import android.view.Menu; -import android.view.View; -import android.widget.Button; - -public class JavaActivity extends Activity { - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - - Button next = (Button) findViewById(R.id.Button01); - next.setOnClickListener(new View.OnClickListener() { - public void onClick(View view) { - Intent myIntent = new Intent(view.getContext(), KotlinActivity.class); - startActivityForResult(myIntent, 0); - } - }); - } - - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - // Inflate the menu; this adds items to the action bar if it is present. - getMenuInflater().inflate(R.menu.main, menu); - return true; - } - -} diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/kotlin/org/example/kotlin/mixed/KotlinActivity.kt b/runners/gradle-integration-tests/testData/androidApp/app/src/main/kotlin/org/example/kotlin/mixed/KotlinActivity.kt deleted file mode 100644 index ca2f27b0..00000000 --- a/runners/gradle-integration-tests/testData/androidApp/app/src/main/kotlin/org/example/kotlin/mixed/KotlinActivity.kt +++ /dev/null @@ -1,28 +0,0 @@ -package org.example.kotlin.mixed - -import android.content.Intent -import android.os.Bundle -import android.app.Activity -import android.view.Menu -import android.widget.Button - -class KotlinActivity : Activity() { - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main2) - - val next = findViewById(R.id.Button02) as Button - next.setOnClickListener { - val intent: Intent = Intent() - setResult(RESULT_OK, intent) - finish() - } - } - - override fun onCreateOptionsMenu(menu: Menu?): Boolean { - // Inflate the menu; this adds items to the action bar if it is present. - menuInflater.inflate(R.menu.main_activity2, menu) - return true - } -} diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/drawable-hdpi/ic_launcher.png b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/drawable-hdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 96a442e5..00000000 --- a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/drawable-hdpi/ic_launcher.png +++ /dev/null diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/drawable-mdpi/ic_launcher.png b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/drawable-mdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 359047df..00000000 --- a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/drawable-mdpi/ic_launcher.png +++ /dev/null diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/drawable-xhdpi/ic_launcher.png b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/drawable-xhdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 71c6d760..00000000 --- a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/drawable-xhdpi/ic_launcher.png +++ /dev/null diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/layout/activity_main.xml b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/layout/activity_main.xml deleted file mode 100644 index ede57c39..00000000 --- a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/layout/activity_main.xml +++ /dev/null @@ -1,24 +0,0 @@ -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:paddingLeft="@dimen/activity_horizontal_margin" - android:paddingRight="@dimen/activity_horizontal_margin" - android:paddingTop="@dimen/activity_vertical_margin" - android:paddingBottom="@dimen/activity_vertical_margin" - tools:context=".MainActivity"> - - <TextView - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:text="Activity 1" /> - - <Button android:text="Next" - android:id="@+id/Button01" - android:layout_width="250px" - android:textSize="18px" - android:layout_height="55px"> - </Button> - -</LinearLayout> diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/layout/activity_main2.xml b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/layout/activity_main2.xml deleted file mode 100644 index d707536a..00000000 --- a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/layout/activity_main2.xml +++ /dev/null @@ -1,24 +0,0 @@ -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:paddingLeft="@dimen/activity_horizontal_margin" - android:paddingRight="@dimen/activity_horizontal_margin" - android:paddingTop="@dimen/activity_vertical_margin" - android:paddingBottom="@dimen/activity_vertical_margin" - tools:context=".MainActivity"> - - <TextView - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:text="Activity 2" /> - - <Button android:text="Next" - android:id="@+id/Button02" - android:layout_width="250px" - android:textSize="18px" - android:layout_height="55px"> - </Button> - -</LinearLayout> diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/menu/main.xml b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/menu/main.xml deleted file mode 100644 index f3b10b6c..00000000 --- a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/menu/main.xml +++ /dev/null @@ -1,6 +0,0 @@ -<menu xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:id="@+id/action_settings" - android:title="@string/action_settings" - android:orderInCategory="100" - android:showAsAction="never" /> -</menu> diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/menu/main_activity2.xml b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/menu/main_activity2.xml deleted file mode 100644 index f3b10b6c..00000000 --- a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/menu/main_activity2.xml +++ /dev/null @@ -1,6 +0,0 @@ -<menu xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:id="@+id/action_settings" - android:title="@string/action_settings" - android:orderInCategory="100" - android:showAsAction="never" /> -</menu> diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/values/dimens.xml b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/values/dimens.xml deleted file mode 100644 index 47c82246..00000000 --- a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/values/dimens.xml +++ /dev/null @@ -1,5 +0,0 @@ -<resources> - <!-- Default screen margins, per the Android Design guidelines. --> - <dimen name="activity_horizontal_margin">16dp</dimen> - <dimen name="activity_vertical_margin">16dp</dimen> -</resources> diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/values/strings.xml b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/values/strings.xml deleted file mode 100644 index d8f08bc2..00000000 --- a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/values/strings.xml +++ /dev/null @@ -1,10 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - - <string name="app_name">AndroidSample</string> - <string name="action_settings">Settings</string> - <string name="hello_world">Hello world!</string> - <string name="title_activity_main_activity1">JavaActivity</string> - <string name="title_activity_main_activity2">KotlinActivity</string> - -</resources> diff --git a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/values/styles.xml b/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/values/styles.xml deleted file mode 100644 index 6ce89c7b..00000000 --- a/runners/gradle-integration-tests/testData/androidApp/app/src/main/res/values/styles.xml +++ /dev/null @@ -1,20 +0,0 @@ -<resources> - - <!-- - Base application theme, dependent on API level. This theme is replaced - by AppBaseTheme from res/values-vXX/styles.xml on newer devices. - --> - <style name="AppBaseTheme" parent="android:Theme.Light"> - <!-- - Theme customizations available in newer API levels can go in - res/values-vXX/styles.xml, while customizations related to - backward-compatibility can go here. - --> - </style> - - <!-- Application theme. --> - <style name="AppTheme" parent="AppBaseTheme"> - <!-- All customizations that are NOT specific to a particular API-level can go here. --> - </style> - -</resources> diff --git a/runners/gradle-integration-tests/testData/androidApp/build.gradle b/runners/gradle-integration-tests/testData/androidApp/build.gradle deleted file mode 100644 index 35356b90..00000000 --- a/runners/gradle-integration-tests/testData/androidApp/build.gradle +++ /dev/null @@ -1,21 +0,0 @@ -buildscript { - repositories { - mavenCentral() - jcenter() - maven { url 'https://maven.google.com' } - maven { url "https://dl.bintray.com/kotlin/kotlin-eap-1.1" } - maven { url "https://dl.bintray.com/kotlin/kotlin-dev" } - } - dependencies { - classpath "com.android.tools.build:gradle:$abt_plugin_version" - } -} - -allprojects { - repositories { - mavenCentral() - jcenter() - maven { url "https://dl.bintray.com/kotlin/kotlin-eap-1.1" } - maven { url "https://dl.bintray.com/kotlin/kotlin-dev" } - } -} diff --git a/runners/gradle-integration-tests/testData/androidApp/fileTree.txt b/runners/gradle-integration-tests/testData/androidApp/fileTree.txt deleted file mode 100644 index f66c79e3..00000000 --- a/runners/gradle-integration-tests/testData/androidApp/fileTree.txt +++ /dev/null @@ -1,20 +0,0 @@ -/ - app/ - alltypes/ - index.html - index-outline.html - index.html - org.example.kotlin.mixed/ - -java-activity/ - -init-.html - index.html - on-create-options-menu.html - on-create.html - -kotlin-activity/ - -init-.html - index.html - on-create-options-menu.html - on-create.html - index.html - package-list - style.css diff --git a/runners/gradle-integration-tests/testData/androidApp/settings.gradle b/runners/gradle-integration-tests/testData/androidApp/settings.gradle deleted file mode 100644 index 1feb2867..00000000 --- a/runners/gradle-integration-tests/testData/androidApp/settings.gradle +++ /dev/null @@ -1,3 +0,0 @@ -rootProject.name = "androidApp" - -include ':app'
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/build.gradle b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/build.gradle deleted file mode 100644 index 66421f52..00000000 --- a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/build.gradle +++ /dev/null @@ -1,49 +0,0 @@ -buildscript { - repositories { - jcenter() - mavenLocal() - } - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$test_kotlin_version" - } -} - -plugins { - id 'org.jetbrains.dokka' -} - - -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' - -android { - compileSdkVersion Integer.parseInt(sdk_version) - buildToolsVersion abt_version - - defaultConfig { - applicationId "org.example.kotlin.mixed" - minSdkVersion 14 - targetSdkVersion Integer.parseInt(sdk_version) - versionCode 1 - versionName "1.0" - } - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt') - } - } - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - } -} - -dependencies { - compile "org.jetbrains.kotlin:kotlin-stdlib:$test_kotlin_version" - dokkaRuntime files(dokka_fatjar) -} - - -dokka { - outputFormat = "javadoc" -}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/AndroidManifest.xml b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/AndroidManifest.xml deleted file mode 100644 index b4e1a892..00000000 --- a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,26 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="org.example.kotlin.mixed" > - - <application - android:allowBackup="true" - android:icon="@drawable/ic_launcher" - android:label="@string/app_name" - android:theme="@style/AppTheme" > - - <activity - android:name=".JavaActivity" - android:label="@string/title_activity_main_activity1" > - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - <category android:name="android.intent.category.LAUNCHER" /> - </intent-filter> - </activity> - - <activity - android:name=".KotlinActivity" - android:label="@string/title_activity_main_activity2" /> - - </application> - -</manifest> diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/java/org/example/kotlin/mixed/JavaActivity.java b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/java/org/example/kotlin/mixed/JavaActivity.java deleted file mode 100644 index 3668c594..00000000 --- a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/java/org/example/kotlin/mixed/JavaActivity.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.example.kotlin.mixed; - -import android.content.Intent; -import android.os.Bundle; -import android.app.Activity; -import android.view.Menu; -import android.view.View; -import android.widget.Button; - -public class JavaActivity extends Activity { - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - - Button next = (Button) findViewById(R.id.Button01); - next.setOnClickListener(new View.OnClickListener() { - public void onClick(View view) { - Intent myIntent = new Intent(view.getContext(), KotlinActivity.class); - startActivityForResult(myIntent, 0); - } - }); - } - - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - // Inflate the menu; this adds items to the action bar if it is present. - getMenuInflater().inflate(R.menu.main, menu); - return true; - } - -} diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/kotlin/org/example/kotlin/mixed/KotlinActivity.kt b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/kotlin/org/example/kotlin/mixed/KotlinActivity.kt deleted file mode 100644 index ca2f27b0..00000000 --- a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/kotlin/org/example/kotlin/mixed/KotlinActivity.kt +++ /dev/null @@ -1,28 +0,0 @@ -package org.example.kotlin.mixed - -import android.content.Intent -import android.os.Bundle -import android.app.Activity -import android.view.Menu -import android.widget.Button - -class KotlinActivity : Activity() { - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main2) - - val next = findViewById(R.id.Button02) as Button - next.setOnClickListener { - val intent: Intent = Intent() - setResult(RESULT_OK, intent) - finish() - } - } - - override fun onCreateOptionsMenu(menu: Menu?): Boolean { - // Inflate the menu; this adds items to the action bar if it is present. - menuInflater.inflate(R.menu.main_activity2, menu) - return true - } -} diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/drawable-hdpi/ic_launcher.png b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/drawable-hdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 96a442e5..00000000 --- a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/drawable-hdpi/ic_launcher.png +++ /dev/null diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/drawable-mdpi/ic_launcher.png b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/drawable-mdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 359047df..00000000 --- a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/drawable-mdpi/ic_launcher.png +++ /dev/null diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/drawable-xhdpi/ic_launcher.png b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/drawable-xhdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 71c6d760..00000000 --- a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/drawable-xhdpi/ic_launcher.png +++ /dev/null diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/layout/activity_main.xml b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/layout/activity_main.xml deleted file mode 100644 index ede57c39..00000000 --- a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/layout/activity_main.xml +++ /dev/null @@ -1,24 +0,0 @@ -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:paddingLeft="@dimen/activity_horizontal_margin" - android:paddingRight="@dimen/activity_horizontal_margin" - android:paddingTop="@dimen/activity_vertical_margin" - android:paddingBottom="@dimen/activity_vertical_margin" - tools:context=".MainActivity"> - - <TextView - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:text="Activity 1" /> - - <Button android:text="Next" - android:id="@+id/Button01" - android:layout_width="250px" - android:textSize="18px" - android:layout_height="55px"> - </Button> - -</LinearLayout> diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/layout/activity_main2.xml b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/layout/activity_main2.xml deleted file mode 100644 index d707536a..00000000 --- a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/layout/activity_main2.xml +++ /dev/null @@ -1,24 +0,0 @@ -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:paddingLeft="@dimen/activity_horizontal_margin" - android:paddingRight="@dimen/activity_horizontal_margin" - android:paddingTop="@dimen/activity_vertical_margin" - android:paddingBottom="@dimen/activity_vertical_margin" - tools:context=".MainActivity"> - - <TextView - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:text="Activity 2" /> - - <Button android:text="Next" - android:id="@+id/Button02" - android:layout_width="250px" - android:textSize="18px" - android:layout_height="55px"> - </Button> - -</LinearLayout> diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/menu/main.xml b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/menu/main.xml deleted file mode 100644 index f3b10b6c..00000000 --- a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/menu/main.xml +++ /dev/null @@ -1,6 +0,0 @@ -<menu xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:id="@+id/action_settings" - android:title="@string/action_settings" - android:orderInCategory="100" - android:showAsAction="never" /> -</menu> diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/menu/main_activity2.xml b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/menu/main_activity2.xml deleted file mode 100644 index f3b10b6c..00000000 --- a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/menu/main_activity2.xml +++ /dev/null @@ -1,6 +0,0 @@ -<menu xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:id="@+id/action_settings" - android:title="@string/action_settings" - android:orderInCategory="100" - android:showAsAction="never" /> -</menu> diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/values/dimens.xml b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/values/dimens.xml deleted file mode 100644 index 47c82246..00000000 --- a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/values/dimens.xml +++ /dev/null @@ -1,5 +0,0 @@ -<resources> - <!-- Default screen margins, per the Android Design guidelines. --> - <dimen name="activity_horizontal_margin">16dp</dimen> - <dimen name="activity_vertical_margin">16dp</dimen> -</resources> diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/values/strings.xml b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/values/strings.xml deleted file mode 100644 index d8f08bc2..00000000 --- a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/values/strings.xml +++ /dev/null @@ -1,10 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - - <string name="app_name">AndroidSample</string> - <string name="action_settings">Settings</string> - <string name="hello_world">Hello world!</string> - <string name="title_activity_main_activity1">JavaActivity</string> - <string name="title_activity_main_activity2">KotlinActivity</string> - -</resources> diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/values/styles.xml b/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/values/styles.xml deleted file mode 100644 index 6ce89c7b..00000000 --- a/runners/gradle-integration-tests/testData/androidAppJavadoc/app/src/main/res/values/styles.xml +++ /dev/null @@ -1,20 +0,0 @@ -<resources> - - <!-- - Base application theme, dependent on API level. This theme is replaced - by AppBaseTheme from res/values-vXX/styles.xml on newer devices. - --> - <style name="AppBaseTheme" parent="android:Theme.Light"> - <!-- - Theme customizations available in newer API levels can go in - res/values-vXX/styles.xml, while customizations related to - backward-compatibility can go here. - --> - </style> - - <!-- Application theme. --> - <style name="AppTheme" parent="AppBaseTheme"> - <!-- All customizations that are NOT specific to a particular API-level can go here. --> - </style> - -</resources> diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/build.gradle b/runners/gradle-integration-tests/testData/androidAppJavadoc/build.gradle deleted file mode 100644 index 35356b90..00000000 --- a/runners/gradle-integration-tests/testData/androidAppJavadoc/build.gradle +++ /dev/null @@ -1,21 +0,0 @@ -buildscript { - repositories { - mavenCentral() - jcenter() - maven { url 'https://maven.google.com' } - maven { url "https://dl.bintray.com/kotlin/kotlin-eap-1.1" } - maven { url "https://dl.bintray.com/kotlin/kotlin-dev" } - } - dependencies { - classpath "com.android.tools.build:gradle:$abt_plugin_version" - } -} - -allprojects { - repositories { - mavenCentral() - jcenter() - maven { url "https://dl.bintray.com/kotlin/kotlin-eap-1.1" } - maven { url "https://dl.bintray.com/kotlin/kotlin-dev" } - } -} diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/fileTree.txt b/runners/gradle-integration-tests/testData/androidAppJavadoc/fileTree.txt deleted file mode 100644 index c5e79eba..00000000 --- a/runners/gradle-integration-tests/testData/androidAppJavadoc/fileTree.txt +++ /dev/null @@ -1,21 +0,0 @@ -/ - allclasses-frame.html - allclasses-noframe.html - constant-values.html - deprecated-list.html - help-doc.html - index-all.html - index.html - org/ - example/ - kotlin/ - mixed/ - JavaActivity.html - KotlinActivity.html - package-frame.html - package-summary.html - package-tree.html - overview-tree.html - package-list - script.js - stylesheet.css diff --git a/runners/gradle-integration-tests/testData/androidAppJavadoc/settings.gradle b/runners/gradle-integration-tests/testData/androidAppJavadoc/settings.gradle deleted file mode 100644 index a4e67fea..00000000 --- a/runners/gradle-integration-tests/testData/androidAppJavadoc/settings.gradle +++ /dev/null @@ -1,3 +0,0 @@ -rootProject.name = "androidAppJavadoc" - -include ':app'
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/build.gradle b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/build.gradle deleted file mode 100644 index 736668ab..00000000 --- a/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/build.gradle +++ /dev/null @@ -1,20 +0,0 @@ -subprojects { - buildscript { - repositories { - mavenCentral() - jcenter() - maven { url 'https://maven.google.com' } - maven { url "https://dl.bintray.com/kotlin/kotlin-eap/" } - maven { url "https://dl.bintray.com/kotlin/kotlin-dev" } - } - - } - - repositories { - mavenCentral() - jcenter() - maven { url 'https://maven.google.com' } - maven { url "https://dl.bintray.com/kotlin/kotlin-eap/" } - maven { url "https://dl.bintray.com/kotlin/kotlin-dev" } - } -}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/fileTree.txt b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/fileTree.txt deleted file mode 100644 index 6c96a01c..00000000 --- a/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/fileTree.txt +++ /dev/null @@ -1,14 +0,0 @@ -/ - lib/ - alltypes/ - index.html - example/ - -lib-clz-use/ - -init-.html - f.html - index.html - index.html - index-outline.html - index.html - package-list - style.css diff --git a/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/jlib/build.gradle b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/jlib/build.gradle deleted file mode 100644 index bbfeb03c..00000000 --- a/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/jlib/build.gradle +++ /dev/null @@ -1 +0,0 @@ -apply plugin: 'java' diff --git a/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/jlib/src/main/java/example/jlib/LibClz.java b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/jlib/src/main/java/example/jlib/LibClz.java deleted file mode 100644 index 1d9a6fb2..00000000 --- a/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/jlib/src/main/java/example/jlib/LibClz.java +++ /dev/null @@ -1,5 +0,0 @@ -package example.jlib; - -public class LibClz { - -}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/lib/build.gradle b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/lib/build.gradle deleted file mode 100644 index b1ee52ab..00000000 --- a/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/lib/build.gradle +++ /dev/null @@ -1,39 +0,0 @@ -buildscript { - dependencies { - classpath "com.android.tools.build:gradle:$abt_plugin_version" - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$test_kotlin_version" - } -} - - -plugins { - id 'org.jetbrains.dokka' -} - - -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' - - -android { - compileSdkVersion Integer.parseInt(sdk_version) - buildToolsVersion abt_version - - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - } -} - -dependencies { - api(project(":jlib")) - dokkaRuntime files(dokka_fatjar) -} - -dokka { - configuration { - externalDocumentationLink { - url = new URL("https://example.com") - packageListUrl = file("$rootDir/package-list").toURI().toURL() - } - } -}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/lib/src/main/AndroidManifest.xml b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/lib/src/main/AndroidManifest.xml deleted file mode 100644 index 267f6efd..00000000 --- a/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/lib/src/main/AndroidManifest.xml +++ /dev/null @@ -1,4 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="example"> -</manifest> diff --git a/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/lib/src/main/kotlin/example/LibClzUse.kt b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/lib/src/main/kotlin/example/LibClzUse.kt deleted file mode 100644 index d034a3a9..00000000 --- a/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/lib/src/main/kotlin/example/LibClzUse.kt +++ /dev/null @@ -1,13 +0,0 @@ -package example - -import example.jlib.LibClz - -/** - * Uses jlib - */ -class LibClzUse { - /** - * Returns LibClz - */ - fun f(): LibClz = LibClz() -}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/package-list b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/package-list deleted file mode 100644 index bf76058e..00000000 --- a/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/package-list +++ /dev/null @@ -1 +0,0 @@ -example.jlib
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/settings.gradle b/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/settings.gradle deleted file mode 100644 index 5b4250a0..00000000 --- a/runners/gradle-integration-tests/testData/androidLibDependsOnJavaLib/settings.gradle +++ /dev/null @@ -1,5 +0,0 @@ -rootProject.name = "androidLibDependsOnJavaLib" - - -include(":lib") -include(":jlib")
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/build.gradle b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/build.gradle deleted file mode 100644 index 660257ab..00000000 --- a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/build.gradle +++ /dev/null @@ -1,75 +0,0 @@ -buildscript { - repositories { - jcenter() - mavenLocal() - } - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$test_kotlin_version" - } -} - -plugins { - id 'org.jetbrains.dokka' -} - - -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-android-extensions' - -android { - compileSdkVersion Integer.parseInt(sdk_version) - buildToolsVersion abt_version - - defaultConfig { - applicationId "org.example.kotlin.mixed" - minSdkVersion 14 - targetSdkVersion Integer.parseInt(sdk_version) - versionCode 1 - versionName "1.0" - } - buildTypes { - release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt') - } - } - - flavorDimensions "mode" - productFlavors { - free { - dimension "mode" - applicationIdSuffix ".free" - versionNameSuffix "-free" - } - full { - dimension "mode" - applicationIdSuffix ".full" - versionNameSuffix "-full" - } - } - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - free.java.srcDirs += 'src/free/kotlin' - } -} - -dependencies { - compile "org.jetbrains.kotlin:kotlin-stdlib:$test_kotlin_version" - dokkaRuntime files(dokka_fatjar) -} - - -dokka { - outputDirectory = "$buildDir/dokka/all" -} - -task dokkaFullFlavourOnly(type: org.jetbrains.dokka.gradle.DokkaTask) { - outputDirectory = "$buildDir/dokka/fullOnly" - configuration { - moduleName = "full" - kotlinTasks { - ["compileFullReleaseKotlin"] - } - } -}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/free/AndroidManifest.xml b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/free/AndroidManifest.xml deleted file mode 100644 index 3ecbcd3a..00000000 --- a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/free/AndroidManifest.xml +++ /dev/null @@ -1,9 +0,0 @@ -<manifest xmlns:android="http://schemas.android.com/apk/res/android"> - - <application> - <activity - android:name="org.example.kotlin.mixed.free.AdActivity" - android:label="@string/title_activity_ad" - android:theme="@style/AppTheme"></activity> - </application> -</manifest> diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/free/kotlin/org/example/kotlin/mixed/free/AdActivity.kt b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/free/kotlin/org/example/kotlin/mixed/free/AdActivity.kt deleted file mode 100644 index b0b980fd..00000000 --- a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/free/kotlin/org/example/kotlin/mixed/free/AdActivity.kt +++ /dev/null @@ -1,14 +0,0 @@ -package org.example.kotlin.mixed.free - -import android.os.Bundle -import android.app.Activity -import org.example.kotlin.mixed.R - -class AdActivity : Activity() { - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_ad) - } - -} diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/free/res/layout/activity_ad.xml b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/free/res/layout/activity_ad.xml deleted file mode 100644 index e6443d05..00000000 --- a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/free/res/layout/activity_ad.xml +++ /dev/null @@ -1,24 +0,0 @@ -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:paddingLeft="@dimen/activity_horizontal_margin" - android:paddingRight="@dimen/activity_horizontal_margin" - android:paddingTop="@dimen/activity_vertical_margin" - android:paddingBottom="@dimen/activity_vertical_margin" - tools:context=".free.AdActivity"> - - <TextView - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:text="Advertisment" /> - - <Button android:text="Next" - android:id="@+id/Button02" - android:layout_width="250px" - android:textSize="18px" - android:layout_height="55px"> - </Button> - -</LinearLayout> diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/free/res/values/strings.xml b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/free/res/values/strings.xml deleted file mode 100644 index bbdf2d06..00000000 --- a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/free/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ -<resources> - <string name="title_activity_ad">AdActivity</string> -</resources> diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/AndroidManifest.xml b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/AndroidManifest.xml deleted file mode 100644 index b4e1a892..00000000 --- a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,26 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="org.example.kotlin.mixed" > - - <application - android:allowBackup="true" - android:icon="@drawable/ic_launcher" - android:label="@string/app_name" - android:theme="@style/AppTheme" > - - <activity - android:name=".JavaActivity" - android:label="@string/title_activity_main_activity1" > - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - <category android:name="android.intent.category.LAUNCHER" /> - </intent-filter> - </activity> - - <activity - android:name=".KotlinActivity" - android:label="@string/title_activity_main_activity2" /> - - </application> - -</manifest> diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/java/org/example/kotlin/mixed/JavaActivity.java b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/java/org/example/kotlin/mixed/JavaActivity.java deleted file mode 100644 index 3668c594..00000000 --- a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/java/org/example/kotlin/mixed/JavaActivity.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.example.kotlin.mixed; - -import android.content.Intent; -import android.os.Bundle; -import android.app.Activity; -import android.view.Menu; -import android.view.View; -import android.widget.Button; - -public class JavaActivity extends Activity { - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - - Button next = (Button) findViewById(R.id.Button01); - next.setOnClickListener(new View.OnClickListener() { - public void onClick(View view) { - Intent myIntent = new Intent(view.getContext(), KotlinActivity.class); - startActivityForResult(myIntent, 0); - } - }); - } - - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - // Inflate the menu; this adds items to the action bar if it is present. - getMenuInflater().inflate(R.menu.main, menu); - return true; - } - -} diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/kotlin/org/example/kotlin/mixed/KotlinActivity.kt b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/kotlin/org/example/kotlin/mixed/KotlinActivity.kt deleted file mode 100644 index ca2f27b0..00000000 --- a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/kotlin/org/example/kotlin/mixed/KotlinActivity.kt +++ /dev/null @@ -1,28 +0,0 @@ -package org.example.kotlin.mixed - -import android.content.Intent -import android.os.Bundle -import android.app.Activity -import android.view.Menu -import android.widget.Button - -class KotlinActivity : Activity() { - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main2) - - val next = findViewById(R.id.Button02) as Button - next.setOnClickListener { - val intent: Intent = Intent() - setResult(RESULT_OK, intent) - finish() - } - } - - override fun onCreateOptionsMenu(menu: Menu?): Boolean { - // Inflate the menu; this adds items to the action bar if it is present. - menuInflater.inflate(R.menu.main_activity2, menu) - return true - } -} diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/drawable-hdpi/ic_launcher.png b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/drawable-hdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 96a442e5..00000000 --- a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/drawable-hdpi/ic_launcher.png +++ /dev/null diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/drawable-mdpi/ic_launcher.png b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/drawable-mdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 359047df..00000000 --- a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/drawable-mdpi/ic_launcher.png +++ /dev/null diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/drawable-xhdpi/ic_launcher.png b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/drawable-xhdpi/ic_launcher.png Binary files differdeleted file mode 100644 index 71c6d760..00000000 --- a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/drawable-xhdpi/ic_launcher.png +++ /dev/null diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/layout/activity_main.xml b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/layout/activity_main.xml deleted file mode 100644 index ede57c39..00000000 --- a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/layout/activity_main.xml +++ /dev/null @@ -1,24 +0,0 @@ -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:paddingLeft="@dimen/activity_horizontal_margin" - android:paddingRight="@dimen/activity_horizontal_margin" - android:paddingTop="@dimen/activity_vertical_margin" - android:paddingBottom="@dimen/activity_vertical_margin" - tools:context=".MainActivity"> - - <TextView - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:text="Activity 1" /> - - <Button android:text="Next" - android:id="@+id/Button01" - android:layout_width="250px" - android:textSize="18px" - android:layout_height="55px"> - </Button> - -</LinearLayout> diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/layout/activity_main2.xml b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/layout/activity_main2.xml deleted file mode 100644 index d707536a..00000000 --- a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/layout/activity_main2.xml +++ /dev/null @@ -1,24 +0,0 @@ -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:paddingLeft="@dimen/activity_horizontal_margin" - android:paddingRight="@dimen/activity_horizontal_margin" - android:paddingTop="@dimen/activity_vertical_margin" - android:paddingBottom="@dimen/activity_vertical_margin" - tools:context=".MainActivity"> - - <TextView - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:text="Activity 2" /> - - <Button android:text="Next" - android:id="@+id/Button02" - android:layout_width="250px" - android:textSize="18px" - android:layout_height="55px"> - </Button> - -</LinearLayout> diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/menu/main.xml b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/menu/main.xml deleted file mode 100644 index f3b10b6c..00000000 --- a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/menu/main.xml +++ /dev/null @@ -1,6 +0,0 @@ -<menu xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:id="@+id/action_settings" - android:title="@string/action_settings" - android:orderInCategory="100" - android:showAsAction="never" /> -</menu> diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/menu/main_activity2.xml b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/menu/main_activity2.xml deleted file mode 100644 index f3b10b6c..00000000 --- a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/menu/main_activity2.xml +++ /dev/null @@ -1,6 +0,0 @@ -<menu xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:id="@+id/action_settings" - android:title="@string/action_settings" - android:orderInCategory="100" - android:showAsAction="never" /> -</menu> diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/values/dimens.xml b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/values/dimens.xml deleted file mode 100644 index 47c82246..00000000 --- a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/values/dimens.xml +++ /dev/null @@ -1,5 +0,0 @@ -<resources> - <!-- Default screen margins, per the Android Design guidelines. --> - <dimen name="activity_horizontal_margin">16dp</dimen> - <dimen name="activity_vertical_margin">16dp</dimen> -</resources> diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/values/strings.xml b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/values/strings.xml deleted file mode 100644 index d8f08bc2..00000000 --- a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/values/strings.xml +++ /dev/null @@ -1,10 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<resources> - - <string name="app_name">AndroidSample</string> - <string name="action_settings">Settings</string> - <string name="hello_world">Hello world!</string> - <string name="title_activity_main_activity1">JavaActivity</string> - <string name="title_activity_main_activity2">KotlinActivity</string> - -</resources> diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/values/styles.xml b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/values/styles.xml deleted file mode 100644 index 6ce89c7b..00000000 --- a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/app/src/main/res/values/styles.xml +++ /dev/null @@ -1,20 +0,0 @@ -<resources> - - <!-- - Base application theme, dependent on API level. This theme is replaced - by AppBaseTheme from res/values-vXX/styles.xml on newer devices. - --> - <style name="AppBaseTheme" parent="android:Theme.Light"> - <!-- - Theme customizations available in newer API levels can go in - res/values-vXX/styles.xml, while customizations related to - backward-compatibility can go here. - --> - </style> - - <!-- Application theme. --> - <style name="AppTheme" parent="AppBaseTheme"> - <!-- All customizations that are NOT specific to a particular API-level can go here. --> - </style> - -</resources> diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/build.gradle b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/build.gradle deleted file mode 100644 index 35356b90..00000000 --- a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/build.gradle +++ /dev/null @@ -1,21 +0,0 @@ -buildscript { - repositories { - mavenCentral() - jcenter() - maven { url 'https://maven.google.com' } - maven { url "https://dl.bintray.com/kotlin/kotlin-eap-1.1" } - maven { url "https://dl.bintray.com/kotlin/kotlin-dev" } - } - dependencies { - classpath "com.android.tools.build:gradle:$abt_plugin_version" - } -} - -allprojects { - repositories { - mavenCentral() - jcenter() - maven { url "https://dl.bintray.com/kotlin/kotlin-eap-1.1" } - maven { url "https://dl.bintray.com/kotlin/kotlin-dev" } - } -} diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/fileTree.txt b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/fileTree.txt deleted file mode 100644 index 77b563b2..00000000 --- a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/fileTree.txt +++ /dev/null @@ -1,47 +0,0 @@ -/ - all/ - app/ - alltypes/ - index.html - index-outline.html - index.html - org.example.kotlin.mixed/ - -java-activity/ - -init-.html - index.html - on-create-options-menu.html - on-create.html - -kotlin-activity/ - -init-.html - index.html - on-create-options-menu.html - on-create.html - index.html - org.example.kotlin.mixed.free/ - -ad-activity/ - -init-.html - index.html - on-create.html - index.html - package-list - style.css - fullOnly/ - full/ - alltypes/ - index.html - index-outline.html - index.html - org.example.kotlin.mixed/ - -java-activity/ - -init-.html - index.html - on-create-options-menu.html - on-create.html - -kotlin-activity/ - -init-.html - index.html - on-create-options-menu.html - on-create.html - index.html - package-list - style.css diff --git a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/settings.gradle b/runners/gradle-integration-tests/testData/androidMultiFlavourApp/settings.gradle deleted file mode 100644 index 1feb2867..00000000 --- a/runners/gradle-integration-tests/testData/androidMultiFlavourApp/settings.gradle +++ /dev/null @@ -1,3 +0,0 @@ -rootProject.name = "androidApp" - -include ':app'
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/basic/build.gradle b/runners/gradle-integration-tests/testData/basic/build.gradle deleted file mode 100644 index 79645204..00000000 --- a/runners/gradle-integration-tests/testData/basic/build.gradle +++ /dev/null @@ -1,42 +0,0 @@ -buildscript { - repositories { - mavenCentral() - jcenter() - maven { url "https://dl.bintray.com/kotlin/kotlin-eap-1.1" } - maven { url "https://dl.bintray.com/kotlin/kotlin-dev" } - } - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$test_kotlin_version" - } -} - -plugins { - id 'org.jetbrains.dokka' -} - -apply plugin: 'kotlin' -apply plugin: 'org.jetbrains.dokka' - -repositories { - mavenCentral() - jcenter() - maven { - url "https://dl.bintray.com/kotlin/kotlin-eap-1.1" - } - maven { - url "https://dl.bintray.com/kotlin/kotlin-dev" - } -} - -dependencies { - dokkaRuntime files(dokka_fatjar) - compile group: 'org.jetbrains.kotlin', name: 'kotlin-runtime', version: test_kotlin_version - compile group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: test_kotlin_version -} - - -dokka { - configuration { - classpath += "$projectDir/classDir" - } -}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/basic/classDir/p1/MyBinaryClass.class b/runners/gradle-integration-tests/testData/basic/classDir/p1/MyBinaryClass.class Binary files differdeleted file mode 100644 index ccfff300..00000000 --- a/runners/gradle-integration-tests/testData/basic/classDir/p1/MyBinaryClass.class +++ /dev/null diff --git a/runners/gradle-integration-tests/testData/basic/fileTree.txt b/runners/gradle-integration-tests/testData/basic/fileTree.txt deleted file mode 100644 index 2ceae371..00000000 --- a/runners/gradle-integration-tests/testData/basic/fileTree.txt +++ /dev/null @@ -1,33 +0,0 @@ -/ - basic/ - alltypes/ - index.html - demo/ - -a/ - -init-.html - index.html - p.html - -greeter/ - -init-.html - greet.html - index.html - name.html - -some-interface.html - -some-sub-type/ - -init-.html - index.html - -some-type/ - -init-.html - index.html - constructor.html - index.html - main.html - p1.-my-binary-class/ - index.html - test.html - str.html - x.html - index-outline.html - index.html - package-list - style.css diff --git a/runners/gradle-integration-tests/testData/basic/settings.gradle b/runners/gradle-integration-tests/testData/basic/settings.gradle deleted file mode 100644 index c36a146c..00000000 --- a/runners/gradle-integration-tests/testData/basic/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = "basic"
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/basic/src/main/kotlin/demo/HelloWorld.kt b/runners/gradle-integration-tests/testData/basic/src/main/kotlin/demo/HelloWorld.kt deleted file mode 100644 index 3d7bcb51..00000000 --- a/runners/gradle-integration-tests/testData/basic/src/main/kotlin/demo/HelloWorld.kt +++ /dev/null @@ -1,45 +0,0 @@ -package demo - -import p1.MyBinaryClass - -/** - * This class supports greeting people by name. - * - * @property name The name of the person to be greeted. - */ -class Greeter(val name: String) { - - /** - * Prints the greeting to the standard output. - */ - fun greet() { - println("Hello $name!") - } -} - -fun main(args: Array<String>) { - Greeter(args[0]).greet() -} - -val str = "Hello! ".repeat(4) -val x: (a: String, b: Int) -> Int = { a, b -> 0 } - -interface SomeInterface -private class SomeImpl : SomeInterface - -fun SomeInterface.constructor(): SomeInterface { - return SomeImpl() -} - -open class SomeType -class SomeSubType : SomeType() - -fun SomeType.constructor(): SomeType { - return SomeSubType() -} - - -annotation class A(val p: String) - -val MyBinaryClass.test get() = s() - diff --git a/runners/gradle-integration-tests/testData/multiProjectSingleOut/build.gradle b/runners/gradle-integration-tests/testData/multiProjectSingleOut/build.gradle deleted file mode 100644 index 0ea86d4c..00000000 --- a/runners/gradle-integration-tests/testData/multiProjectSingleOut/build.gradle +++ /dev/null @@ -1,37 +0,0 @@ -plugins { - id 'org.jetbrains.dokka' -} - -subprojects { - buildscript { - repositories { - mavenCentral() - jcenter() - maven { url "https://dl.bintray.com/kotlin/kotlin-eap-1.1" } - maven { url "https://dl.bintray.com/kotlin/kotlin-dev" } - } - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$test_kotlin_version" - } - } - repositories { - mavenCentral() - jcenter() - maven { url "https://dl.bintray.com/kotlin/kotlin-eap-1.1" } - maven { url "https://dl.bintray.com/kotlin/kotlin-dev" } - } -} - -dependencies { - dokkaRuntime files(dokka_fatjar) -} - -apply plugin: 'org.jetbrains.dokka' - -dokka { - configuration { - kotlinTasks { - [":subA:compileKotlin", ":subB:compileKotlin"] - } - } -}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/multiProjectSingleOut/fileTree.txt b/runners/gradle-integration-tests/testData/multiProjectSingleOut/fileTree.txt deleted file mode 100644 index 5624fca6..00000000 --- a/runners/gradle-integration-tests/testData/multiProjectSingleOut/fileTree.txt +++ /dev/null @@ -1,33 +0,0 @@ -/ - multi-project-root/ - alltypes/ - index.html - index-outline.html - index.html - package-list - s1/ - -my-class/ - -init-.html - index.html - otherworks.html - -super/ - -init-.html - bar.html - foo.html - index.html - index.html - some-cool-thing.html - s2/ - -cooler/ - -init-.html - a.html - coolest.html - index.html - my-class.html - -superful/ - -init-.html - bar.html - index.html - index.html - main.html - style.css diff --git a/runners/gradle-integration-tests/testData/multiProjectSingleOut/settings.gradle b/runners/gradle-integration-tests/testData/multiProjectSingleOut/settings.gradle deleted file mode 100644 index 283cc526..00000000 --- a/runners/gradle-integration-tests/testData/multiProjectSingleOut/settings.gradle +++ /dev/null @@ -1,3 +0,0 @@ -rootProject.name = "multiProjectRoot" - -include 'subA', 'subB'
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/multiProjectSingleOut/subA/build.gradle b/runners/gradle-integration-tests/testData/multiProjectSingleOut/subA/build.gradle deleted file mode 100644 index 0600411e..00000000 --- a/runners/gradle-integration-tests/testData/multiProjectSingleOut/subA/build.gradle +++ /dev/null @@ -1,6 +0,0 @@ -apply plugin: 'kotlin' - -dependencies { - compile group: 'org.jetbrains.kotlin', name: 'kotlin-runtime', version: test_kotlin_version - compile group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: test_kotlin_version -} diff --git a/runners/gradle-integration-tests/testData/multiProjectSingleOut/subA/src/main/kotlin/module.kt b/runners/gradle-integration-tests/testData/multiProjectSingleOut/subA/src/main/kotlin/module.kt deleted file mode 100644 index 126d7f3e..00000000 --- a/runners/gradle-integration-tests/testData/multiProjectSingleOut/subA/src/main/kotlin/module.kt +++ /dev/null @@ -1,31 +0,0 @@ -package s1 - -/** - * Coolest one - */ -fun someCoolThing(s: String) = s.repeat(2) - -/** - * Just a class - */ -class MyClass { - /** - * Ultimate answer to all questions - */ - fun otherworks(): Int = 42 -} - -/** - * Just a SUPER class - */ -open class Super { - /** - * Same as [MyClass.otherworks] - */ - fun foo(i: Int = 21) = i * 2 - - /** - * magic - */ - open fun bar() = foo() -}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/multiProjectSingleOut/subB/build.gradle b/runners/gradle-integration-tests/testData/multiProjectSingleOut/subB/build.gradle deleted file mode 100644 index 7b8ff9f3..00000000 --- a/runners/gradle-integration-tests/testData/multiProjectSingleOut/subB/build.gradle +++ /dev/null @@ -1,7 +0,0 @@ -apply plugin: 'kotlin' - -dependencies { - compile group: 'org.jetbrains.kotlin', name: 'kotlin-runtime', version: test_kotlin_version - compile group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: test_kotlin_version - compile project(":subA") -} diff --git a/runners/gradle-integration-tests/testData/multiProjectSingleOut/subB/src/main/kotlin/module.kt b/runners/gradle-integration-tests/testData/multiProjectSingleOut/subB/src/main/kotlin/module.kt deleted file mode 100644 index 8a87590a..00000000 --- a/runners/gradle-integration-tests/testData/multiProjectSingleOut/subB/src/main/kotlin/module.kt +++ /dev/null @@ -1,31 +0,0 @@ -package s2 - -import s1.Super -import s1.MyClass -import s1.someCoolThing - -/** - * Just an entry-point - */ -fun main(args: Array<String>) { - -} - -/** - * Take a glass of hot water - */ -class Cooler { - val myClass = MyClass() - val a = myClass.otherworks() - val coolest = someCoolThing() -} - -/** - * Powerful - */ -class Superful : Super() { - /** - * Overriden magic - */ - override fun bar() = foo(20) * 2 -}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/multiplatformProject/build.gradle b/runners/gradle-integration-tests/testData/multiplatformProject/build.gradle deleted file mode 100644 index b5454c55..00000000 --- a/runners/gradle-integration-tests/testData/multiplatformProject/build.gradle +++ /dev/null @@ -1,60 +0,0 @@ -buildscript { - repositories { - mavenCentral() - jcenter() - maven { url "https://dl.bintray.com/kotlin/kotlin-eap-1.1" } - maven { url "https://dl.bintray.com/kotlin/kotlin-dev" } - } - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$test_kotlin_version" - } -} - -plugins { - id 'org.jetbrains.dokka' -} - -repositories { - jcenter() - mavenLocal() -} - -group 'org.test' -version '1.0-SNAPSHOT' - -apply plugin: "org.jetbrains.kotlin.multiplatform" - -kotlin { - jvm() // Create a JVM target with the default name 'jvm' - js() - sourceSets { - jsMain { - dependencies{ - implementation "org.jetbrains.kotlin:kotlin-stdlib-js" - } - } - jvmMain { - dependencies { - implementation kotlin('stdlib-jdk8') - } - } - } -} - -dependencies { - dokkaRuntime files(dokka_fatjar) -} - -apply plugin: 'org.jetbrains.dokka' - -dokka { - - multiplatform { - javascript { - targets = ["js"] - platform = "js" - kotlinTasks { [tasks.getByPath(":compileKotlinJs")] } - } - jvm {} - } -} diff --git a/runners/gradle-integration-tests/testData/multiplatformProject/fileTree.txt b/runners/gradle-integration-tests/testData/multiplatformProject/fileTree.txt deleted file mode 100644 index e9cc847c..00000000 --- a/runners/gradle-integration-tests/testData/multiplatformProject/fileTree.txt +++ /dev/null @@ -1,18 +0,0 @@ -/ - multiplatform-project-root/ - alltypes/ - index.html - index-outline.html - index.html - org.kotlintestmpp/ - get-current-date.html - index.html - js.html - jvm.html - kotlin.-string/ - index.html - my-extension.html - main.html - shared.html - package-list - style.css diff --git a/runners/gradle-integration-tests/testData/multiplatformProject/settings.gradle b/runners/gradle-integration-tests/testData/multiplatformProject/settings.gradle deleted file mode 100644 index 0bb1e91b..00000000 --- a/runners/gradle-integration-tests/testData/multiplatformProject/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = "multiplatformProjectRoot" diff --git a/runners/gradle-integration-tests/testData/multiplatformProject/src/jsMain/kotlin/org/kotlintestmpp/main.kt b/runners/gradle-integration-tests/testData/multiplatformProject/src/jsMain/kotlin/org/kotlintestmpp/main.kt deleted file mode 100644 index a77b50f9..00000000 --- a/runners/gradle-integration-tests/testData/multiplatformProject/src/jsMain/kotlin/org/kotlintestmpp/main.kt +++ /dev/null @@ -1,14 +0,0 @@ -package org.kotlintestmpp - -fun main(args : Array<String>) { - console.log("Hello, world!") -} - -fun js(){} -fun shared(){} - -fun getCurrentDate(): String { - return "test" -} - -fun String.myExtension() = println("test")
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/multiplatformProject/src/jvmMain/kotlin/org/kotlintestmpp/main.kt b/runners/gradle-integration-tests/testData/multiplatformProject/src/jvmMain/kotlin/org/kotlintestmpp/main.kt deleted file mode 100644 index 96d725fc..00000000 --- a/runners/gradle-integration-tests/testData/multiplatformProject/src/jvmMain/kotlin/org/kotlintestmpp/main.kt +++ /dev/null @@ -1,20 +0,0 @@ -package org.kotlintestmpp - - -fun main(args : Array<String>) { - println("Hello, world!") -} - -/** - * comment for this class - */ -fun jvm(){} -fun shared(){} - -fun getCurrentDate(): String { - return "test" -} - -fun String.myExtension() = println("test2") - - diff --git a/runners/gradle-integration-tests/testData/sourcesChange/build.gradle b/runners/gradle-integration-tests/testData/sourcesChange/build.gradle deleted file mode 100644 index a6270e23..00000000 --- a/runners/gradle-integration-tests/testData/sourcesChange/build.gradle +++ /dev/null @@ -1,35 +0,0 @@ -buildscript { - repositories { - mavenCentral() - jcenter() - maven { url "https://dl.bintray.com/kotlin/kotlin-eap-1.1" } - maven { url "https://dl.bintray.com/kotlin/kotlin-dev" } - } - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$test_kotlin_version" - } -} - -plugins { - id 'org.jetbrains.dokka' -} - -apply plugin: 'kotlin' -apply plugin: 'org.jetbrains.dokka' - -repositories { - mavenCentral() - jcenter() - maven { - url "https://dl.bintray.com/kotlin/kotlin-eap-1.1" - } - maven { - url "https://dl.bintray.com/kotlin/kotlin-dev" - } -} - -dependencies { - dokkaRuntime files(dokka_fatjar) - compile group: 'org.jetbrains.kotlin', name: 'kotlin-runtime', version: test_kotlin_version - compile group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: test_kotlin_version -}
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/sourcesChange/fileTree.txt b/runners/gradle-integration-tests/testData/sourcesChange/fileTree.txt deleted file mode 100644 index 09f3724b..00000000 --- a/runners/gradle-integration-tests/testData/sourcesChange/fileTree.txt +++ /dev/null @@ -1,10 +0,0 @@ -/ - sources-change/ - alltypes.html - demo/ - hello.html - index.html - index-outline.html - index.html - package-list - style.css diff --git a/runners/gradle-integration-tests/testData/sourcesChange/fileTree1.txt b/runners/gradle-integration-tests/testData/sourcesChange/fileTree1.txt deleted file mode 100644 index eeb377f7..00000000 --- a/runners/gradle-integration-tests/testData/sourcesChange/fileTree1.txt +++ /dev/null @@ -1,11 +0,0 @@ -/ - sources-change/ - alltypes.html - demo/ - hello.html - index.html - world.html - index-outline.html - index.html - package-list - style.css diff --git a/runners/gradle-integration-tests/testData/sourcesChange/settings.gradle b/runners/gradle-integration-tests/testData/sourcesChange/settings.gradle deleted file mode 100644 index 3fb032bf..00000000 --- a/runners/gradle-integration-tests/testData/sourcesChange/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = "sourcesChange"
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/sourcesChange/src/main/kotlin/demo/HelloWorld.kt b/runners/gradle-integration-tests/testData/sourcesChange/src/main/kotlin/demo/HelloWorld.kt deleted file mode 100644 index c54dea50..00000000 --- a/runners/gradle-integration-tests/testData/sourcesChange/src/main/kotlin/demo/HelloWorld.kt +++ /dev/null @@ -1,6 +0,0 @@ -package demo - -/** - * @return Hello - */ -fun hello(): String = "Hello"
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/sourcesChange/src1/main/kotlin/demo/HelloWorld.kt b/runners/gradle-integration-tests/testData/sourcesChange/src1/main/kotlin/demo/HelloWorld.kt deleted file mode 100644 index 53f22ff5..00000000 --- a/runners/gradle-integration-tests/testData/sourcesChange/src1/main/kotlin/demo/HelloWorld.kt +++ /dev/null @@ -1,11 +0,0 @@ -package demo - -/** - * @return Hello - */ -fun hello(): String = "Hello" - -/** - * @return World - */ -fun world(): String = "World"
\ No newline at end of file diff --git a/runners/gradle-integration-tests/testData/typeSafeConfiguration/build.gradle b/runners/gradle-integration-tests/testData/typeSafeConfiguration/build.gradle deleted file mode 100644 index 444d2ab3..00000000 --- a/runners/gradle-integration-tests/testData/typeSafeConfiguration/build.gradle +++ /dev/null @@ -1,74 +0,0 @@ -import org.jetbrains.dokka.* -import org.jetbrains.dokka.gradle.* -import org.jetbrains.kotlin.gradle.tasks.* - -import groovy.transform.CompileStatic -import java.util.concurrent.Callable - -buildscript { - repositories { - jcenter() - mavenLocal() - } - dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$test_kotlin_version" - } -} - -plugins { - id 'org.jetbrains.dokka' -} - -apply plugin: 'kotlin' - -@CompileStatic -def configureDokkaTypeSafely(DokkaTask dokka) { - dokka.with { - outputFormat = "some String" - outputDirectory = "some String" - cacheRoot = null as String - impliedPlatforms = new ArrayList<String>() - } - dokka.configuration.with { - moduleName = "some String" - classpath = Arrays.asList("someClassDir") - includes = Collections.<String> emptyList() - samples = Collections.<String> emptyList() - jdkVersion = 6 - sourceRoots = new ArrayList<GradleSourceRootImpl>() as List<DokkaConfiguration.SourceRoot> - - includeNonPublic = false - skipDeprecated = false - skipEmptyPackages = true - reportUndocumented = true - perPackageOptions = new ArrayList<GradlePackageOptionsImpl>() as List<DokkaConfiguration.PackageOptions> - externalDocumentationLinks = new ArrayList<DokkaConfiguration.ExternalDocumentationLink>() - noStdlibLink = false - languageVersion = null as String - apiVersion = null as String - sourceRoot(new Action<GradleSourceRootImpl>() { - @Override - void execute(GradleSourceRootImpl sourceRoot) { - sourceRoot.path = "some String" - } - }) - externalDocumentationLink(new Action<GradleExternalDocumentationLinkImpl.Builder>() { - @Override - void execute(GradleExternalDocumentationLinkImpl.Builder builder) { - builder.url = uri("some URI").toURL() - builder.packageListUrl = uri("some URI").toURL() - builder.build() - } - }) - kotlinTasks(new Callable<List<Object>>() { - @Override - List<Object> call() { - return defaultKotlinTasks() - } - }) - } -} - -project.tasks.withType(DokkaTask) { dokka -> - configureDokkaTypeSafely(dokka) -} diff --git a/runners/gradle-integration-tests/testData/typeSafeConfiguration/settings.gradle b/runners/gradle-integration-tests/testData/typeSafeConfiguration/settings.gradle deleted file mode 100644 index be82e328..00000000 --- a/runners/gradle-integration-tests/testData/typeSafeConfiguration/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = "type-safe-configuration"
\ No newline at end of file diff --git a/runners/gradle-plugin/build.gradle b/runners/gradle-plugin/build.gradle deleted file mode 100644 index ceb03bae..00000000 --- a/runners/gradle-plugin/build.gradle +++ /dev/null @@ -1,108 +0,0 @@ -import com.gradle.publish.DependenciesBuilder - -apply plugin: 'java' -apply plugin: 'kotlin' - - -apply plugin: 'com.github.johnrengelman.shadow' -apply plugin: "com.gradle.plugin-publish" - -sourceCompatibility = 1.8 - -tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { - kotlinOptions { - freeCompilerArgs += "-Xjsr305=strict" - languageVersion = language_version - apiVersion = language_version - jvmTarget = "1.8" - } -} - -repositories { - jcenter() - google() -} - -dependencies { - testCompile group: 'junit', name: 'junit', version: '4.12' - - shadow group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib', version: kotlin_for_gradle_runtime_version - shadow group: 'org.jetbrains.kotlin', name: 'kotlin-reflect', version: kotlin_for_gradle_runtime_version - - compile project(":integration") - - compileOnly "org.jetbrains.kotlin:kotlin-gradle-plugin" - compileOnly("com.android.tools.build:gradle:3.0.0") - compileOnly("com.android.tools.build:gradle-core:3.0.0") - compileOnly("com.android.tools.build:builder-model:3.0.0") - compileOnly gradleApi() - compileOnly localGroovy() - implementation "com.google.code.gson:gson:$gson_version" -} - -task sourceJar(type: Jar) { - from sourceSets.main.allSource -} - -processResources { - eachFile { - if (it.name == "org.jetbrains.dokka.properties") { - it.filter { line -> - line.replace("<version>", dokka_version) - } - } - } -} - -shadowJar { - baseName = 'dokka-gradle-plugin' - classifier = '' -} - -apply plugin: 'maven-publish' - -publishing { - publications { - dokkaGradlePlugin(MavenPublication) { publication -> - artifactId = 'dokka-gradle-plugin' - - artifact sourceJar { - classifier "sources" - } - - project.shadow.component(publication) - } - } -} - -bintrayPublication(project, ['dokkaGradlePlugin']) - -configurations.archives.artifacts.clear() -artifacts { - archives shadowJar -} - -pluginBundle { - website = 'https://www.kotlinlang.org/' - vcsUrl = 'https://github.com/kotlin/dokka.git' - description = 'Dokka, the Kotlin documentation tool' - tags = ['dokka', 'kotlin', 'kdoc', 'android'] - - plugins { - dokkaGradlePlugin { - id = 'org.jetbrains.dokka' - displayName = 'Dokka plugin' - } - } - - withDependencies { List<Dependency> list -> - list.clear() - def builder = new DependenciesBuilder() - builder.addUniqueScopedDependencies(list, configurations.shadow, "compile") - } - - mavenCoordinates { - groupId = "org.jetbrains.dokka" - artifactId = "dokka-gradle-plugin" - } -}
\ No newline at end of file diff --git a/runners/gradle-plugin/build.gradle.kts b/runners/gradle-plugin/build.gradle.kts new file mode 100644 index 00000000..0222f5e0 --- /dev/null +++ b/runners/gradle-plugin/build.gradle.kts @@ -0,0 +1,63 @@ +import org.jetbrains.configureBintrayPublication +import org.jetbrains.dokkaVersion + +plugins { + `java-gradle-plugin` +} + +repositories { + google() +} + +dependencies { + implementation(project(":core")) + compileOnly("org.jetbrains.kotlin:kotlin-gradle-plugin") + compileOnly("com.android.tools.build:gradle:3.0.0") + compileOnly("com.android.tools.build:gradle-core:3.0.0") + compileOnly("com.android.tools.build:builder-model:3.0.0") + compileOnly(gradleApi()) + compileOnly(gradleKotlinDsl()) + testImplementation(gradleApi()) + testImplementation(gradleKotlinDsl()) + testImplementation(kotlin("test-junit")) + testImplementation("org.jetbrains.kotlin:kotlin-gradle-plugin") + + constraints { + val kotlin_version: String by project + compileOnly("org.jetbrains.kotlin:kotlin-reflect:${kotlin_version}") { + because("kotlin-gradle-plugin and :core both depend on this") + } + } +} + +val sourceJar by tasks.registering(Jar::class) { + archiveClassifier.set("sources") + from(sourceSets["main"].allSource) +} + +gradlePlugin { + plugins { + create("dokkaGradlePlugin") { + id = "org.jetbrains.dokka" + implementationClass = "org.jetbrains.dokka.gradle.DokkaPlugin" + version = dokkaVersion + } + } +} + +publishing { + publications { + register<MavenPublication>("pluginMaven") { + artifactId = "dokka-gradle-plugin" + } + + register<MavenPublication>("dokkaGradlePluginForIntegrationTests") { + artifactId = "dokka-gradle-plugin" + from(components["java"]) + version = "for-integration-tests-SNAPSHOT" + } + } +} + + +configureBintrayPublication("dokkaGradlePluginPluginMarkerMaven", "pluginMaven") diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaTask.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaTask.kt new file mode 100644 index 00000000..846f021c --- /dev/null +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaTask.kt @@ -0,0 +1,45 @@ +package org.jetbrains.dokka.gradle + +import org.gradle.api.DefaultTask +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.attributes.Usage +import org.gradle.api.tasks.Classpath +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.TaskAction +import org.gradle.kotlin.dsl.dependencies +import org.jetbrains.dokka.DokkaBootstrap +import org.jetbrains.dokka.plugability.Configurable + + +abstract class AbstractDokkaTask : DefaultTask(), Configurable { + @Input + var outputDirectory: String = defaultDokkaOutputDirectory().absolutePath + + @Input + override val pluginsConfiguration: Map<String, String> = mutableMapOf() + + @Classpath + val plugins: Configuration = project.maybeCreateDokkaPluginConfiguration(name) + + @Classpath + val runtime: Configuration = project.maybeCreateDokkaRuntimeConfiguration(name) + + @TaskAction + protected fun run() { + val kotlinColorsEnabledBefore = System.getProperty(DokkaTask.COLORS_ENABLED_PROPERTY) ?: "false" + System.setProperty(DokkaTask.COLORS_ENABLED_PROPERTY, "false") + try { + generate() + } finally { + System.setProperty(DokkaTask.COLORS_ENABLED_PROPERTY, kotlinColorsEnabledBefore) + } + } + + protected abstract fun generate() + + protected fun DokkaBootstrap(bootstrapClassFQName: String): DokkaBootstrap { + return DokkaBootstrap(runtime, bootstrapClassFQName) + } +} diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/ConfigurationExtractor.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/ConfigurationExtractor.kt index 39672b9a..1bfd2c78 100644 --- a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/ConfigurationExtractor.kt +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/ConfigurationExtractor.kt @@ -1,9 +1,5 @@ package org.jetbrains.dokka.gradle -import com.android.build.gradle.* -import com.android.build.gradle.api.BaseVariant -import com.android.builder.core.BuilderConstants -import org.gradle.api.NamedDomainObjectCollection import org.gradle.api.Project import org.gradle.api.Task import org.gradle.api.UnknownDomainObjectException @@ -13,90 +9,71 @@ import org.gradle.api.plugins.JavaPluginConvention import org.gradle.api.tasks.SourceSet import org.gradle.api.tasks.compile.AbstractCompile import org.jetbrains.dokka.ReflectDsl -import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension +import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension import org.jetbrains.kotlin.gradle.dsl.KotlinSingleTargetExtension import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation -import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType -import org.jetbrains.kotlin.gradle.plugin.KotlinTarget +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet import org.jetbrains.kotlin.gradle.tasks.KotlinCompile -import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult import java.io.File import java.io.Serializable class ConfigurationExtractor(private val project: Project) { - - fun extractConfiguration(targetName: String, variantNames: List<String>) = - if (project.isMultiplatformProject()) { - extractFromMultiPlatform(targetName, variantNames) - } else { - extractFromSinglePlatform(variantNames) + fun extractConfiguration(sourceSetName: String): PlatformData? { + val projectExtension = project.extensions.findByType(KotlinProjectExtension::class.java) ?: run { + project.logger.error("Missing kotlin project extension") + return null } - private fun extractFromSinglePlatform(variantNames: List<String>): PlatformData? { - val target: KotlinTarget - try { - target = project.extensions.getByType(KotlinSingleTargetExtension::class.java).target - } catch (e: Throwable) { - when (e) { - is UnknownDomainObjectException, is NoClassDefFoundError, is ClassNotFoundException -> - return null - else -> throw e - } + val sourceSet = projectExtension.sourceSets.findByName(sourceSetName) ?: run { + project.logger.error("No source set with name '$sourceSetName' found") + return null } - return try { - PlatformData( - null, - accumulateClassPaths(variantNames, target), - accumulateSourceSets(variantNames, target), - getPlatformName(target.platformType) - ) - } catch (e: NoSuchMethodError) { + val compilation = try { + when (projectExtension) { + is KotlinMultiplatformExtension -> { + val targets = projectExtension.targets.flatMap { it.compilations } + targets.find { it.name == sourceSetName } + ?: targets.find { it.kotlinSourceSets.contains(sourceSet) } + } + is KotlinSingleTargetExtension -> projectExtension.target.compilations.find { + it.kotlinSourceSets.contains(sourceSet) + } + else -> null + } + } catch (e: NoClassDefFoundError) { // Old Kotlin plugin versions null } + + val sourceRoots = sourceSet.sourceFiles + val classpath = compilation?.classpath + ?: sourceRoots + sourceSet.allParentSourceFiles() + + return PlatformData( + sourceSetName, + classpath.filter { it.exists() }, + sourceRoots, + sourceSet.dependsOn.map { it.name }, + compilation?.target?.platformType?.name ?: "common" + ) } - private fun extractFromMultiPlatform(targetName: String, variantNames: List<String>): PlatformData? = - try { - project.extensions.getByType(KotlinMultiplatformExtension::class.java).targets - } catch (e: Throwable) { - when (e) { - is UnknownDomainObjectException, is NoClassDefFoundError, is ClassNotFoundException -> - null - else -> throw e - } - }?.let { - val fixedName = if (targetName.toLowerCase() == "common") "metadata" else targetName.toLowerCase() - it.find { target -> target.name.toLowerCase() == fixedName }?.let { target -> - PlatformData( - fixedName, - accumulateClassPaths(variantNames, target), - accumulateSourceSets(variantNames, target), - target.platformType.toString() - ) - } - } + private fun KotlinSourceSet.allParentSourceFiles(): List<File> = + sourceFiles + dependsOn.flatMap { it.allParentSourceFiles() } fun extractFromJavaPlugin(): PlatformData? = project.convention.findPlugin(JavaPluginConvention::class.java) ?.run { sourceSets.findByName(SourceSet.MAIN_SOURCE_SET_NAME)?.allSource?.srcDirs } - ?.let { PlatformData(null, emptyList(), it.toList(), "") } + ?.let { PlatformData(null, emptyList(), it.toList(), emptyList(), "") } - fun extractFromKotlinTasks(kotlinTasks: List<Task>): PlatformData? = + fun extractFromKotlinTasks(kotlinTasks: List<Task>): List<PlatformData> = try { - kotlinTasks.map { extractFromKotlinTask(it) }.let { platformDataList -> - PlatformData( - null, - platformDataList.flatMap { it.classpath }, - platformDataList.flatMap { it.sourceRoots }, - "" - ) - } + kotlinTasks.map { extractFromKotlinTask(it) } } catch (e: Throwable) { when (e) { is UnknownDomainObjectException, is NoClassDefFoundError, is ClassNotFoundException -> - extractFromKotlinTasksTheHardWay(kotlinTasks) + listOfNotNull(extractFromKotlinTasksTheHardWay(kotlinTasks)) else -> throw e } } @@ -110,10 +87,18 @@ class ConfigurationExtractor(private val project: Project) { when (e) { is UnknownDomainObjectException, is NoClassDefFoundError, is ClassNotFoundException -> project.extensions.getByType(KotlinMultiplatformExtension::class.java).targets - .firstNotNullResult { target -> target.compilations.find { it.compileKotlinTask == task } } + .flatMap { it.compilations }.firstOrNull { it.compileKotlinTask == task } else -> throw e } - }.let { PlatformData(task.name, getClasspath(it), getSourceSet(it), it?.platformType?.toString() ?: "") } + }.let { compilation -> + PlatformData( + task.name, + compilation?.classpath.orEmpty(), + compilation?.sourceFiles.orEmpty(), + compilation?.dependentSourceSets?.map { it.name }.orEmpty(), + compilation?.platformType?.toString() ?: "" + ) + } private fun extractFromKotlinTasksTheHardWay(kotlinTasks: List<Task>): PlatformData? { val allClasspath = mutableSetOf<File>() @@ -134,8 +119,8 @@ class ConfigurationExtractor(private val project: Project) { val taskClasspath: Iterable<File> = (it["getClasspath", AbstractCompile::class].takeIfIsFunc()?.invoke() - ?: it["compileClasspath", abstractKotlinCompileClz].takeIfIsProp()?.v() - ?: it["getClasspath", abstractKotlinCompileClz]()) + ?: it["compileClasspath", abstractKotlinCompileClz].takeIfIsProp()?.v() + ?: it["getClasspath", abstractKotlinCompileClz]()) if (taskClasspath is FileCollection) { allClasspathFileCollection += taskClasspath @@ -152,97 +137,42 @@ class ConfigurationExtractor(private val project: Project) { } classpath.addAll(project.files(allClasspath).toList()) - return PlatformData(null, classpath, allSourceRoots.toList(), "") + return PlatformData(null, classpath, allSourceRoots.toList(), emptyList(), "") } - private fun getSourceSet(target: KotlinTarget, variantName: String): List<File> = - if (target.isAndroidTarget()) - getSourceSet(getCompilation(target, variantName)) - else - getSourceSet(getMainCompilation(target)) - - private fun getClasspath(target: KotlinTarget, variantName: String): List<File> = - if (target.isAndroidTarget()) - getClasspathFromAndroidTask(getCompilation(target, variantName)) - else - getClasspath(getMainCompilation(target)) - - private fun getSourceSet(compilation: KotlinCompilation<*>?): List<File> = compilation - ?.allKotlinSourceSets - ?.flatMap { it.kotlin.sourceDirectories } - ?.filter { it.exists() } - .orEmpty() - - private fun getClasspath(compilation: KotlinCompilation<*>?): List<File> = compilation - ?.compileDependencyFiles - ?.files - ?.toList() - ?.filter { it.exists() } - .orEmpty() - - // This is a workaround for KT-33893 - private fun getClasspathFromAndroidTask(compilation: KotlinCompilation<*>): List<File> = (compilation - .compileKotlinTask as? KotlinCompile) - ?.classpath?.files?.toList() ?: getClasspath(compilation) - - private fun getMainCompilation(target: KotlinTarget) = - getCompilation(target, getMainCompilationName(target)) - - private fun getCompilation(target: KotlinTarget, name: String) = - target.compilations.getByName(name) - - private fun getMainCompilationName(target: KotlinTarget) = if (target.isAndroidTarget()) - getVariants(project).filter { it.buildType.name == BuilderConstants.RELEASE }.map { it.name }.first() - else - KotlinCompilation.MAIN_COMPILATION_NAME - - private fun getVariants(project: Project): Set<BaseVariant> { - val androidExtension = project.extensions.getByName("android") - val baseVariants = when (androidExtension) { - is AppExtension -> androidExtension.applicationVariants.toSet() - is LibraryExtension -> { - androidExtension.libraryVariants.toSet() + - if (androidExtension is FeatureExtension) { - androidExtension.featureVariants.toSet() - } else { - emptySet<BaseVariant>() - } - } - is TestExtension -> androidExtension.applicationVariants.toSet() - else -> emptySet() - } - val testVariants = if (androidExtension is TestedExtension) { - androidExtension.testVariants.toSet() + androidExtension.unitTestVariants.toSet() - } else { - emptySet<BaseVariant>() - } + private val KotlinCompilation<*>.sourceFiles: List<File> + get() = kotlinSourceSets.flatMap { it.sourceFiles } - return baseVariants + testVariants - } + private val KotlinSourceSet.sourceFiles: List<File> + get() = kotlin.sourceDirectories.filter { it.exists() }.toList() - private fun getPlatformName(platform: KotlinPlatformType): String = - if (platform == KotlinPlatformType.androidJvm) KotlinPlatformType.jvm.toString() else platform.toString() + private val KotlinCompilation<*>.dependentSourceSets: Set<KotlinSourceSet> + get() = (allKotlinSourceSets - kotlinSourceSets) - private fun accumulateClassPaths(variantNames: List<String>, target: KotlinTarget) = - if (variantNames.isNotEmpty()) { - variantNames.flatMap { getClasspath(target, it) }.distinct() + private val KotlinCompilation<*>.classpath: List<File> + get() = if (target.isAndroidTarget()) { + getClasspathFromAndroidTask(this) } else { - if (target.isAndroidTarget()) - getClasspathFromAndroidTask(getMainCompilation(target)) - else - getClasspath(getMainCompilation(target)) + getClasspathFromRegularTask(this) } - private fun accumulateSourceSets(variantNames: List<String>, target: KotlinTarget) = - if (variantNames.isNotEmpty()) - variantNames.flatMap { getSourceSet(target, it) }.distinct() - else - getSourceSet(getMainCompilation(target)) + // This is a workaround for KT-33893 + private fun getClasspathFromAndroidTask(compilation: KotlinCompilation<*>): List<File> = (compilation + .compileKotlinTask as? KotlinCompile) + ?.classpath?.files?.toList() ?: getClasspathFromRegularTask(compilation) + + private fun getClasspathFromRegularTask(compilation: KotlinCompilation<*>): List<File> = + compilation + .compileDependencyFiles + .files + .toList() + .filter { it.exists() } data class PlatformData( val name: String?, val classpath: List<File>, val sourceRoots: List<File>, + val dependentSourceSets: List<String>, val platform: String ) : Serializable -}
\ No newline at end of file +} diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaArtifacts.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaArtifacts.kt new file mode 100644 index 00000000..90d51015 --- /dev/null +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaArtifacts.kt @@ -0,0 +1,17 @@ +package org.jetbrains.dokka.gradle + +import org.gradle.api.Project +import org.jetbrains.dokka.DokkaVersion + +internal val Project.dokkaArtifacts get() = DokkaArtifacts(this) + +internal class DokkaArtifacts(private val project: Project) { + private fun fromModuleName(name: String) = + project.dependencies.create("org.jetbrains.dokka:$name:${DokkaVersion.version}") + + val dokkaCore get() = fromModuleName("dokka-core") + val dokkaBase get() = fromModuleName("dokka-base") + val javadocPlugin get() = fromModuleName("javadoc-plugin") + val gfmPlugin get() = fromModuleName("gfm-plugin") + val jekyllPlugin get() = fromModuleName("jekyll-plugin") +} diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaBootstrapFactory.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaBootstrapFactory.kt new file mode 100644 index 00000000..df29c19b --- /dev/null +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaBootstrapFactory.kt @@ -0,0 +1,18 @@ +package org.jetbrains.dokka.gradle + +import org.gradle.api.artifacts.Configuration +import org.jetbrains.dokka.DokkaBootstrap +import java.net.URLClassLoader + + +fun DokkaBootstrap(configuration: Configuration, bootstrapClassFQName: String): DokkaBootstrap { + val runtimeJars = configuration.resolve() + val runtimeClassLoader = URLClassLoader( + runtimeJars.map { it.toURI().toURL() }.toTypedArray(), + ClassLoader.getSystemClassLoader().parent + ) + + val bootstrapClass = runtimeClassLoader.loadClass(bootstrapClassFQName) + val bootstrapInstance = bootstrapClass.constructors.first().newInstance() + return automagicTypedProxy(DokkaPlugin::class.java.classLoader, bootstrapInstance) +} diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaCollectorTask.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaCollectorTask.kt new file mode 100644 index 00000000..37952ea8 --- /dev/null +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaCollectorTask.kt @@ -0,0 +1,58 @@ +package org.jetbrains.dokka.gradle + +import org.gradle.api.DefaultTask +import org.gradle.api.plugins.JavaBasePlugin +import org.gradle.api.plugins.JavaBasePlugin.DOCUMENTATION_GROUP +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.TaskAction +import java.lang.IllegalStateException + +open class DokkaCollectorTask : DefaultTask() { + + @Input + var modules: List<String> = emptyList() + + @Input + var outputDirectory: String = defaultDokkaOutputDirectory().absolutePath + + private lateinit var configuration: GradleDokkaConfigurationImpl + + @Input + var dokkaTaskNames: Set<String> = setOf() + + @TaskAction + fun collect() { + val configurations = project.subprojects + .filter { subProject -> subProject.name in modules } + .flatMap { subProject -> dokkaTaskNames.mapNotNull(subProject.tasks::findByName) } + .filterIsInstance<DokkaTask>() + .mapNotNull { dokkaTask -> dokkaTask.getConfigurationOrNull() } + + + val initial = GradleDokkaConfigurationImpl().apply { + outputDir = outputDirectory + cacheRoot = configurations.first().cacheRoot + } + + // TODO this certainly not the ideal solution + configuration = configurations.fold(initial) { acc, it: GradleDokkaConfigurationImpl -> + if (acc.cacheRoot != it.cacheRoot) + throw IllegalStateException("Dokka task configurations differ on core argument cacheRoot") + acc.sourceSets = acc.sourceSets + it.sourceSets + acc.pluginsClasspath = (acc.pluginsClasspath + it.pluginsClasspath).distinct() + acc + } + project.tasks.withType(DokkaTask::class.java).configureEach { it.enforcedConfiguration = configuration } + } + + init { + // TODO: This this certainly not the ideal solution + dokkaTaskNames.forEach { dokkaTaskName -> + finalizedBy(dokkaTaskName) + } + + group = DOCUMENTATION_GROUP + } + + +} diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaMultimoduleTask.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaMultimoduleTask.kt new file mode 100644 index 00000000..6fd58afe --- /dev/null +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaMultimoduleTask.kt @@ -0,0 +1,68 @@ +package org.jetbrains.dokka.gradle + +import com.google.gson.GsonBuilder +import org.gradle.api.plugins.JavaBasePlugin +import org.gradle.api.plugins.JavaBasePlugin.DOCUMENTATION_GROUP +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Internal +import org.jetbrains.dokka.plugability.Configurable + +open class DokkaMultimoduleTask : AbstractDokkaTask(), Configurable { + + @Input + var documentationFileName: String = "README.md" + + + @Input + var dokkaTaskNames: Set<String> = setOf() + set(value) { + field = value.toSet() + setDependsOn(getSubprojectDokkaTasks(value)) + } + + + override fun generate() { + val bootstrap = DokkaBootstrap("org.jetbrains.dokka.DokkaMultimoduleBootstrapImpl") + val gson = GsonBuilder().setPrettyPrinting().create() + val configuration = getConfiguration() + bootstrap.configure(gson.toJson(configuration)) { level, message -> + when (level) { + "debug" -> logger.debug(message) + "info" -> logger.info(message) + "progress" -> logger.lifecycle(message) + "warn" -> logger.warn(message) + "error" -> logger.error(message) + } + } + bootstrap.generate() + } + + @Internal + internal fun getConfiguration(): GradleDokkaConfigurationImpl = + GradleDokkaConfigurationImpl().apply { + outputDir = project.file(outputDirectory).absolutePath + pluginsClasspath = plugins.resolve().toList() + pluginsConfiguration = this@DokkaMultimoduleTask.pluginsConfiguration + modules = getSubprojectDokkaTasks(dokkaTaskNames).map { dokkaTask -> + GradleDokkaModuleDescription().apply { + name = dokkaTask.project.name + path = dokkaTask.project.projectDir.resolve(dokkaTask.outputDirectory) + .toRelativeString(project.file(outputDirectory)) + docFile = dokkaTask.project.projectDir.resolve(documentationFileName).absolutePath + } + } + } + + private fun getSubprojectDokkaTasks(dokkaTaskName: String): List<DokkaTask> { + return project.subprojects + .mapNotNull { subproject -> subproject.tasks.findByName(dokkaTaskName) as? DokkaTask } + } + + private fun getSubprojectDokkaTasks(dokkaTaskNames: Set<String>): List<DokkaTask> { + return dokkaTaskNames.flatMap { dokkaTaskName -> getSubprojectDokkaTasks(dokkaTaskName) } + } + + init { + group = DOCUMENTATION_GROUP + } +} diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaSourceSetIDFactory.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaSourceSetIDFactory.kt new file mode 100644 index 00000000..3fadb4fd --- /dev/null +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaSourceSetIDFactory.kt @@ -0,0 +1,10 @@ +@file:Suppress("FunctionName") + +package org.jetbrains.dokka.gradle + +import org.gradle.api.Project +import org.jetbrains.dokka.DokkaSourceSetID + +internal fun DokkaSourceSetID(project: Project, sourceSetName: String): DokkaSourceSetID { + return DokkaSourceSetID(moduleName = project.path, sourceSetName = sourceSetName) +} diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaTask.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaTask.kt index bafe657e..0d7e74a3 100644 --- a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaTask.kt +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaTask.kt @@ -1,28 +1,26 @@ package org.jetbrains.dokka.gradle import com.google.gson.GsonBuilder -import org.gradle.api.* -import org.gradle.api.artifacts.Configuration +import org.gradle.api.NamedDomainObjectContainer +import org.gradle.api.Project +import org.gradle.api.Task import org.gradle.api.file.FileCollection import org.gradle.api.internal.plugins.DslObject import org.gradle.api.plugins.JavaBasePlugin import org.gradle.api.tasks.* -import org.jetbrains.dokka.DokkaBootstrap import org.jetbrains.dokka.DokkaConfiguration.ExternalDocumentationLink.Builder import org.jetbrains.dokka.DokkaConfiguration.SourceRoot +import org.jetbrains.dokka.DokkaException import org.jetbrains.dokka.Platform import org.jetbrains.dokka.ReflectDsl import org.jetbrains.dokka.ReflectDsl.isNotInstance import org.jetbrains.dokka.gradle.ConfigurationExtractor.PlatformData -import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType import java.io.File -import java.net.URLClassLoader import java.util.concurrent.Callable -import java.util.function.BiConsumer -open class DokkaTask : DefaultTask() { +open class DokkaTask : AbstractDokkaTask() { private val ANDROID_REFERENCE_URL = Builder("https://developer.android.com/reference/").build() - private val GLOBAL_PLATFORM_NAME = "global" // Used for copying perPackageOptions to other platforms + private val configExtractor = ConfigurationExtractor(project) @Suppress("MemberVisibilityCanBePrivate") fun defaultKotlinTasks(): List<Task> = with(ReflectDsl) { @@ -44,60 +42,50 @@ open class DokkaTask : DefaultTask() { dependsOn(Callable { kotlinTasks.map { it.taskDependencies } }) } - @Input - var outputFormat: String = "html" - - @Input - var outputDirectory: String = "" - - var dokkaRuntime: Configuration? = null - - @Input - var subProjects: List<String> = emptyList() - - @Input - var impliedPlatforms: MutableList<String> = arrayListOf() - @Optional @Input var cacheRoot: String? = null - var multiplatform: NamedDomainObjectContainer<GradlePassConfigurationImpl> - @Suppress("UNCHECKED_CAST") - @Nested get() = (DslObject(this).extensions.getByName(MULTIPLATFORM_EXTENSION_NAME) as NamedDomainObjectContainer<GradlePassConfigurationImpl>) - internal set(value) = DslObject(this).extensions.add(MULTIPLATFORM_EXTENSION_NAME, value) - var configuration: GradlePassConfigurationImpl - @Suppress("UNCHECKED_CAST") - @Nested get() = DslObject(this).extensions.getByType(GradlePassConfigurationImpl::class.java) - internal set(value) = DslObject(this).extensions.add(CONFIGURATION_EXTENSION_NAME, value) + /** + * Hack used by DokkaCollector to enforce a different configuration to be used. + */ + @get:Internal + internal var enforcedConfiguration: GradleDokkaConfigurationImpl? = null - // Configure Dokka with closure in Gradle Kotlin DSL - fun configuration(action: Action<in GradlePassConfigurationImpl>) = action.execute(configuration) + @get:Nested + val dokkaSourceSets: NamedDomainObjectContainer<GradleDokkaSourceSet> = + project.container(GradleDokkaSourceSet::class.java) { name -> GradleDokkaSourceSet(name, project) } + .also { container -> DslObject(this).extensions.add("dokkaSourceSets", container) } - private val kotlinTasks: List<Task> by lazy { extractKotlinCompileTasks(configuration.collectKotlinTasks ?: { defaultKotlinTasks() }) } - private val configExtractor = ConfigurationExtractor(project) + private val kotlinTasks: List<Task> by lazy { + extractKotlinCompileTasks( + dokkaSourceSets.mapNotNull { + it.collectKotlinTasks?.invoke() + }.takeIf { it.isNotEmpty() }?.flatten() ?: defaultKotlinTasks() + ) + } @Input var disableAutoconfiguration: Boolean = false - private var outputDiagnosticInfo: Boolean = false // Workaround for Gradle, which fires some methods (like collectConfigurations()) multiple times in its lifecycle + @Input + var failOnWarning: Boolean = false - private fun loadFatJar() { - if (ClassloaderContainer.fatJarClassLoader == null) { - val jars = dokkaRuntime!!.resolve().toList() - ClassloaderContainer.fatJarClassLoader = URLClassLoader(jars.map { it.toURI().toURL() }.toTypedArray(), ClassLoader.getSystemClassLoader().parent) - } - } + @Input + var offlineMode: Boolean = false - private fun extractKotlinCompileTasks(collectTasks: () -> List<Any?>?): List<Task> { - val inputList = (collectTasks.invoke() ?: emptyList()).filterNotNull() - val (paths, other) = inputList.partition { it is String } + private var outputDiagnosticInfo: Boolean = + false // Workaround for Gradle, which fires some methods (like collectConfigurations()) multiple times in its lifecycle - val taskContainer = project.tasks + protected fun extractKotlinCompileTasks(collectTasks: List<Any?>?): List<Task> { + val inputList = (collectTasks ?: emptyList()).filterNotNull() + val (paths, other) = inputList.partition { it is String } - val tasksByPath = paths.map { taskContainer.findByPath(it as String) ?: throw IllegalArgumentException("Task with path '$it' not found") } + val tasksByPath = paths.map { + project.tasks.findByPath(it as String) ?: throw IllegalArgumentException("Task with path '$it' not found") + } other .filter { it !is Task || it isNotInstance getAbstractKotlinCompileFor(it) } @@ -111,11 +99,14 @@ open class DokkaTask : DefaultTask() { return (tasksByPath + other) as List<Task> } - private fun Iterable<File>.toSourceRoots(): List<GradleSourceRootImpl> = this.filter { it.exists() }.map { GradleSourceRootImpl().apply { path = it.path } } - private fun Iterable<String>.toProjects(): List<Project> = project.subprojects.toList().filter { this.contains(it.name) } + private fun Iterable<File>.toSourceRoots(): List<GradleSourceRootImpl> = + this.filter { it.exists() }.map { GradleSourceRootImpl().apply { path = it.path } } + + private fun Iterable<String>.toProjects(): List<Project> = + project.subprojects.toList().filter { this.contains(it.name) } protected open fun collectSuppressedFiles(sourceRoots: List<SourceRoot>) = - if(project.isAndroidProject()) { + if (project.isAndroidProject()) { val generatedRoot = project.buildDir.resolve("generated").absoluteFile sourceRoots .map { File(it.path) } @@ -126,153 +117,152 @@ open class DokkaTask : DefaultTask() { emptyList() } - @TaskAction - fun generate() { + override fun generate() = enforcedConfiguration?.let { generate(it) } ?: generate(getConfigurationOrThrow()) + + protected open fun generate(configuration: GradleDokkaConfigurationImpl) { outputDiagnosticInfo = true - val kotlinColorsEnabledBefore = System.getProperty(COLORS_ENABLED_PROPERTY) ?: "false" - System.setProperty(COLORS_ENABLED_PROPERTY, "false") - try { - loadFatJar() - - val bootstrapClass = ClassloaderContainer.fatJarClassLoader!!.loadClass("org.jetbrains.dokka.DokkaBootstrapImpl") - val bootstrapInstance = bootstrapClass.constructors.first().newInstance() - val bootstrapProxy: DokkaBootstrap = - automagicTypedProxy(javaClass.classLoader, bootstrapInstance) - - val gson = GsonBuilder().setPrettyPrinting().create() - - val globalConfig = multiplatform.toList().find { it.name.toLowerCase() == GLOBAL_PLATFORM_NAME } - val passConfigurationList = collectConfigurations() - .map { defaultPassConfiguration(it, globalConfig) } - - val configuration = GradleDokkaConfigurationImpl() - configuration.outputDir = outputDirectory - configuration.format = outputFormat - configuration.generateIndexPages = true - configuration.cacheRoot = cacheRoot - configuration.impliedPlatforms = impliedPlatforms - configuration.passesConfigurations = passConfigurationList - - if(passConfigurationList.isEmpty() || passConfigurationList == listOf(globalConfig)) { - println("No pass configurations for generation detected!") - return + val bootstrap = DokkaBootstrap("org.jetbrains.dokka.DokkaBootstrapImpl") + + bootstrap.configure( + GsonBuilder().setPrettyPrinting().create().toJson(configuration) + ) { level, message -> + when (level) { + "debug" -> logger.debug(message) + "info" -> logger.info(message) + "progress" -> logger.lifecycle(message) + "warn" -> logger.warn(message) + "error" -> logger.error(message) } + } + bootstrap.generate() + } - bootstrapProxy.configure( - BiConsumer { level, message -> - when (level) { - "info" -> logger.info(message) - "warn" -> logger.warn(message) - "error" -> logger.error(message) - } - }, - gson.toJson(configuration) - ) - bootstrapProxy.generate() + @Internal + internal fun getConfigurationOrNull(): GradleDokkaConfigurationImpl? { + val defaultModulesConfiguration = configuredDokkaSourceSets + .map { configureDefault(it) }.takeIf { it.isNotEmpty() } + ?: listOf( + configureDefault(configureDokkaSourceSet(dokkaSourceSets.create("main"))) + ).takeIf { project.isNotMultiplatformProject() } ?: emptyList() - } finally { - System.setProperty(COLORS_ENABLED_PROPERTY, kotlinColorsEnabledBefore) + if (defaultModulesConfiguration.isEmpty()) { + return null } - } - protected open fun collectConfigurations() = - if (this.isMultiplatformProject()) collectMultiplatform() else listOf(collectSinglePlatform(configuration)) + return GradleDokkaConfigurationImpl().apply { + outputDir = project.file(outputDirectory).absolutePath + cacheRoot = this@DokkaTask.cacheRoot + offlineMode = this@DokkaTask.offlineMode + sourceSets = defaultModulesConfiguration + pluginsClasspath = plugins.resolve().toList() + pluginsConfiguration = this@DokkaTask.pluginsConfiguration + failOnWarning = this@DokkaTask.failOnWarning + } + } - protected open fun collectMultiplatform() = multiplatform - .filterNot { it.name.toLowerCase() == GLOBAL_PLATFORM_NAME } - .map { collectSinglePlatform(it) } + @Internal + internal fun getConfigurationOrThrow(): GradleDokkaConfigurationImpl { + return getConfigurationOrNull() ?: throw DokkaException( + """ + No source sets to document found. + Make source to configure at least one source set e.g. + + dokka { + dokkaSourceSets { + create("commonMain") { + displayName = "common" + platform = "common" + } + } + } + """ + ) + } - protected open fun collectSinglePlatform(config: GradlePassConfigurationImpl): GradlePassConfigurationImpl { - val userConfig = config.let { - if (it.collectKotlinTasks != null) { - configExtractor.extractFromKotlinTasks(extractKotlinCompileTasks(it.collectKotlinTasks!!)) - ?.let { platformData -> mergeUserConfigurationAndPlatformData(it, platformData) } ?: it - } else { - it + @get:Internal + protected val configuredDokkaSourceSets: List<GradleDokkaSourceSet> + get() = dokkaSourceSets.map { configureDokkaSourceSet(it) } + + private fun configureDokkaSourceSet(config: GradleDokkaSourceSet): GradleDokkaSourceSet { + val userConfig = config + .apply { + collectKotlinTasks?.let { + configExtractor.extractFromKotlinTasks(extractKotlinCompileTasks(it())) + .fold(this) { config, platformData -> + mergeUserConfigurationAndPlatformData(config, platformData) + } + } } - } if (disableAutoconfiguration) return userConfig - val baseConfig = configExtractor.extractConfiguration(userConfig.name, userConfig.androidVariants) + return configExtractor.extractConfiguration(userConfig.name) ?.let { mergeUserConfigurationAndPlatformData(userConfig, it) } - ?: if (this.isMultiplatformProject()) { - if (outputDiagnosticInfo) - logger.warn("Could not find target with name: ${userConfig.name} in Kotlin Gradle Plugin, " + - "using only user provided configuration for this target") - userConfig - } else { - logger.warn("Could not find target with name: ${userConfig.name} in Kotlin Gradle Plugin") - collectFromSinglePlatformOldPlugin() - } - - return if (subProjects.isNotEmpty()) { - try { - subProjects.toProjects().fold(baseConfig) { config, subProject -> - mergeUserConfigurationAndPlatformData( - config, - ConfigurationExtractor(subProject).extractConfiguration(config.name, config.androidVariants)!! + ?: if (this.dokkaSourceSets.isNotEmpty()) { + if (outputDiagnosticInfo) + logger.warn( + "Could not find source set with name: ${userConfig.name} in Kotlin Gradle Plugin, " + + "using only user provided configuration for this source set" ) - } - } catch(e: NullPointerException) { - logger.warn("Cannot extract sources from subProjects. Do you have the Kotlin plugin in version 1.3.30+ " + - "and the Kotlin plugin applied in the root project?") - baseConfig + userConfig + } else { + if (outputDiagnosticInfo) + logger.warn("Could not find source set with name: ${userConfig.name} in Kotlin Gradle Plugin") + collectFromSinglePlatformOldPlugin(userConfig.name, userConfig) } - } else { - baseConfig - } } - protected open fun collectFromSinglePlatformOldPlugin() = - configExtractor.extractFromKotlinTasks(kotlinTasks) - ?.let { mergeUserConfigurationAndPlatformData(configuration, it) } - ?: configExtractor.extractFromJavaPlugin() - ?.let { mergeUserConfigurationAndPlatformData(configuration, it) } - ?: configuration - - protected open fun mergeUserConfigurationAndPlatformData(userConfig: GradlePassConfigurationImpl, autoConfig: PlatformData) = - userConfig.copy().apply { - sourceRoots.addAll(userConfig.sourceRoots.union(autoConfig.sourceRoots.toSourceRoots()).distinct()) - classpath = userConfig.classpath.union(autoConfig.classpath.map { it.absolutePath }).distinct() - if (userConfig.platform == null && autoConfig.platform != "") - platform = autoConfig.platform + private fun collectFromSinglePlatformOldPlugin(name: String, userConfig: GradleDokkaSourceSet) = + kotlinTasks.find { it.name == name } + ?.let { configExtractor.extractFromKotlinTasks(listOf(it)) } + ?.singleOrNull() + ?.let { mergeUserConfigurationAndPlatformData(userConfig, it) } + ?: configExtractor.extractFromJavaPlugin() + ?.let { mergeUserConfigurationAndPlatformData(userConfig, it) } + ?: userConfig + + private fun mergeUserConfigurationAndPlatformData( + userConfig: GradleDokkaSourceSet, + autoConfig: PlatformData + ) = userConfig.copy().apply { + sourceRoots.addAll(userConfig.sourceRoots.union(autoConfig.sourceRoots.toSourceRoots()).distinct()) + dependentSourceSets.addAll(userConfig.dependentSourceSets) + dependentSourceSets.addAll(autoConfig.dependentSourceSets.map { DokkaSourceSetID(project, it) }) + classpath = userConfig.classpath.union(autoConfig.classpath.map { it.absolutePath }).distinct() + if (userConfig.platform == null && autoConfig.platform != "") + platform = autoConfig.platform + } + + private fun configureDefault(config: GradleDokkaSourceSet): GradleDokkaSourceSet { + if (config.moduleDisplayName.isBlank()) { + config.moduleDisplayName = project.name } - protected open fun defaultPassConfiguration( - config: GradlePassConfigurationImpl, - globalConfig: GradlePassConfigurationImpl? - ): GradlePassConfigurationImpl { - if (config.moduleName == "") { - config.moduleName = project.name + if (config.displayName.isBlank()) { + config.displayName = config.name.substringBeforeLast("Main", config.platform.toString()) } - if (config.targets.isEmpty() && multiplatform.isNotEmpty()){ - config.targets = listOf(config.name) + + if (project.isAndroidProject() && !config.noAndroidSdkLink) { + config.externalDocumentationLinks.add(ANDROID_REFERENCE_URL) + } + + if (config.platform?.isNotBlank() == true) { + config.analysisPlatform = dokkaPlatformFromString(config.platform.toString()) } - config.classpath = (config.classpath as List<Any>).map { it.toString() }.distinct() // Workaround for Groovy's GStringImpl + + // Workaround for Groovy's GStringImpl + config.classpath = (config.classpath as List<Any>).map { it.toString() }.distinct() config.sourceRoots = config.sourceRoots.distinct().toMutableList() config.samples = config.samples.map { project.file(it).absolutePath } config.includes = config.includes.map { project.file(it).absolutePath } config.suppressedFiles += collectSuppressedFiles(config.sourceRoots) - if (project.isAndroidProject() && !config.noAndroidSdkLink) { // TODO: introduce Android as a separate Dokka platform? - config.externalDocumentationLinks.add(ANDROID_REFERENCE_URL) - } - if (config.platform != null && config.platform.toString().isNotEmpty()) { - config.analysisPlatform = dokkaPlatformFromString(config.platform.toString()) - } - if (globalConfig != null) { - config.perPackageOptions.addAll(globalConfig.perPackageOptions) - config.externalDocumentationLinks.addAll(globalConfig.externalDocumentationLinks) - config.sourceLinks.addAll(globalConfig.sourceLinks) - config.samples += globalConfig.samples.map { project.file(it).absolutePath } - config.includes += globalConfig.includes.map { project.file(it).absolutePath } - } + return config } private fun dokkaPlatformFromString(platform: String) = when (platform.toLowerCase()) { - KotlinPlatformType.androidJvm.toString().toLowerCase(), "androidjvm", "android" -> Platform.jvm + "androidjvm", "android" -> Platform.jvm "metadata" -> Platform.common else -> Platform.fromString(platform) } @@ -283,16 +273,16 @@ open class DokkaTask : DefaultTask() { // Needed for Gradle incremental build @InputFiles - fun getInputFiles(): FileCollection { - val config = collectConfigurations() - return project.files(config.flatMap { it.sourceRoots }.map { project.fileTree(File(it.path)) }) + + fun getInputFiles(): FileCollection = configuredDokkaSourceSets.let { config -> + project.files(config.flatMap { it.sourceRoots }.map { project.fileTree(File(it.path)) }) + project.files(config.flatMap { it.includes }) + project.files(config.flatMap { it.samples }.map { project.fileTree(File(it)) }) } @Classpath fun getInputClasspath(): FileCollection = - project.files((collectConfigurations().flatMap { it.classpath } as List<Any>).map { project.fileTree(File(it.toString())) }) + project.files((configuredDokkaSourceSets.flatMap { it.classpath } as List<Any>) + .map { project.fileTree(File(it.toString())) }) companion object { const val COLORS_ENABLED_PROPERTY = "kotlin.colors.enabled" diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/ProxyUtils.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/ProxyUtils.kt index f8965993..468f597f 100644 --- a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/ProxyUtils.kt +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/ProxyUtils.kt @@ -1,9 +1,6 @@ package org.jetbrains.dokka.gradle -import java.lang.reflect.InvocationHandler -import java.lang.reflect.InvocationTargetException -import java.lang.reflect.Method -import java.lang.reflect.Proxy +import java.lang.reflect.* /** @@ -13,8 +10,8 @@ import java.lang.reflect.Proxy * to create access proxy for [delegate] into [targetClassLoader]. */ @Suppress("UNCHECKED_CAST") -inline fun <reified T : Any> automagicTypedProxy(targetClassLoader: ClassLoader, delegate: Any): T = - automagicProxy(targetClassLoader, T::class.java, delegate) as T +internal inline fun <reified T : Any> automagicTypedProxy(targetClassLoader: ClassLoader, delegate: Any): T = + automagicProxy(targetClassLoader, T::class.java, delegate) as T /** @@ -24,14 +21,14 @@ inline fun <reified T : Any> automagicTypedProxy(targetClassLoader: ClassLoader, * to create access proxy for [delegate] into [targetClassLoader]. * */ -fun automagicProxy(targetClassLoader: ClassLoader, targetType: Class<*>, delegate: Any): Any = - Proxy.newProxyInstance( - targetClassLoader, - arrayOf(targetType), - DelegatedInvocationHandler(delegate) - ) +internal fun automagicProxy(targetClassLoader: ClassLoader, targetType: Class<*>, delegate: Any): Any = + Proxy.newProxyInstance( + targetClassLoader, + arrayOf(targetType), + DelegatedInvocationHandler(delegate) + ) -class DelegatedInvocationHandler(private val delegate: Any) : InvocationHandler { +internal class DelegatedInvocationHandler(private val delegate: Any) : InvocationHandler { @Throws(Throwable::class) override fun invoke(proxy: Any, method: Method, args: Array<Any?>?): Any? { diff --git a/integration/src/main/kotlin/org/jetbrains/dokka/ReflectDsl.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/ReflectDsl.kt index 1984a3e5..4b511022 100644 --- a/integration/src/main/kotlin/org/jetbrains/dokka/ReflectDsl.kt +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/ReflectDsl.kt @@ -5,7 +5,7 @@ import kotlin.reflect.full.memberFunctions import kotlin.reflect.full.memberProperties import kotlin.reflect.jvm.isAccessible -object ReflectDsl { +internal object ReflectDsl { class CallOrPropAccess(private val receiver: Any?, private val clz: KClass<*>, diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/configurationImplementations.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/configurationImplementations.kt index 65afad04..b6b8399c 100644 --- a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/configurationImplementations.kt +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/configurationImplementations.kt @@ -1,12 +1,19 @@ +@file:Suppress("FunctionName") + package org.jetbrains.dokka.gradle +import com.android.build.gradle.api.AndroidSourceSet import groovy.lang.Closure import org.gradle.api.Action +import org.gradle.api.Project import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Internal import org.gradle.api.tasks.Optional import org.gradle.util.ConfigureUtil import org.jetbrains.dokka.DokkaConfiguration import org.jetbrains.dokka.DokkaConfiguration.* +import org.jetbrains.dokka.DokkaDefaults +import org.jetbrains.dokka.DokkaSourceSetID import org.jetbrains.dokka.Platform import java.io.File import java.io.Serializable @@ -14,8 +21,10 @@ import java.net.URL import java.util.concurrent.Callable import kotlin.reflect.KMutableProperty import kotlin.reflect.full.memberProperties +import org.gradle.api.tasks.SourceSet as GradleSourceSet +import org.jetbrains.kotlin.gradle.model.SourceSet as KotlinSourceSet -class GradleSourceRootImpl: SourceRoot, Serializable { +class GradleSourceRootImpl : SourceRoot, Serializable { override var path: String = "" set(value) { field = File(value).absolutePath @@ -24,34 +33,113 @@ class GradleSourceRootImpl: SourceRoot, Serializable { override fun toString(): String = path } -open class GradlePassConfigurationImpl(@Transient val name: String = ""): PassConfiguration { - @Input @Optional override var classpath: List<String> = emptyList() - @Input override var moduleName: String = "" - @Input override var sourceRoots: MutableList<SourceRoot> = mutableListOf() - @Input override var samples: List<String> = emptyList() - @Input override var includes: List<String> = emptyList() - @Input override var includeNonPublic: Boolean = false - @Input override var includeRootPackage: Boolean = false - @Input override var reportUndocumented: Boolean = false - @Input override var skipEmptyPackages: Boolean = true - @Input override var skipDeprecated: Boolean = false - @Input override var jdkVersion: Int = 6 - @Input override var sourceLinks: MutableList<SourceLinkDefinition> = mutableListOf() - @Input override var perPackageOptions: MutableList<PackageOptions> = mutableListOf() - @Input override var externalDocumentationLinks: MutableList<ExternalDocumentationLink> = mutableListOf() - @Input @Optional override var languageVersion: String? = null - @Input @Optional override var apiVersion: String? = null - @Input override var noStdlibLink: Boolean = false - @Input override var noJdkLink: Boolean = false - @Input var noAndroidSdkLink: Boolean = false - @Input override var suppressedFiles: List<String> = emptyList() - @Input override var collectInheritedExtensionsFromLibraries: Boolean = false - @Input override var analysisPlatform: Platform = Platform.DEFAULT - @Input @Optional var platform: String? = null - @Input override var targets: List<String> = emptyList() - @Input @Optional override var sinceKotlin: String? = null - @Transient var collectKotlinTasks: (() -> List<Any?>?)? = null - @Input @Transient var androidVariants: List<String> = emptyList() +open class GradleDokkaSourceSet constructor( + @Transient @get:Input val name: String, + @Transient @get:Internal internal val project: Project +) : DokkaSourceSet { + + @Input + @Optional + override var classpath: List<String> = emptyList() + + @Input + override var moduleDisplayName: String = "" + + @Input + override var displayName: String = "" + + @get:Internal + override val sourceSetID: DokkaSourceSetID = DokkaSourceSetID(project, name) + + @Input + override var sourceRoots: MutableList<SourceRoot> = mutableListOf() + + @Input + override var dependentSourceSets: MutableSet<DokkaSourceSetID> = mutableSetOf() + + @Input + override var samples: List<String> = emptyList() + + @Input + override var includes: List<String> = emptyList() + + @Input + override var includeNonPublic: Boolean = DokkaDefaults.includeNonPublic + + @Input + override var includeRootPackage: Boolean = DokkaDefaults.includeRootPackage + + @Input + override var reportUndocumented: Boolean = DokkaDefaults.reportUndocumented + + @Input + override var skipEmptyPackages: Boolean = DokkaDefaults.skipEmptyPackages + + @Input + override var skipDeprecated: Boolean = DokkaDefaults.skipDeprecated + + @Input + override var jdkVersion: Int = DokkaDefaults.jdkVersion + + @Input + override var sourceLinks: MutableList<SourceLinkDefinition> = mutableListOf() + + @Input + override var perPackageOptions: MutableList<PackageOptions> = mutableListOf() + + @Input + override var externalDocumentationLinks: MutableList<ExternalDocumentationLink> = mutableListOf() + + @Input + @Optional + override var languageVersion: String? = null + + @Input + @Optional + override var apiVersion: String? = null + + @Input + override var noStdlibLink: Boolean = DokkaDefaults.noStdlibLink + + @Input + override var noJdkLink: Boolean = DokkaDefaults.noJdkLink + + @Input + var noAndroidSdkLink: Boolean = false + + @Input + override var suppressedFiles: List<String> = emptyList() + + @Input + override var analysisPlatform: Platform = DokkaDefaults.analysisPlatform + + @Input + @Optional + var platform: String? = null + + @Internal + @Transient + var collectKotlinTasks: (() -> List<Any?>?)? = null + + fun DokkaSourceSetID(sourceSetName: String): DokkaSourceSetID { + return DokkaSourceSetID(project, sourceSetName) + } + + fun dependsOn(sourceSet: GradleSourceSet) { + dependsOn(DokkaSourceSetID(sourceSet.name)) + } + + fun dependsOn(sourceSet: DokkaSourceSet) { + dependsOn(sourceSet.sourceSetID) + } + + fun dependsOn(sourceSetName: String) { + dependsOn(DokkaSourceSetID(sourceSetName)) + } + + fun dependsOn(sourceSetID: DokkaSourceSetID) { + dependentSourceSets.add(sourceSetID) + } fun kotlinTasks(taskSupplier: Callable<List<Any>>) { collectKotlinTasks = { taskSupplier.call() } @@ -95,70 +183,76 @@ open class GradlePassConfigurationImpl(@Transient val name: String = ""): PassCo } fun externalDocumentationLink(c: Closure<Unit>) { - val builder = ConfigureUtil.configure(c, GradleExternalDocumentationLinkImpl.Builder()) - externalDocumentationLinks.add(builder.build()) + val link = ConfigureUtil.configure(c, GradleExternalDocumentationLinkImpl()) + externalDocumentationLinks.add(ExternalDocumentationLink.Builder(link.url, link.packageListUrl).build()) } - fun externalDocumentationLink(action: Action<in GradleExternalDocumentationLinkImpl.Builder>) { - val builder = GradleExternalDocumentationLinkImpl.Builder() - action.execute(builder) - externalDocumentationLinks.add(builder.build()) + fun externalDocumentationLink(action: Action<in GradleExternalDocumentationLinkImpl>) { + val link = GradleExternalDocumentationLinkImpl() + action.execute(link) + externalDocumentationLinks.add(ExternalDocumentationLink.Builder(link.url, link.packageListUrl).build()) } } +fun GradleDokkaSourceSet.dependsOn(sourceSet: KotlinSourceSet) { + dependsOn(DokkaSourceSetID(sourceSet.name)) +} + +fun GradleDokkaSourceSet.dependsOn(sourceSet: org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet) { + dependsOn(DokkaSourceSetID(sourceSet.name)) +} + +fun GradleDokkaSourceSet.dependsOn(sourceSet: AndroidSourceSet) { + dependsOn(DokkaSourceSetID(sourceSet.name)) +} + class GradleSourceLinkDefinitionImpl : SourceLinkDefinition, Serializable { override var path: String = "" override var url: String = "" override var lineSuffix: String? = null } -class GradleExternalDocumentationLinkImpl( - override val url: URL, - override val packageListUrl: URL -): ExternalDocumentationLink, Serializable { - open class Builder(open var url: URL? = null, - open var packageListUrl: URL? = null) { - - constructor(root: String, packageList: String? = null) : this(URL(root), packageList?.let { URL(it) }) +class GradleExternalDocumentationLinkImpl : ExternalDocumentationLink, Serializable { + override var url: URL = URL("http://") + override var packageListUrl: URL = URL("http://") +} - fun build(): ExternalDocumentationLink = - if (packageListUrl != null && url != null) - GradleExternalDocumentationLinkImpl(url!!, packageListUrl!!) - else if (url != null) - GradleExternalDocumentationLinkImpl(url!!, URL(url!!, "package-list")) - else - throw IllegalArgumentException("url or url && packageListUrl must not be null for external documentation link") - } +class GradleDokkaModuleDescription : DokkaModuleDescription { + override var name: String = "" + override var path: String = "" + override var docFile: String = "" } -class GradleDokkaConfigurationImpl: DokkaConfiguration { +class GradleDokkaConfigurationImpl : DokkaConfiguration { override var outputDir: String = "" - override var format: String = "html" - override var generateIndexPages: Boolean = false - override var cacheRoot: String? = null - override var impliedPlatforms: List<String> = emptyList() - override var passesConfigurations: List<GradlePassConfigurationImpl> = emptyList() + override var cacheRoot: String? = DokkaDefaults.cacheRoot + override var offlineMode: Boolean = DokkaDefaults.offlineMode + override var failOnWarning: Boolean = DokkaDefaults.failOnWarning + override var sourceSets: List<GradleDokkaSourceSet> = emptyList() + override var pluginsClasspath: List<File> = emptyList() + override var pluginsConfiguration: Map<String, String> = mutableMapOf() + override var modules: List<GradleDokkaModuleDescription> = emptyList() } -class GradlePackageOptionsImpl: PackageOptions, Serializable { +class GradlePackageOptionsImpl : PackageOptions, Serializable { override var prefix: String = "" - override var includeNonPublic: Boolean = false - override var reportUndocumented: Boolean = false - override var skipDeprecated: Boolean = false - override var suppress: Boolean = false + override var includeNonPublic: Boolean = DokkaDefaults.includeNonPublic + override var reportUndocumented: Boolean = DokkaDefaults.reportUndocumented + override var skipDeprecated: Boolean = DokkaDefaults.skipDeprecated + override var suppress: Boolean = DokkaDefaults.suppress } -fun GradlePassConfigurationImpl.copy(): GradlePassConfigurationImpl { - val newObj = GradlePassConfigurationImpl(this.name) +internal fun GradleDokkaSourceSet.copy(): GradleDokkaSourceSet { + val newObj = GradleDokkaSourceSet(this.name, this.project) this::class.memberProperties.forEach { field -> if (field is KMutableProperty<*>) { - val value = field.getter.call(this) - if (value is Collection<*>) { - field.setter.call(newObj, value.toMutableList()) - } else { - field.setter.call(newObj, field.getter.call(this)) + when (val value = field.getter.call(this)) { + is List<*> -> field.setter.call(newObj, value.toMutableList()) + is Set<*> -> field.setter.call(newObj, value.toMutableSet()) + else -> field.setter.call(newObj, field.getter.call(this)) } + } } return newObj -}
\ No newline at end of file +} diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/defaultDokkaOutputDirectory.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/defaultDokkaOutputDirectory.kt new file mode 100644 index 00000000..0a7ab534 --- /dev/null +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/defaultDokkaOutputDirectory.kt @@ -0,0 +1,13 @@ +package org.jetbrains.dokka.gradle + +import org.gradle.api.Task +import java.io.File + +internal fun Task.defaultDokkaOutputDirectory(): File { + return defaultDokkaOutputDirectory(project.buildDir, name) +} + +internal fun defaultDokkaOutputDirectory(buildDir: File, taskName: String): File { + val formatClassifier = taskName.removePrefix("dokka").decapitalize() + return File(buildDir, "dokka${File.separator}$formatClassifier") +} diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/dokkaConfigurations.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/dokkaConfigurations.kt new file mode 100644 index 00000000..20f54cc5 --- /dev/null +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/dokkaConfigurations.kt @@ -0,0 +1,41 @@ +package org.jetbrains.dokka.gradle + +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.attributes.Usage + +internal fun Project.maybeCreateDokkaDefaultPluginConfiguration(): Configuration { + return configurations.maybeCreate("dokkaPlugin") { + attributes.attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage::class.java, "java-runtime")) + isCanBeConsumed = false + } +} + +internal fun Project.maybeCreateDokkaDefaultRuntimeConfiguration(): Configuration { + return configurations.maybeCreate("dokkaRuntime") { + attributes.attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage::class.java, "java-runtime")) + isCanBeConsumed = false + } +} + +internal fun Project.maybeCreateDokkaPluginConfiguration(dokkaTaskName: String): Configuration { + return project.configurations.maybeCreate("${dokkaTaskName}Plugin") { + extendsFrom(maybeCreateDokkaDefaultPluginConfiguration()) + attributes.attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage::class.java, "java-runtime")) + isCanBeConsumed = false + defaultDependencies { dependencies -> + dependencies.add(project.dokkaArtifacts.dokkaBase) + } + } +} + +internal fun Project.maybeCreateDokkaRuntimeConfiguration(dokkaTaskName: String): Configuration { + return project.configurations.maybeCreate("${dokkaTaskName}Runtime") { + extendsFrom(maybeCreateDokkaDefaultRuntimeConfiguration()) + attributes.attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage::class.java, "java-runtime")) + isCanBeConsumed = false + defaultDependencies { dependencies -> + dependencies.add(project.dokkaArtifacts.dokkaCore) + } + } +} diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/main.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/main.kt index 7ed29c58..d70448f1 100644 --- a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/main.kt +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/main.kt @@ -2,57 +2,49 @@ package org.jetbrains.dokka.gradle import org.gradle.api.Plugin import org.gradle.api.Project -import org.gradle.api.artifacts.Configuration -import org.gradle.util.GradleVersion -import java.io.File -import java.io.InputStream -import java.util.* - -internal const val CONFIGURATION_EXTENSION_NAME = "configuration" -internal const val MULTIPLATFORM_EXTENSION_NAME = "multiplatform" +import org.gradle.kotlin.dsl.register open class DokkaPlugin : Plugin<Project> { - private val taskName = "dokka" - override fun apply(project: Project) { - loadDokkaVersion() - val dokkaRuntimeConfiguration = addConfiguration(project) - addTasks(project, dokkaRuntimeConfiguration, DokkaTask::class.java) - } - private fun loadDokkaVersion() = DokkaVersion.loadFrom(javaClass.getResourceAsStream("/META-INF/gradle-plugins/org.jetbrains.dokka.properties")) + project.setupDokkaTasks("dokkaHtml") - private fun addConfiguration(project: Project) = - project.configurations.create("dokkaRuntime").apply { - defaultDependencies{ dependencies -> dependencies.add(project.dependencies.create("org.jetbrains.dokka:dokka-fatjar:${DokkaVersion.version}")) } + project.setupDokkaTasks("dokkaJavadoc") { + plugins.dependencies.add(project.dokkaArtifacts.javadocPlugin) } - protected open fun addTasks(project: Project, runtimeConfiguration: Configuration, taskClass: Class<out DokkaTask>) { - if(GradleVersion.current() >= GradleVersion.version("4.10")) { - project.tasks.register(taskName, taskClass) - } else { - project.tasks.create(taskName, taskClass) + project.setupDokkaTasks("dokkaGfm") { + plugins.dependencies.add(project.dokkaArtifacts.gfmPlugin) } - project.tasks.withType(taskClass) { task -> - task.multiplatform = project.container(GradlePassConfigurationImpl::class.java) - task.configuration = GradlePassConfigurationImpl() - task.dokkaRuntime = runtimeConfiguration - task.outputDirectory = File(project.buildDir, taskName).absolutePath + + project.setupDokkaTasks("dokkaJekyll") { + plugins.dependencies.add(project.dokkaArtifacts.jekyllPlugin) } } -} -object DokkaVersion { - var version: String? = null + /** + * Creates [DokkaTask], [DokkaMultimoduleTask] and [DokkaCollectorTask] for the given + * name and configuration. + */ + private fun Project.setupDokkaTasks(name: String, configuration: AbstractDokkaTask.() -> Unit = {}) { + project.maybeCreateDokkaPluginConfiguration(name) + project.maybeCreateDokkaRuntimeConfiguration(name) + project.tasks.register<DokkaTask>(name) { + configuration() + } - fun loadFrom(stream: InputStream) { - version = Properties().apply { - load(stream) - }.getProperty("dokka-version") + if (project.subprojects.isNotEmpty()) { + val multimoduleName = "${name}Multimodule" + project.maybeCreateDokkaPluginConfiguration(multimoduleName) + project.maybeCreateDokkaRuntimeConfiguration(multimoduleName) + project.tasks.register<DokkaMultimoduleTask>(multimoduleName) { + dokkaTaskNames = dokkaTaskNames + name + configuration() + } + + project.tasks.register<DokkaCollectorTask>("${name}Collector") { + dokkaTaskNames = dokkaTaskNames + name + } + } } } - -object ClassloaderContainer { - @JvmField - var fatJarClassLoader: ClassLoader? = null -}
\ No newline at end of file diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/utils.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/utils.kt index 31892e8e..b6c5cbd8 100644 --- a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/utils.kt +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/utils.kt @@ -1,5 +1,6 @@ package org.jetbrains.dokka.gradle +import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.Project import org.gradle.api.UnknownDomainObjectException import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension @@ -7,25 +8,30 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType import org.jetbrains.kotlin.gradle.plugin.KotlinTarget -fun Project.isAndroidProject() = try { +internal fun Project.isAndroidProject() = try { project.extensions.getByName("android") true -} catch(e: UnknownDomainObjectException) { +} catch (e: UnknownDomainObjectException) { false -} catch(e: ClassNotFoundException) { +} catch (e: ClassNotFoundException) { false } -fun Project.isMultiplatformProject() = try { +internal fun Project.isNotMultiplatformProject() = !isMultiplatformProject() + +internal fun Project.isMultiplatformProject() = try { project.extensions.getByType(KotlinMultiplatformExtension::class.java) true -} catch(e: UnknownDomainObjectException) { +} catch (e: UnknownDomainObjectException) { false -} catch (e: NoClassDefFoundError){ +} catch (e: NoClassDefFoundError) { false -} catch(e: ClassNotFoundException) { +} catch (e: ClassNotFoundException) { false } -fun KotlinTarget.isAndroidTarget() = this.platformType == KotlinPlatformType.androidJvm -fun DokkaTask.isMultiplatformProject() = this.multiplatform.isNotEmpty()
\ No newline at end of file +internal fun KotlinTarget.isAndroidTarget() = this.platformType == KotlinPlatformType.androidJvm + +internal fun <T : Any> NamedDomainObjectContainer<T>.maybeCreate(name: String, configuration: T.() -> Unit): T { + return findByName(name) ?: create(name, configuration) +} diff --git a/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/AutomagicProxyTest.kt b/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/AutomagicProxyTest.kt new file mode 100644 index 00000000..e981d6fe --- /dev/null +++ b/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/AutomagicProxyTest.kt @@ -0,0 +1,48 @@ +package org.jetbrains.dokka.gradle + +import org.jetbrains.dokka.DokkaBootstrap +import org.jetbrains.dokka.gradle.AutomagicProxyTest.TestInterface +import java.util.function.BiConsumer +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith + + +class AutomagicProxyTest { + + private class TestException(message: String, cause: Throwable?) : Exception(message, cause) + + private fun interface TestInterface { + @Throws(Throwable::class) + operator fun invoke(): Int + } + + @Test + fun `simple method invocation`() { + val instance = TestInterface { 0 } + val proxy = automagicTypedProxy<TestInterface>(instance.javaClass.classLoader, instance) + assertEquals(0, proxy()) + } + + @Test + fun `exception throw in DokkaBootstrap is not wrapped inside UndeclaredThrowableException`() { + val instanceThrowingTestException = object : DokkaBootstrap { + override fun configure(serializedConfigurationJSON: String, logger: BiConsumer<String, String>) = Unit + override fun generate() { + throw TestException("Test Exception Message", Exception("Cause Exception Message")) + } + } + + val proxy = automagicTypedProxy<DokkaBootstrap>( + instanceThrowingTestException.javaClass.classLoader, + instanceThrowingTestException + ) + + val exception = assertFailsWith<TestException> { + proxy.generate() + } + + assertEquals("Test Exception Message", exception.message) + assertEquals("Cause Exception Message", exception.cause?.message) + } +} diff --git a/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/DokkaTasksTest.kt b/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/DokkaTasksTest.kt new file mode 100644 index 00000000..b948c540 --- /dev/null +++ b/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/DokkaTasksTest.kt @@ -0,0 +1,65 @@ +package org.jetbrains.dokka.gradle + +import org.gradle.api.plugins.JavaBasePlugin +import org.gradle.kotlin.dsl.withType +import org.gradle.testfixtures.ProjectBuilder +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertSame +import kotlin.test.assertTrue + +class DokkaTasksTest { + + @Test + fun `one task per format is registered`() { + val project = ProjectBuilder.builder().build() + project.plugins.apply("org.jetbrains.dokka") + + assertTrue( + project.tasks.findByName("dokkaHtml") is DokkaTask, + "Expected DokkaTask: dokkaHtml" + ) + + assertTrue( + project.tasks.findByName("dokkaGfm") is DokkaTask, + "Expected DokkaTask: dokkaGfm" + ) + + assertTrue( + project.tasks.findByName("dokkaJekyll") is DokkaTask, + "Expected DokkaTask: dokkaJekyll" + ) + + assertTrue( + project.tasks.findByName("dokkaJavadoc") is DokkaTask, + "Expected DokkaTask: dokkaJavadoc" + ) + } + + @Test + fun `dokka plugin configurations extend dokkaPlugin`() { + val project = ProjectBuilder.builder().build() + project.plugins.apply("org.jetbrains.dokka") + + val dokkaPluginsConfiguration = project.maybeCreateDokkaDefaultPluginConfiguration() + + project.tasks.withType<DokkaTask>().forEach { dokkaTask -> + assertSame( + dokkaTask.plugins.extendsFrom.single(), dokkaPluginsConfiguration, + "Expected dokka plugins configuration to extend default ${dokkaPluginsConfiguration.name} configuration" + ) + } + } + + @Test + fun `all dokka tasks are part of the documentation group`() { + val project = ProjectBuilder.builder().build() + project.plugins.apply("org.jetbrains.dokka") + project.tasks.filter { "dokka" in it.name.toLowerCase() }.forEach { dokkaTask -> + assertEquals( + JavaBasePlugin.DOCUMENTATION_GROUP, dokkaTask.group, + "Expected task: ${dokkaTask.path} group to be ${JavaBasePlugin.DOCUMENTATION_GROUP}" + ) + } + } +} diff --git a/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/KotlinDslDokkaTaskConfigurationTest.kt b/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/KotlinDslDokkaTaskConfigurationTest.kt new file mode 100644 index 00000000..7b78fb55 --- /dev/null +++ b/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/KotlinDslDokkaTaskConfigurationTest.kt @@ -0,0 +1,97 @@ +package org.jetbrains.dokka.gradle + +import org.gradle.testfixtures.ProjectBuilder +import org.jetbrains.dokka.DokkaSourceSetID +import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension +import kotlin.test.Test +import kotlin.test.assertEquals + +class KotlinDslDokkaTaskConfigurationTest { + + @Test + fun `configure project using dokka extension function`() { + val project = ProjectBuilder.builder().build() + project.plugins.apply("org.jetbrains.dokka") + project.tasks.withType(DokkaTask::class.java).forEach { dokkaTask -> + dokkaTask.outputDirectory = "test" + } + + project.tasks.withType(DokkaTask::class.java).forEach { dokkaTask -> + assertEquals("test", dokkaTask.outputDirectory) + } + } + + @Test + fun `sourceSet dependsOn by String`() { + val project = ProjectBuilder.builder().build() + project.plugins.apply("org.jetbrains.dokka") + + project.tasks.withType(DokkaTask::class.java).forEach { dokkaTask -> + dokkaTask.dokkaSourceSets.run { + val commonMain = create("commonMain") + val jvmMain = create("jvmMain") { + it.dependsOn("commonMain") + } + + assertEquals( + 0, commonMain.dependentSourceSets.size, + "Expected no dependent source set in commonMain" + ) + + assertEquals( + 1, jvmMain.dependentSourceSets.size, + "Expected only one dependent source set in jvmMain" + ) + + assertEquals( + commonMain.sourceSetID, jvmMain.dependentSourceSets.single(), + "Expected jvmMain to depend on commonMain" + ) + + assertEquals( + DokkaSourceSetID(project.path, "commonMain"), commonMain.sourceSetID + ) + } + } + } + + @Test + fun `sourceSet dependsOn by DokkaSourceSet`() { + val project = ProjectBuilder.builder().build() + project.plugins.apply("org.jetbrains.dokka") + + project.tasks.withType(DokkaTask::class.java).first().run { + dokkaSourceSets.run { + val commonMain = create("commonMain") + val jvmMain = create("jvmMain") { + it.dependsOn(commonMain) + } + + assertEquals( + commonMain.sourceSetID, jvmMain.dependentSourceSets.single() + ) + } + } + } + + @Test + fun `sourceSet dependsOn by KotlinSourceSet`() { + val project = ProjectBuilder.builder().build() + project.plugins.apply("org.jetbrains.dokka") + project.plugins.apply("org.jetbrains.kotlin.jvm") + + val kotlin = project.extensions.getByName("kotlin") as KotlinJvmProjectExtension + + project.tasks.withType(DokkaTask::class.java).first().run { + dokkaSourceSets.run { + val special = create("special") { + it.dependsOn(kotlin.sourceSets.getByName("main")) + } + + assertEquals( + DokkaSourceSetID(project, "main"), special.dependentSourceSets.single() + ) + } + } + } +} diff --git a/runners/maven-plugin/build.gradle b/runners/maven-plugin/build.gradle deleted file mode 100644 index 76fab68d..00000000 --- a/runners/maven-plugin/build.gradle +++ /dev/null @@ -1,161 +0,0 @@ -import groovy.io.FileType -import org.jetbrains.CorrectShadowPublishing -import org.jetbrains.CrossPlatformExec - -import java.nio.file.Files -import java.nio.file.StandardCopyOption - -apply plugin: 'kotlin' -apply plugin: 'com.github.johnrengelman.shadow' - -sourceCompatibility = 1.8 - -tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { - kotlinOptions { - freeCompilerArgs += "-Xjsr305=strict" - languageVersion = "1.2" - apiVersion = languageVersion - jvmTarget = "1.8" - } -} - -configurations { - maven -} - -dependencies { - maven group: "org.apache.maven", name: 'apache-maven', version: maven_version, classifier: 'bin', ext: 'zip' - - shadow project(":runners:fatjar") - shadow "org.apache.maven:maven-core:$maven_version" - shadow "org.apache.maven:maven-model:$maven_version" - shadow "org.apache.maven:maven-plugin-api:$maven_version" - shadow "org.apache.maven:maven-archiver:$maven_archiver_version" - shadow "org.codehaus.plexus:plexus-utils:$plexus_utils_version" - shadow "org.codehaus.plexus:plexus-archiver:$plexus_archiver_version" - shadow "org.apache.maven.plugin-tools:maven-plugin-annotations:$maven_plugin_tools_version" - shadow "com.github.olivergondza:maven-jdk-tools-wrapper:0.1" -} - -final File mavenHome = new File(buildDir, "maven-bin") -final File mvn = new File(mavenHome, "apache-maven-$maven_version/bin/mvn") - -tasks.clean.doLast { - delete mavenHome -} - -task setupMaven(type: Sync) { - from configurations.maven.collect{ zipTree(it) } - into "$buildDir/maven-bin" -} - -def mavenBuildDir = "$buildDir/maven" - - -sourceSets.main.resources { - srcDirs += "$mavenBuildDir/classes/java/main" - exclude "**/*.class" -} - -task generatePom() { - inputs.file(new File(projectDir, "pom.tpl.xml")) - outputs.file(new File(mavenBuildDir, "pom.xml")) - doLast { - final pomTemplate = new File(projectDir, "pom.tpl.xml") - final pom = new File(mavenBuildDir, "pom.xml") - pom.parentFile.mkdirs() - pom.text = pomTemplate.text.replace("<version>dokka_version</version>", "<version>$dokka_version</version>") - .replace("<maven.version></maven.version>", "<maven.version>$maven_version</maven.version>") - .replace("<version>maven-plugin-plugin</version>", "<version>$maven_plugin_tools_version</version>") - } -} -// -//task mergeClassOutputs doLast { -// def sourceDir = new File(buildDir, "classes/kotlin") -// def targetDir = new File(buildDir, "classes/java") -// -// sourceDir.eachFileRecurse FileType.ANY, { -// def filePath = it.toPath() -// def targetFilePath = targetDir.toPath().resolve(sourceDir.toPath().relativize(filePath)) -// if (it.isFile()) { -// Files.move(filePath, targetFilePath, StandardCopyOption.REPLACE_EXISTING) -// } else if (it.isDirectory()) { -// targetFilePath.toFile().mkdirs() -// } -// } -//} - - - -task syncKotlinClasses(type: Sync, dependsOn: compileKotlin) { - from "$buildDir/classes/kotlin" - into "$mavenBuildDir/classes/java" - - preserve { - include '**/*.class' - } -} - -task syncJavaClasses(type: Sync, dependsOn: compileJava) { - from "$buildDir/classes/java" - into "$mavenBuildDir/classes/java" - - preserve { - include '**/*.class' - } -} - -task helpMojo(type: CrossPlatformExec, dependsOn: setupMaven) { - workingDir mavenBuildDir - commandLine mvn, '-e', '-B', 'org.apache.maven.plugins:maven-plugin-plugin:helpmojo' - - dependsOn syncKotlinClasses -} - - -task pluginDescriptor(type: CrossPlatformExec, dependsOn: setupMaven) { - workingDir mavenBuildDir - commandLine mvn, '-e', '-B', 'org.apache.maven.plugins:maven-plugin-plugin:descriptor' - - dependsOn syncJavaClasses -} - - -//mergeClassOutputs.dependsOn compileKotlin -//helpMojo.dependsOn mergeClassOutputs -helpMojo.dependsOn generatePom -sourceSets.main.java.srcDir("$buildDir/maven/generated-sources/plugin") -compileJava.dependsOn helpMojo -processResources.dependsOn pluginDescriptor - -pluginDescriptor.dependsOn generatePom - -shadowJar { - baseName = 'dokka-maven-plugin' - classifier = '' -} - -shadowJar.dependsOn pluginDescriptor - - -task sourceJar(type: Jar) { - from sourceSets.main.allSource -} - -apply plugin: 'maven-publish' - -publishing { - publications { - dokkaMavenPlugin(MavenPublication) { MavenPublication publication -> - artifactId = 'dokka-maven-plugin' - - artifact sourceJar { - classifier "sources" - } - - CorrectShadowPublishing.configure(publication, project) - } - } -} - -bintrayPublication(project, ['dokkaMavenPlugin']) diff --git a/runners/maven-plugin/build.gradle.kts b/runners/maven-plugin/build.gradle.kts new file mode 100644 index 00000000..fbd2b48a --- /dev/null +++ b/runners/maven-plugin/build.gradle.kts @@ -0,0 +1,98 @@ +import org.jetbrains.CrossPlatformExec +import org.jetbrains.SetupMaven +import org.jetbrains.registerDokkaArtifactPublication + +val setupMaven by tasks.register<SetupMaven>("setupMaven") + +dependencies { + implementation(project(":core")) + implementation("org.apache.maven:maven-core:${setupMaven.mavenVersion}") + implementation("org.apache.maven:maven-plugin-api:${setupMaven.mavenVersion}") + implementation("org.apache.maven.plugin-tools:maven-plugin-annotations:${setupMaven.mavenPluginToolsVersion}") + implementation("org.apache.maven:maven-archiver:2.5") + implementation(kotlin("stdlib-jdk8")) + implementation("org.eclipse.aether:aether-api:${setupMaven.aetherVersion}") + implementation("org.eclipse.aether:aether-spi:${setupMaven.aetherVersion}") + implementation("org.eclipse.aether:aether-impl:${setupMaven.aetherVersion}") + implementation("org.eclipse.aether:aether-connector-basic:${setupMaven.aetherVersion}") + implementation("org.eclipse.aether:aether-transport-file:${setupMaven.aetherVersion}") + implementation("org.eclipse.aether:aether-transport-http:${setupMaven.aetherVersion}") + implementation("org.apache.maven:maven-aether-provider:3.3.3") +} + +tasks.named<Delete>("clean") { + delete(setupMaven.mavenBuildDir) + delete(setupMaven.mavenBinDir) +} + +/** + * Generate pom.xml for Maven Plugin Plugin + */ +val generatePom by tasks.registering(Copy::class) { + val dokka_version: String by project + inputs.property("dokka_version", dokka_version) + + from("$projectDir/pom.tpl.xml") { + rename("(.*).tpl.xml", "$1.xml") + } + into(setupMaven.mavenBuildDir) + + eachFile { + filter { line -> + line.replace("<maven.version></maven.version>", "<maven.version>${setupMaven.mavenVersion}</maven.version>") + } + filter { line -> + line.replace("<version>dokka_version</version>", "<version>$dokka_version</version>") + } + filter { line -> + line.replace( + "<version>maven-plugin-plugin</version>", + "<version>${setupMaven.mavenPluginToolsVersion}</version>" + ) + } + } +} + +/** + * Copy compiled classes to [mavenBuildDir] for Maven Plugin Plugin + */ +val syncClasses by tasks.registering(Sync::class) { + dependsOn(tasks.compileKotlin, tasks.compileJava) + from("$buildDir/classes/kotlin", "$buildDir/classes/java") + into("${setupMaven.mavenBuildDir}/classes/java") + + preserve { + include("**/*.class") + } +} + +val helpMojo by tasks.registering(CrossPlatformExec::class) { + dependsOn(setupMaven, generatePom, syncClasses) + workingDir(setupMaven.mavenBuildDir) + commandLine(setupMaven.mvn, "-e", "-B", "org.apache.maven.plugins:maven-plugin-plugin:helpmojo") +} + +val pluginDescriptor by tasks.registering(CrossPlatformExec::class) { + dependsOn(setupMaven, generatePom, syncClasses) + workingDir(setupMaven.mavenBuildDir) + commandLine(setupMaven.mvn, "-e", "-B", "org.apache.maven.plugins:maven-plugin-plugin:descriptor") +} + +val sourceJar by tasks.registering(Jar::class) { + archiveClassifier.set("sources") + from(sourceSets["main"].allSource) +} + +tasks.named<Jar>("jar") { + dependsOn(pluginDescriptor, helpMojo) + metaInf { + from("${setupMaven.mavenBuildDir}/classes/java/main/META-INF") + } + manifest { + attributes("Class-Path" to configurations.runtimeClasspath.get().files.joinToString(" ") { it.name }) + } +} + +registerDokkaArtifactPublication("dokkaMavenPlugin") { + artifactId = "dokka-maven-plugin" +} diff --git a/runners/maven-plugin/src/main/kotlin/DokkaMojo.kt b/runners/maven-plugin/src/main/kotlin/DokkaMojo.kt index 1cbe39f3..514df151 100644 --- a/runners/maven-plugin/src/main/kotlin/DokkaMojo.kt +++ b/runners/maven-plugin/src/main/kotlin/DokkaMojo.kt @@ -3,15 +3,32 @@ package org.jetbrains.dokka.maven import org.apache.maven.archiver.MavenArchiveConfiguration import org.apache.maven.archiver.MavenArchiver import org.apache.maven.execution.MavenSession +import org.apache.maven.model.Dependency import org.apache.maven.plugin.AbstractMojo import org.apache.maven.plugin.MojoExecutionException import org.apache.maven.plugins.annotations.* import org.apache.maven.project.MavenProject import org.apache.maven.project.MavenProjectHelper +import org.apache.maven.repository.internal.MavenRepositorySystemUtils import org.codehaus.plexus.archiver.Archiver import org.codehaus.plexus.archiver.jar.JarArchiver +import org.eclipse.aether.DefaultRepositorySystemSession +import org.eclipse.aether.RepositorySystem +import org.eclipse.aether.RepositorySystemSession +import org.eclipse.aether.artifact.DefaultArtifact +import org.eclipse.aether.collection.CollectRequest +import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory +import org.eclipse.aether.graph.DependencyNode +import org.eclipse.aether.impl.DefaultServiceLocator +import org.eclipse.aether.repository.LocalRepository +import org.eclipse.aether.repository.RemoteRepository +import org.eclipse.aether.resolution.DependencyRequest +import org.eclipse.aether.spi.connector.RepositoryConnectorFactory +import org.eclipse.aether.spi.connector.transport.TransporterFactory +import org.eclipse.aether.transport.file.FileTransporterFactory +import org.eclipse.aether.transport.http.HttpTransporterFactory +import org.eclipse.aether.util.graph.visitor.PreorderNodeListGenerator import org.jetbrains.dokka.* -import org.jetbrains.dokka.Utilities.defaultLinks import java.io.File import java.net.URL @@ -30,36 +47,47 @@ class ExternalDocumentationLinkBuilder : DokkaConfiguration.ExternalDocumentatio @Parameter(name = "url", required = true) override var url: URL? = null + @Parameter(name = "packageListUrl", required = true) override var packageListUrl: URL? = null } -abstract class AbstractDokkaMojo : AbstractMojo() { +abstract class AbstractDokkaMojo(private val defaultDokkaPlugins: List<Dependency>) : AbstractMojo() { class SourceRoot : DokkaConfiguration.SourceRoot { @Parameter(required = true) override var path: String = "" } + @Parameter(defaultValue = "\${project}", readonly = true) + private var mavenProject: MavenProject? = null + + @Parameter() + private var session: RepositorySystemSession? = null + class PackageOptions : DokkaConfiguration.PackageOptions { @Parameter override var prefix: String = "" + @Parameter - override var includeNonPublic: Boolean = false + override var includeNonPublic: Boolean = DokkaDefaults.includeNonPublic + @Parameter - override var reportUndocumented: Boolean = true + override var reportUndocumented: Boolean = DokkaDefaults.reportUndocumented + @Parameter - override var skipDeprecated: Boolean = false + override var skipDeprecated: Boolean = DokkaDefaults.skipDeprecated + @Parameter - override var suppress: Boolean = false + override var suppress: Boolean = DokkaDefaults.suppress } + @Parameter + var sourceSetName: String = "JVM" + @Parameter(required = true, defaultValue = "\${project.compileSourceRoots}") var sourceDirectories: List<String> = emptyList() @Parameter - var sourceRoots: List<SourceRoot> = emptyList() - - @Parameter var samples: List<String> = emptyList() @Parameter @@ -74,18 +102,23 @@ abstract class AbstractDokkaMojo : AbstractMojo() { @Parameter(required = true, defaultValue = "\${project.artifactId}") var moduleName: String = "" + @Parameter + var moduleDisplayName: String = "" + @Parameter(required = false, defaultValue = "false") var skip: Boolean = false - @Parameter(required = false, defaultValue = "6") - var jdkVersion: Int = 6 + @Parameter(required = false, defaultValue = "${DokkaDefaults.jdkVersion}") + var jdkVersion: Int = DokkaDefaults.jdkVersion @Parameter - var skipDeprecated: Boolean = false + var skipDeprecated: Boolean = DokkaDefaults.skipDeprecated + @Parameter - var skipEmptyPackages: Boolean = true + var skipEmptyPackages: Boolean = DokkaDefaults.skipEmptyPackages + @Parameter - var reportUndocumented: Boolean = true + var reportUndocumented: Boolean = DokkaDefaults.reportUndocumented @Parameter var impliedPlatforms: List<String> = emptyList() @@ -96,15 +129,21 @@ abstract class AbstractDokkaMojo : AbstractMojo() { @Parameter var externalDocumentationLinks: List<ExternalDocumentationLinkBuilder> = emptyList() - @Parameter(defaultValue = "false") - var noStdlibLink: Boolean = false + @Parameter(defaultValue = "${DokkaDefaults.noStdlibLink}") + var noStdlibLink: Boolean = DokkaDefaults.noStdlibLink - @Parameter(defaultValue = "false") - var noJdkLink: Boolean = false + @Parameter(defaultValue = "${DokkaDefaults.noJdkLink}") + var noJdkLink: Boolean = DokkaDefaults.noJdkLink @Parameter var cacheRoot: String? = null + @Parameter(defaultValue = "JVM") + var displayName: String = "JVM" + + @Parameter(defaultValue = "${DokkaDefaults.offlineMode}") + var offlineMode: Boolean = DokkaDefaults.offlineMode + @Parameter var languageVersion: String? = null @@ -112,33 +151,26 @@ abstract class AbstractDokkaMojo : AbstractMojo() { var apiVersion: String? = null @Parameter - var includeRootPackage: Boolean = false - - @Parameter - var suppressedFiles: List<String> = emptyList() + var includeRootPackage: Boolean = DokkaDefaults.includeRootPackage @Parameter - var collectInheritedExtensionsFromLibraries: Boolean = false + var suppressedFiles: List<String> = emptyList() @Parameter var platform: String = "" @Parameter - var targets: List<String> = emptyList() + var includeNonPublic: Boolean = DokkaDefaults.includeNonPublic @Parameter - var sinceKotlin: String? = null + var failOnWarning: Boolean = DokkaDefaults.failOnWarning @Parameter - var includeNonPublic: Boolean = false - - @Parameter - var generateIndexPages: Boolean = false + var dokkaPlugins: List<Dependency> = emptyList() + get() = field + defaultDokkaPlugins protected abstract fun getOutDir(): String - protected abstract fun getOutFormat(): String - override fun execute() { if (skip) { log.info("Dokka skip parameter is true so no dokka output will be produced") @@ -150,18 +182,36 @@ abstract class AbstractDokkaMojo : AbstractMojo() { throw MojoExecutionException("Incorrect path property, only Unix based path allowed.") } } + fun defaultLinks(config: DokkaSourceSetImpl): List<ExternalDocumentationLinkImpl> { + val links = mutableListOf<ExternalDocumentationLinkImpl>() + if (!config.noJdkLink) + links += DokkaConfiguration.ExternalDocumentationLink + .Builder("https://docs.oracle.com/javase/${config.jdkVersion}/docs/api/") + .build() as ExternalDocumentationLinkImpl + + if (!config.noStdlibLink) + links += DokkaConfiguration.ExternalDocumentationLink + .Builder("https://kotlinlang.org/api/latest/jvm/stdlib/") + .build() as ExternalDocumentationLinkImpl + return links + } - val passConfiguration = PassConfigurationImpl( + val sourceSet = DokkaSourceSetImpl( + moduleDisplayName = moduleDisplayName.takeIf(String::isNotBlank) ?: moduleName, + displayName = displayName, + sourceSetID = DokkaSourceSetID(moduleName, sourceSetName), classpath = classpath, - sourceRoots = sourceDirectories.map { SourceRootImpl(it) } + sourceRoots.map { SourceRootImpl(path = it.path) }, + sourceRoots = sourceDirectories.map { SourceRootImpl(it) }, + dependentSourceSets = emptySet(), samples = samples, includes = includes, - collectInheritedExtensionsFromLibraries = collectInheritedExtensionsFromLibraries, // TODO: Should we implement this? - sourceLinks = sourceLinks.map { SourceLinkDefinitionImpl(it.path, it.url, it.lineSuffix) }, - jdkVersion = jdkVersion, - skipDeprecated = skipDeprecated, - skipEmptyPackages = skipEmptyPackages, + includeNonPublic = includeNonPublic, + includeRootPackage = includeRootPackage, reportUndocumented = reportUndocumented, + skipEmptyPackages = skipEmptyPackages, + skipDeprecated = skipDeprecated, + jdkVersion = jdkVersion, + sourceLinks = sourceLinks.map { SourceLinkDefinitionImpl(it.path, it.url, it.lineSuffix) }, perPackageOptions = perPackageOptions.map { PackageOptionsImpl( prefix = it.prefix, @@ -169,61 +219,132 @@ abstract class AbstractDokkaMojo : AbstractMojo() { reportUndocumented = it.reportUndocumented, skipDeprecated = it.skipDeprecated, suppress = it.suppress - )}, + ) + }, externalDocumentationLinks = externalDocumentationLinks.map { it.build() as ExternalDocumentationLinkImpl }, - noStdlibLink = noStdlibLink, - noJdkLink = noJdkLink, languageVersion = languageVersion, apiVersion = apiVersion, - moduleName = moduleName, + noStdlibLink = noStdlibLink, + noJdkLink = noJdkLink, suppressedFiles = suppressedFiles, - sinceKotlin = sinceKotlin, - analysisPlatform = if (platform.isNotEmpty()) Platform.fromString(platform) else Platform.DEFAULT, - targets = targets, - includeNonPublic = includeNonPublic, - includeRootPackage = includeRootPackage - ) + analysisPlatform = if (platform.isNotEmpty()) Platform.fromString(platform) else Platform.DEFAULT + ).let { + it.copy( + externalDocumentationLinks = defaultLinks(it) + it.externalDocumentationLinks + ) + } - passConfiguration.externalDocumentationLinks += passConfiguration.defaultLinks() + val logger = MavenDokkaLogger(log) val configuration = DokkaConfigurationImpl( outputDir = getOutDir(), - format = getOutFormat(), - impliedPlatforms = impliedPlatforms, + offlineMode = offlineMode, cacheRoot = cacheRoot, - passesConfigurations = listOf(passConfiguration), - generateIndexPages = generateIndexPages + sourceSets = listOf(sourceSet).also { + if (sourceSet.moduleDisplayName.isEmpty()) logger.warn("Not specified module name. It can result in unexpected behaviour while including documentation for module") + }, + pluginsClasspath = getArtifactByAether("org.jetbrains.dokka", "dokka-base", dokkaVersion) + + dokkaPlugins.map { getArtifactByAether(it.groupId, it.artifactId, it.version ?: dokkaVersion) }.flatten(), + pluginsConfiguration = mutableMapOf(), //TODO implement as it is in Gradle + modules = emptyList(), + failOnWarning = failOnWarning ) - val gen = DokkaGenerator(configuration, MavenDokkaLogger(log)) + val gen = DokkaGenerator(configuration, logger) gen.generate() } -} -@Mojo(name = "dokka", defaultPhase = LifecyclePhase.PRE_SITE, threadSafe = true, requiresDependencyResolution = ResolutionScope.COMPILE, requiresProject = true) -class DokkaMojo : AbstractDokkaMojo() { - @Parameter(required = true, defaultValue = "html") - var outputFormat: String = "html" + private fun newRepositorySystem(): RepositorySystem { + val locator: DefaultServiceLocator = MavenRepositorySystemUtils.newServiceLocator() + locator.addService(RepositoryConnectorFactory::class.java, BasicRepositoryConnectorFactory::class.java) + locator.addService(TransporterFactory::class.java, FileTransporterFactory::class.java) + locator.addService(TransporterFactory::class.java, HttpTransporterFactory::class.java) + return locator.getService(RepositorySystem::class.java) + } + + private fun newSession(system: RepositorySystem): RepositorySystemSession { + val session: DefaultRepositorySystemSession = + MavenRepositorySystemUtils.newSession() + val localRepo = LocalRepository(System.getProperty("user.home") + "/.m2/repository") + session.localRepositoryManager = system.newLocalRepositoryManager(session, localRepo) + return session + } + + private fun getArtifactByAether( + groupId: String, + artifactId: String, + version: String + ): List<File> { + val repoSystem: RepositorySystem = newRepositorySystem() + val session: RepositorySystemSession = newSession(repoSystem) + val dependency = + org.eclipse.aether.graph.Dependency(DefaultArtifact("$groupId:$artifactId:$version"), "compile") + val collectRequest = CollectRequest() + collectRequest.root = dependency + val repositories: List<RemoteRepository> = + (mavenProject?.remoteProjectRepositories?.plus(mavenProject?.remotePluginRepositories ?: emptyList()) + ?: mavenProject?.remotePluginRepositories ?: emptyList()) + repositories.forEach { + collectRequest.addRepository( + RemoteRepository.Builder( + "repo", + "default", + it.url + ).build() + ) + } + val node: DependencyNode = repoSystem.collectDependencies(session, collectRequest).root + val dependencyRequest = DependencyRequest() + dependencyRequest.root = node + repoSystem.resolveDependencies(session, dependencyRequest) + val nlg = PreorderNodeListGenerator() + node.accept(nlg) + return nlg.files + } + + private val dokkaVersion: String by lazy { + mavenProject?.pluginArtifacts?.filter { it.groupId == "org.jetbrains.dokka" && it.artifactId == "dokka-maven-plugin" } + ?.firstOrNull()?.version ?: throw IllegalStateException("Not found dokka plugin") + } +} +@Mojo( + name = "dokka", + defaultPhase = LifecyclePhase.PRE_SITE, + threadSafe = true, + requiresDependencyResolution = ResolutionScope.COMPILE, + requiresProject = true +) +class DokkaMojo : AbstractDokkaMojo(emptyList()) { @Parameter(required = true, defaultValue = "\${project.basedir}/target/dokka") var outputDir: String = "" - override fun getOutFormat() = outputFormat override fun getOutDir() = outputDir } -@Mojo(name = "javadoc", defaultPhase = LifecyclePhase.PRE_SITE, threadSafe = true, requiresDependencyResolution = ResolutionScope.COMPILE, requiresProject = true) -class DokkaJavadocMojo : AbstractDokkaMojo() { +@Mojo( + name = "javadoc", + defaultPhase = LifecyclePhase.PRE_SITE, + threadSafe = true, + requiresDependencyResolution = ResolutionScope.COMPILE, + requiresProject = true +) +class DokkaJavadocMojo : AbstractDokkaMojo(listOf(javadocDependency)) { @Parameter(required = true, defaultValue = "\${project.basedir}/target/dokkaJavadoc") var outputDir: String = "" - override fun getOutFormat() = "javadoc" override fun getOutDir() = outputDir } -@Mojo(name = "javadocJar", defaultPhase = LifecyclePhase.PRE_SITE, threadSafe = true, requiresDependencyResolution = ResolutionScope.COMPILE, requiresProject = true) -class DokkaJavadocJarMojo : AbstractDokkaMojo() { +@Mojo( + name = "javadocJar", + defaultPhase = LifecyclePhase.PRE_SITE, + threadSafe = true, + requiresDependencyResolution = ResolutionScope.COMPILE, + requiresProject = true +) +class DokkaJavadocJarMojo : AbstractDokkaMojo(listOf(javadocDependency)) { @Parameter(required = true, defaultValue = "\${project.basedir}/target/dokkaJavadocJar") var outputDir: String = "" @@ -268,12 +389,11 @@ class DokkaJavadocJarMojo : AbstractDokkaMojo() { @Component(role = Archiver::class, hint = "jar") private var jarArchiver: JarArchiver? = null - override fun getOutFormat() = "javadoc" override fun getOutDir() = outputDir override fun execute() { super.execute() - if(!File(outputDir).exists()) { + if (!File(outputDir).exists()) { log.warn("No javadoc generated so no javadoc jar will be generated") return } @@ -298,3 +418,7 @@ class DokkaJavadocJarMojo : AbstractDokkaMojo() { } } +private val javadocDependency = Dependency().apply { + groupId = "org.jetbrains.dokka" + artifactId = "javadoc-plugin" +} diff --git a/runners/maven-plugin/src/main/kotlin/MavenDokkaLogger.kt b/runners/maven-plugin/src/main/kotlin/MavenDokkaLogger.kt index a535c807..4b5f4fa9 100644 --- a/runners/maven-plugin/src/main/kotlin/MavenDokkaLogger.kt +++ b/runners/maven-plugin/src/main/kotlin/MavenDokkaLogger.kt @@ -1,18 +1,15 @@ package org.jetbrains.dokka.maven import org.apache.maven.plugin.logging.Log -import org.jetbrains.dokka.DokkaLogger +import org.jetbrains.dokka.utilities.DokkaLogger class MavenDokkaLogger(val log: Log) : DokkaLogger { - override fun error(message: String) { - log.error(message) - } - - override fun info(message: String) { - log.info(message) - } - - override fun warn(message: String) { - log.warn(message) - } -}
\ No newline at end of file + override var warningsCount: Int = 0 + override var errorsCount: Int = 0 + + override fun debug(message: String) = log.debug(message) + override fun info(message: String) = log.info(message) + override fun progress(message: String) = log.info(message) + override fun warn(message: String) = log.warn(message).also { warningsCount++ } + override fun error(message: String) = log.error(message).also { errorsCount++ } +} diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index 8a89d827..00000000 --- a/settings.gradle +++ /dev/null @@ -1,10 +0,0 @@ -rootProject.name = "dokka" - -include 'core', - 'integration', - 'runners:fatjar', - 'runners:ant', - 'runners:cli', - 'runners:maven-plugin', - 'runners:gradle-plugin', - 'runners:gradle-integration-tests' diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 00000000..a9c50387 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,41 @@ +rootProject.name = "dokka" + +include("core") +include("plugins:base:search-component") +include("testApi") +include("test-tools") +include("runners:gradle-plugin") +include("runners:cli") +include("runners:maven-plugin") +include("kotlin-analysis") +include("kotlin-analysis:dependencies") +include("plugins:base") +include("plugins:base:frontend") +include("plugins:base:test-utils") +include("plugins:mathjax") +include("plugins:gfm") +include("plugins:jekyll") +include("plugins:kotlin-as-java") +include("plugins:javadoc") +include("integration-tests") +include("integration-tests:gradle") +include("integration-tests:cli") +include("integration-tests:maven") + +pluginManagement { + val kotlin_version: String by settings + plugins { + id("org.jetbrains.kotlin.jvm") version kotlin_version + id("com.github.johnrengelman.shadow") version "5.2.0" + id("com.jfrog.bintray") version "1.8.5" + id("com.gradle.plugin-publish") version "0.12.0" + } + + repositories { + maven("https://dl.bintray.com/kotlin/kotlin-eap/") + maven("https://dl.bintray.com/kotlin/kotlin-dev/") + mavenCentral() + jcenter() + gradlePluginPortal() + } +} diff --git a/test-tools/build.gradle.kts b/test-tools/build.gradle.kts new file mode 100644 index 00000000..a61787a8 --- /dev/null +++ b/test-tools/build.gradle.kts @@ -0,0 +1,6 @@ +dependencies { + implementation(project(":testApi")) + implementation(kotlin("stdlib-jdk8")) + implementation(kotlin("reflect")) + implementation("com.willowtreeapps.assertk:assertk-jvm:0.22") +}
\ No newline at end of file diff --git a/test-tools/src/main/kotlin/matchers/content/ContentMatchersDsl.kt b/test-tools/src/main/kotlin/matchers/content/ContentMatchersDsl.kt new file mode 100644 index 00000000..01abab28 --- /dev/null +++ b/test-tools/src/main/kotlin/matchers/content/ContentMatchersDsl.kt @@ -0,0 +1,101 @@ +package matchers.content + +import assertk.assertThat +import assertk.assertions.contains +import assertk.assertions.isEqualTo +import org.jetbrains.dokka.pages.* +import org.jetbrains.dokka.test.tools.matchers.content.* +import kotlin.reflect.KClass + +// entry point: +fun ContentNode.assertNode(block: ContentMatcherBuilder<ContentComposite>.() -> Unit) { + val matcher = ContentMatcherBuilder(ContentComposite::class).apply(block).build() + try { + matcher.tryMatch(this) + } catch (e: MatcherError) { + throw AssertionError(e.message + "\n" + matcher.toDebugString(e.anchor, e.anchorAfter)) + } +} + + +// DSL: +@DslMarker +annotation class ContentMatchersDsl + +@ContentMatchersDsl +class ContentMatcherBuilder<T : ContentComposite> @PublishedApi internal constructor(private val kclass: KClass<T>) { + @PublishedApi + internal val children = mutableListOf<MatcherElement>() + internal val assertions = mutableListOf<T.() -> Unit>() + + fun build() = CompositeMatcher(kclass, children) { assertions.forEach { it() } } + + // part of DSL that cannot be defined as an extension + operator fun String.unaryPlus() { + children += TextMatcher(this) + } +} + +fun <T : ContentComposite> ContentMatcherBuilder<T>.check(assertion: T.() -> Unit) { + assertions += assertion +} + +inline fun <reified S : ContentComposite> ContentMatcherBuilder<*>.composite( + block: ContentMatcherBuilder<S>.() -> Unit +) { + children += ContentMatcherBuilder(S::class).apply(block).build() +} + +inline fun <reified S : ContentNode> ContentMatcherBuilder<*>.node(noinline assertions: S.() -> Unit = {}) { + children += NodeMatcher(S::class, assertions) +} + +fun ContentMatcherBuilder<*>.skipAllNotMatching() { + children += Anything +} + + +// Convenience functions: +fun ContentMatcherBuilder<*>.group(block: ContentMatcherBuilder<ContentGroup>.() -> Unit) = composite(block) + +fun ContentMatcherBuilder<*>.header(expectedLevel: Int? = null, block: ContentMatcherBuilder<ContentHeader>.() -> Unit) = + composite<ContentHeader> { + block() + check { if (expectedLevel != null) assertThat(this::level).isEqualTo(expectedLevel) } + } + +fun ContentMatcherBuilder<*>.p(block: ContentMatcherBuilder<ContentGroup>.() -> Unit) = + composite<ContentGroup> { + block() + check { assertThat(this::style).contains(TextStyle.Paragraph) } + } + +fun ContentMatcherBuilder<*>.link(block: ContentMatcherBuilder<ContentLink>.() -> Unit) = composite(block) + +fun ContentMatcherBuilder<*>.table(block: ContentMatcherBuilder<ContentTable>.() -> Unit) = composite(block) + +fun ContentMatcherBuilder<*>.platformHinted(block: ContentMatcherBuilder<ContentGroup>.() -> Unit) = + composite<PlatformHintedContent> { group(block) } + +fun ContentMatcherBuilder<*>.br() = node<ContentBreakLine>() + +fun ContentMatcherBuilder<*>.somewhere(block: ContentMatcherBuilder<*>.() -> Unit) { + skipAllNotMatching() + block() + skipAllNotMatching() +} + +fun ContentMatcherBuilder<*>.divergentGroup(block: ContentMatcherBuilder<ContentDivergentGroup>.() -> Unit) = + composite(block) + +fun ContentMatcherBuilder<ContentDivergentGroup>.divergentInstance(block: ContentMatcherBuilder<ContentDivergentInstance>.() -> Unit) = + composite(block) + +fun ContentMatcherBuilder<ContentDivergentInstance>.before(block: ContentMatcherBuilder<ContentComposite>.() -> Unit) = + composite(block) + +fun ContentMatcherBuilder<ContentDivergentInstance>.divergent(block: ContentMatcherBuilder<ContentComposite>.() -> Unit) = + composite(block) + +fun ContentMatcherBuilder<ContentDivergentInstance>.after(block: ContentMatcherBuilder<ContentComposite>.() -> Unit) = + composite(block)
\ No newline at end of file diff --git a/test-tools/src/main/kotlin/matchers/content/contentMatchers.kt b/test-tools/src/main/kotlin/matchers/content/contentMatchers.kt new file mode 100644 index 00000000..2284c88d --- /dev/null +++ b/test-tools/src/main/kotlin/matchers/content/contentMatchers.kt @@ -0,0 +1,167 @@ +package org.jetbrains.dokka.test.tools.matchers.content + +import org.jetbrains.dokka.pages.ContentComposite +import org.jetbrains.dokka.pages.ContentNode +import org.jetbrains.dokka.pages.ContentText +import kotlin.reflect.KClass +import kotlin.reflect.full.cast +import kotlin.reflect.full.safeCast + +sealed class MatcherElement + +class TextMatcher(val text: String) : MatcherElement() + +open class NodeMatcher<T : ContentNode>( + val kclass: KClass<T>, + val assertions: T.() -> Unit = {} +) : MatcherElement() { + open fun tryMatch(node: ContentNode) { + kclass.safeCast(node)?.apply { + try { + assertions() + } catch (e: AssertionError) { + throw MatcherError(e.message.orEmpty(), this@NodeMatcher, cause = e) + } + } ?: throw MatcherError("Expected ${kclass.simpleName} but got: $node", this) + } +} + +class CompositeMatcher<T : ContentComposite>( + kclass: KClass<T>, + private val children: List<MatcherElement>, + assertions: T.() -> Unit = {} +) : NodeMatcher<T>(kclass, assertions) { + internal val normalizedChildren: List<MatcherElement> by lazy { + children.fold(listOf<MatcherElement>()) { acc, e -> + when { + acc.lastOrNull() is Anything && e is Anything -> acc + acc.lastOrNull() is TextMatcher && e is TextMatcher -> + acc.dropLast(1) + TextMatcher((acc.lastOrNull() as TextMatcher).text + e.text) + else -> acc + e + } + } + } + + override fun tryMatch(node: ContentNode) { + super.tryMatch(node) + kclass.cast(node).children.asSequence() + .filter { it !is ContentText || it.text.isNotBlank() } + .fold(FurtherSiblings(normalizedChildren, this).pop()) { acc, n -> acc.next(n) }.finish() + } +} + +object Anything : MatcherElement() + +private sealed class MatchWalkerState { + abstract fun next(node: ContentNode): MatchWalkerState + abstract fun finish() +} + +private class TextMatcherState( + val text: String, + val rest: FurtherSiblings, + val anchor: TextMatcher +) : MatchWalkerState() { + override fun next(node: ContentNode): MatchWalkerState { + node as? ContentText ?: throw MatcherError("Expected text: \"$text\" but got $node", anchor) + val trimmed = node.text.trim() + return when { + text == trimmed -> rest.pop() + text.startsWith(trimmed) -> TextMatcherState(text.removePrefix(node.text).trim(), rest, anchor) + else -> throw MatcherError("Expected text: \"$text\", but got: \"${node.text}\"", anchor) + } + } + + override fun finish() = throw MatcherError("\"$text\" was not found" + rest.messageEnd, anchor) +} + +private class EmptyMatcherState(val parent: CompositeMatcher<*>) : MatchWalkerState() { + override fun next(node: ContentNode): MatchWalkerState { + throw MatcherError("Unexpected $node", parent, anchorAfter = true) + } + + override fun finish() = Unit +} + +private class NodeMatcherState( + val matcher: NodeMatcher<*>, + val rest: FurtherSiblings +) : MatchWalkerState() { + override fun next(node: ContentNode): MatchWalkerState { + matcher.tryMatch(node) + return rest.pop() + } + + override fun finish() = + throw MatcherError("Content of type ${matcher.kclass} was not found" + rest.messageEnd, matcher) +} + +private class SkippingMatcherState( + val innerState: MatchWalkerState +) : MatchWalkerState() { + override fun next(node: ContentNode): MatchWalkerState = runCatching { innerState.next(node) }.getOrElse { this } + + override fun finish() = innerState.finish() +} + +private class FurtherSiblings(val list: List<MatcherElement>, val parent: CompositeMatcher<*>) { + fun pop(): MatchWalkerState = when (val head = list.firstOrNull()) { + is TextMatcher -> TextMatcherState(head.text.trim(), drop(), head) + is NodeMatcher<*> -> NodeMatcherState(head, drop()) + is Anything -> SkippingMatcherState(drop().pop()) + null -> EmptyMatcherState(parent) + } + + fun drop() = FurtherSiblings(list.drop(1), parent) + + val messageEnd: String + get() = list.filter { it !is Anything } + .count().takeIf { it > 0 } + ?.let { " and $it further matchers were not satisfied" } ?: "" +} + + + +internal fun MatcherElement.toDebugString(anchor: MatcherElement?, anchorAfter: Boolean): String { + fun Appendable.append(element: MatcherElement, ownPrefix: String, childPrefix: String) { + if (anchor != null) { + if (element != anchor || anchorAfter) append(" ".repeat(4)) + else append("--> ") + } + + append(ownPrefix) + when (element) { + is Anything -> append("skipAllNotMatching\n") + is TextMatcher -> append("\"${element.text}\"\n") + is CompositeMatcher<*> -> { + append("${element.kclass.simpleName.toString()}\n") + if (element.normalizedChildren.isNotEmpty()) { + val newOwnPrefix = childPrefix + '\u251c' + '\u2500' + ' ' + val lastOwnPrefix = childPrefix + '\u2514' + '\u2500' + ' ' + val newChildPrefix = childPrefix + '\u2502' + ' ' + ' ' + val lastChildPrefix = childPrefix + ' ' + ' ' + ' ' + element.normalizedChildren.forEachIndexed { n, e -> + if (n != element.normalizedChildren.lastIndex) append(e, newOwnPrefix, newChildPrefix) + else append(e, lastOwnPrefix, lastChildPrefix) + } + } + if (element == anchor && anchorAfter) { + append("--> $childPrefix\n") + } + } + is NodeMatcher<*> -> append("${element.kclass.simpleName}\n") + } + } + + return buildString { append(this@toDebugString, "", "") } +} + +data class MatcherError( + override val message: String, + val anchor: MatcherElement, + val anchorAfter: Boolean = false, + override val cause: Throwable? = null +) : AssertionError(message, cause) + +// Creating this whole mechanism was most scala-like experience I had since I stopped using scala. +// I don't know how I should feel about it.
\ No newline at end of file diff --git a/testApi/build.gradle.kts b/testApi/build.gradle.kts new file mode 100644 index 00000000..fc882f44 --- /dev/null +++ b/testApi/build.gradle.kts @@ -0,0 +1,18 @@ +import org.jetbrains.registerDokkaArtifactPublication + +plugins { + `maven-publish` + id("com.jfrog.bintray") +} + +dependencies { + api(project(":core")) + implementation(project(":kotlin-analysis")) + implementation("junit:junit:4.13") // TODO: remove dependency to junit + implementation(kotlin("stdlib")) + implementation(kotlin("reflect")) +} + +registerDokkaArtifactPublication("dokkaTestApi") { + artifactId = "dokka-test-api" +} diff --git a/testApi/src/main/kotlin/testApi/context/MockContext.kt b/testApi/src/main/kotlin/testApi/context/MockContext.kt new file mode 100644 index 00000000..97347695 --- /dev/null +++ b/testApi/src/main/kotlin/testApi/context/MockContext.kt @@ -0,0 +1,47 @@ +package org.jetbrains.dokka.testApi.context + +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.DokkaPlugin +import org.jetbrains.dokka.plugability.ExtensionPoint +import org.jetbrains.dokka.utilities.DokkaConsoleLogger +import kotlin.reflect.KClass +import kotlin.reflect.KMutableProperty +import kotlin.reflect.full.memberProperties + +@Suppress("UNCHECKED_CAST") // It is only usable from tests so we do not care about safety +class MockContext( + vararg extensions: Pair<ExtensionPoint<*>, (DokkaContext) -> Any>, + private val testConfiguration: DokkaConfiguration? = null, + private val unusedExtensionPoints: List<ExtensionPoint<*>>? = null +) : DokkaContext { + private val extensionMap by lazy { + extensions.groupBy(Pair<ExtensionPoint<*>, (DokkaContext) -> Any>::first) { + it.second(this) + } + } + + private val plugins = mutableMapOf<KClass<out DokkaPlugin>, DokkaPlugin>() + + override fun <T : DokkaPlugin> plugin(kclass: KClass<T>): T? = plugins.getOrPut(kclass) { + kclass.constructors.single { it.parameters.isEmpty() }.call().also { it.injectContext(this) } + } as T + + override fun <T : Any, E : ExtensionPoint<T>> get(point: E): List<T> = extensionMap[point].orEmpty() as List<T> + + override fun <T : Any, E : ExtensionPoint<T>> single(point: E): T = get(point).single() + + override val logger = DokkaConsoleLogger + + override val configuration: DokkaConfiguration + get() = testConfiguration ?: throw IllegalStateException("This mock context doesn't provide configuration") + + override val unusedPoints: Collection<ExtensionPoint<*>> + get() = unusedExtensionPoints + ?: throw IllegalStateException("This mock context doesn't provide unused extension points") +} + +private fun DokkaPlugin.injectContext(context: DokkaContext) { + (DokkaPlugin::class.memberProperties.single { it.name == "context" } as KMutableProperty<*>) + .setter.call(this, context) +} diff --git a/testApi/src/main/kotlin/testApi/logger/TestLogger.kt b/testApi/src/main/kotlin/testApi/logger/TestLogger.kt new file mode 100644 index 00000000..1dbe4a48 --- /dev/null +++ b/testApi/src/main/kotlin/testApi/logger/TestLogger.kt @@ -0,0 +1,48 @@ +package testApi.logger + +import org.jetbrains.dokka.utilities.DokkaLogger + +class TestLogger(private val logger: DokkaLogger) : DokkaLogger { + override var warningsCount: Int by logger::warningsCount + override var errorsCount: Int by logger::errorsCount + + private var _debugMessages = mutableListOf<String>() + val debugMessages: List<String> get() = _debugMessages.toList() + + private var _infoMessages = mutableListOf<String>() + val infoMessages: List<String> get() = _infoMessages.toList() + + private var _progressMessages = mutableListOf<String>() + val progressMessages: List<String> get() = _progressMessages.toList() + + private var _warnMessages = mutableListOf<String>() + val warnMessages: List<String> get() = _warnMessages.toList() + + private var _errorMessages = mutableListOf<String>() + val errorMessages: List<String> get() = _errorMessages.toList() + + override fun debug(message: String) { + _debugMessages.add(message) + logger.debug(message) + } + + override fun info(message: String) { + _infoMessages.add(message) + logger.info(message) + } + + override fun progress(message: String) { + _progressMessages.add(message) + logger.progress(message) + } + + override fun warn(message: String) { + _warnMessages.add(message) + logger.warn(message) + } + + override fun error(message: String) { + _errorMessages.add(message) + logger.error(message) + } +} diff --git a/testApi/src/main/kotlin/testApi/testRunner/DokkaTestGenerator.kt b/testApi/src/main/kotlin/testApi/testRunner/DokkaTestGenerator.kt new file mode 100644 index 00000000..414919dc --- /dev/null +++ b/testApi/src/main/kotlin/testApi/testRunner/DokkaTestGenerator.kt @@ -0,0 +1,45 @@ +package org.jetbrains.dokka.testApi.testRunner + +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.DokkaGenerator +import org.jetbrains.dokka.plugability.DokkaPlugin +import org.jetbrains.dokka.utilities.DokkaLogger + +internal class DokkaTestGenerator( + private val configuration: DokkaConfiguration, + private val logger: DokkaLogger, + private val testMethods: TestMethods, + private val additionalPlugins: List<DokkaPlugin> = emptyList() +) { + + fun generate() = with(testMethods) { + val dokkaGenerator = DokkaGenerator(configuration, logger) + + val context = + dokkaGenerator.initializePlugins(configuration, logger, additionalPlugins) + pluginsSetupStage(context) + + val modulesFromPlatforms = dokkaGenerator.createDocumentationModels(context) + documentablesCreationStage(modulesFromPlatforms) + + val filteredModules = dokkaGenerator.transformDocumentationModelBeforeMerge(modulesFromPlatforms, context) + documentablesFirstTransformationStep(filteredModules) + + val documentationModel = dokkaGenerator.mergeDocumentationModels(filteredModules, context) + documentablesMergingStage(documentationModel) + + val transformedDocumentation = dokkaGenerator.transformDocumentationModelAfterMerge(documentationModel, context) + documentablesTransformationStage(transformedDocumentation) + + val pages = dokkaGenerator.createPages(transformedDocumentation, context) + pagesGenerationStage(pages) + + val transformedPages = dokkaGenerator.transformPages(pages, context) + pagesTransformationStage(transformedPages) + + dokkaGenerator.render(transformedPages, context) + renderingStage(transformedPages, context) + + dokkaGenerator.reportAfterRendering(context) + } +} diff --git a/testApi/src/main/kotlin/testApi/testRunner/TestRunner.kt b/testApi/src/main/kotlin/testApi/testRunner/TestRunner.kt new file mode 100644 index 00000000..9aae4b0c --- /dev/null +++ b/testApi/src/main/kotlin/testApi/testRunner/TestRunner.kt @@ -0,0 +1,271 @@ +package org.jetbrains.dokka.testApi.testRunner + +import com.intellij.openapi.application.PathManager +import org.jetbrains.dokka.* +import org.jetbrains.dokka.model.DModule +import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet +import org.jetbrains.dokka.pages.RootPageNode +import org.jetbrains.dokka.plugability.DokkaContext +import org.jetbrains.dokka.plugability.DokkaPlugin +import org.jetbrains.dokka.utilities.DokkaConsoleLogger +import org.junit.rules.TemporaryFolder +import testApi.logger.TestLogger +import java.io.File +import java.nio.charset.Charset +import java.nio.file.Files +import java.nio.file.InvalidPathException +import java.nio.file.Path +import java.nio.file.Paths + +// TODO: take dokka configuration from file +abstract class AbstractCoreTest { + protected var logger = TestLogger(DokkaConsoleLogger) + + protected fun getTestDataDir(name: String) = + File("src/test/resources/$name").takeIf { it.exists() }?.toPath() + ?: throw InvalidPathException(name, "Cannot be found") + + protected fun testFromData( + configuration: DokkaConfigurationImpl, + cleanupOutput: Boolean = true, + pluginOverrides: List<DokkaPlugin> = emptyList(), + block: TestBuilder.() -> Unit + ) { + val testMethods = TestBuilder().apply(block).build() + val tempDir = getTempDir(cleanupOutput) + if (!cleanupOutput) + logger.info("Output generated under: ${tempDir.root.absolutePath}") + val newConfiguration = + configuration.copy( + outputDir = tempDir.root.toPath().toAbsolutePath().toString() + ) + DokkaTestGenerator( + newConfiguration, + logger, + testMethods, + pluginOverrides + ).generate() + } + + protected fun testInline( + query: String, + configuration: DokkaConfigurationImpl, + cleanupOutput: Boolean = true, + pluginOverrides: List<DokkaPlugin> = emptyList(), + block: TestBuilder.() -> Unit + ) { + val testMethods = TestBuilder().apply(block).build() + val testDirPath = getTempDir(cleanupOutput).root.toPath() + val fileMap = query.toFileMap() + fileMap.materializeFiles(testDirPath.toAbsolutePath()) + if (!cleanupOutput) + logger.info("Output generated under: ${testDirPath.toAbsolutePath()}") + val newConfiguration = + configuration.copy( + outputDir = testDirPath.toAbsolutePath().toString(), + sourceSets = configuration.sourceSets.map { + it.copy(sourceRoots = it.sourceRoots.map { it.copy(path = "${testDirPath.toAbsolutePath()}/${it.path}") }) + } + ) + DokkaTestGenerator( + newConfiguration, + logger, + testMethods, + pluginOverrides + ).generate() + } + + + private fun String.toFileMap(): Map<String, String> { + return this.trimIndent().trimMargin() + .replace("\r\n", "\n") + .sliceAt(filePathRegex) + .filter { it.isNotEmpty() && it.isNotBlank() && "\n" in it } + .map { fileDeclaration -> fileDeclaration.trim() } + .map { fileDeclaration -> + val filePathAndContent = fileDeclaration.split("\n", limit = 2) + val filePath = filePathAndContent.first().removePrefix("/").trim() + val content = filePathAndContent.last().trim() + filePath to content + } + .toMap() + } + + private fun String.sliceAt(regex: Regex): List<String> { + val matchesStartIndices = regex.findAll(this).toList().map { match -> match.range.first } + return sequence { + yield(0) + yieldAll(matchesStartIndices) + yield(this@sliceAt.length) + } + .zipWithNext { startIndex: Int, endIndex: Int -> substring(startIndex, endIndex) } + .toList() + .also { slices -> + /* Post-condition verifying that no character is lost */ + check(slices.sumBy { it.length } == length) + } + } + + private fun Map<String, String>.materializeFiles( + root: Path = Paths.get("."), + charset: Charset = Charset.forName("utf-8") + ) = this.map { (path, content) -> + val file = root.resolve(path) + Files.createDirectories(file.parent) + Files.write(file, content.toByteArray(charset)) + } + + private fun getTempDir(cleanupOutput: Boolean) = if (cleanupOutput) { + TemporaryFolder().apply { create() } + } else { + object : TemporaryFolder() { + override fun after() {} + }.apply { create() } + } + + protected class TestBuilder { + var pluginsSetupStage: (DokkaContext) -> Unit = {} + var documentablesCreationStage: (List<DModule>) -> Unit = {} + var documentablesFirstTransformationStep: (List<DModule>) -> Unit = {} + var documentablesMergingStage: (DModule) -> Unit = {} + var documentablesTransformationStage: (DModule) -> Unit = {} + var pagesGenerationStage: (RootPageNode) -> Unit = {} + var pagesTransformationStage: (RootPageNode) -> Unit = {} + var renderingStage: (RootPageNode, DokkaContext) -> Unit = { a, b -> } + + @PublishedApi + internal fun build() = TestMethods( + pluginsSetupStage, + documentablesCreationStage, + documentablesFirstTransformationStep, + documentablesMergingStage, + documentablesTransformationStage, + pagesGenerationStage, + pagesTransformationStage, + renderingStage + ) + } + + protected fun dokkaConfiguration(block: DokkaConfigurationBuilder.() -> Unit): DokkaConfigurationImpl = + DokkaConfigurationBuilder().apply(block).build() + + @DslMarker + protected annotation class DokkaConfigurationDsl + + @DokkaConfigurationDsl + protected class DokkaConfigurationBuilder { + var outputDir: String = "out" + var format: String = "html" + var offlineMode: Boolean = false + var cacheRoot: String? = null + var pluginsClasspath: List<File> = emptyList() + var pluginsConfigurations: Map<String, String> = emptyMap() + var failOnWarning: Boolean = false + private val sourceSets = mutableListOf<DokkaSourceSetImpl>() + fun build() = DokkaConfigurationImpl( + outputDir = outputDir, + cacheRoot = cacheRoot, + offlineMode = offlineMode, + sourceSets = sourceSets, + pluginsClasspath = pluginsClasspath, + pluginsConfiguration = pluginsConfigurations, + modules = emptyList(), + failOnWarning = failOnWarning + ) + + fun sourceSets(block: SourceSetsBuilder.() -> Unit) { + sourceSets.addAll(SourceSetsBuilder().apply(block)) + } + } + + @DokkaConfigurationDsl + protected class SourceSetsBuilder : ArrayList<DokkaSourceSetImpl>() { + fun sourceSet(block: DokkaSourceSetBuilder.() -> Unit): DokkaSourceSet = + DokkaSourceSetBuilder().apply(block).build().apply(::add) + } + + @DokkaConfigurationDsl + protected class DokkaSourceSetBuilder( + var moduleName: String = "root", + var moduleDisplayName: String? = null, + var name: String = "main", + var displayName: String = "JVM", + var classpath: List<String> = emptyList(), + var sourceRoots: List<String> = emptyList(), + var dependentSourceSets: Set<DokkaSourceSetID> = emptySet(), + var samples: List<String> = emptyList(), + var includes: List<String> = emptyList(), + var includeNonPublic: Boolean = false, + var includeRootPackage: Boolean = true, + var reportUndocumented: Boolean = false, + var skipEmptyPackages: Boolean = false, + var skipDeprecated: Boolean = false, + var jdkVersion: Int = 8, + var languageVersion: String? = null, + var apiVersion: String? = null, + var noStdlibLink: Boolean = false, + var noJdkLink: Boolean = false, + var suppressedFiles: List<String> = emptyList(), + var analysisPlatform: String = "jvm", + var perPackageOptions: List<PackageOptionsImpl> = emptyList(), + var externalDocumentationLinks: List<ExternalDocumentationLinkImpl> = emptyList(), + var sourceLinks: List<SourceLinkDefinitionImpl> = emptyList() + ) { + fun build() = DokkaSourceSetImpl( + moduleDisplayName = moduleDisplayName ?: moduleName, + displayName = displayName, + sourceSetID = DokkaSourceSetID(moduleName, name), + classpath = classpath, + sourceRoots = sourceRoots.map { SourceRootImpl(it) }, + dependentSourceSets = dependentSourceSets, + samples = samples, + includes = includes, + includeNonPublic = includeNonPublic, + includeRootPackage = includeRootPackage, + reportUndocumented = reportUndocumented, + skipEmptyPackages = skipEmptyPackages, + skipDeprecated = skipDeprecated, + jdkVersion = jdkVersion, + sourceLinks = sourceLinks, + perPackageOptions = perPackageOptions, + externalDocumentationLinks = externalDocumentationLinks, + languageVersion = languageVersion, + apiVersion = apiVersion, + noStdlibLink = noStdlibLink, + noJdkLink = noJdkLink, + suppressedFiles = suppressedFiles, + analysisPlatform = Platform.fromString(analysisPlatform) + ) + } + + protected val jvmStdlibPath: String? by lazy { + PathManager.getResourceRoot(Strictfp::class.java, "/kotlin/jvm/Strictfp.class") + } + + protected val jsStdlibPath: String? by lazy { + PathManager.getResourceRoot(Any::class.java, "/kotlin/jquery") + } + + protected val commonStdlibPath: String? by lazy { + // TODO: feels hacky, find a better way to do it + ClassLoader.getSystemResource("kotlin/UInt.kotlin_metadata") + ?.file + ?.replace("file:", "") + ?.replaceAfter(".jar", "") + } + + companion object { + private val filePathRegex = Regex("""[\n^](/\w+)+(\.\w+)?\s*\n""") + } +} + +data class TestMethods( + val pluginsSetupStage: (DokkaContext) -> Unit, + val documentablesCreationStage: (List<DModule>) -> Unit, + val documentablesFirstTransformationStep: (List<DModule>) -> Unit, + val documentablesMergingStage: (DModule) -> Unit, + val documentablesTransformationStage: (DModule) -> Unit, + val pagesGenerationStage: (RootPageNode) -> Unit, + val pagesTransformationStage: (RootPageNode) -> Unit, + val renderingStage: (RootPageNode, DokkaContext) -> Unit +) diff --git a/testApi/src/main/kotlin/testApi/utils/assertIsInstance.kt b/testApi/src/main/kotlin/testApi/utils/assertIsInstance.kt new file mode 100644 index 00000000..279dbafa --- /dev/null +++ b/testApi/src/main/kotlin/testApi/utils/assertIsInstance.kt @@ -0,0 +1,17 @@ +package testApi.utils + +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.contract + +@OptIn(ExperimentalContracts::class) +inline fun <reified T> assertIsInstance(obj: Any?): T { + contract { + returns() implies (obj is T) + } + + if (obj is T) { + return obj + } + + throw AssertionError("Expected instance of type ${T::class.qualifiedName} but found $obj") +} |